diff --git a/airhub_app/.gitignore b/airhub_app/.gitignore index 3820a95..330fde5 100644 --- a/airhub_app/.gitignore +++ b/airhub_app/.gitignore @@ -43,3 +43,31 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# ali_auth plugin - 不需要提交的文件 +packages/ali_auth/example/ +packages/ali_auth/screenshots/ +packages/ali_auth/doc/ +packages/ali_auth/.github/ +packages/ali_auth/.metadata +packages/ali_auth/.pubignore +packages/ali_auth/.gitignore +packages/ali_auth/CHANGELOG.md +packages/ali_auth/CHANGELOG-zh.md +packages/ali_auth/CODE_OF_CONDUCT.md +packages/ali_auth/LICENSE +packages/ali_auth/README.md +packages/ali_auth/README_en.md +packages/ali_auth/release.md +packages/ali_auth/test/ + +# ali_auth 旧备份 +packages/ali_auth_backup_*/ + +# 调试文档和构建脚本 +ONECLICK_LOGIN_DEBUG.md +TEST_ONECLICK_LOGIN.md +UPDATE_ALIYUN_SDK.md +build_and_install_ios.sh +build_android_release.sh +rebuild_ios.sh diff --git a/airhub_app/android/app/build.gradle.kts b/airhub_app/android/app/build.gradle.kts index 5262112..ef6bf8c 100644 --- a/airhub_app/android/app/build.gradle.kts +++ b/airhub_app/android/app/build.gradle.kts @@ -35,10 +35,16 @@ android { // 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") + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } } +dependencies { + // 阿里云一键登录SDK的授权页需要 AppCompatActivity + implementation("androidx.appcompat:appcompat:1.6.1") +} + flutter { source = "../.." } diff --git a/airhub_app/android/app/proguard-rules.pro b/airhub_app/android/app/proguard-rules.pro new file mode 100644 index 0000000..1c3f04e --- /dev/null +++ b/airhub_app/android/app/proguard-rules.pro @@ -0,0 +1,6 @@ +# 阿里云一键登录SDK - 授权页需要 AppCompatActivity +-keep class androidx.appcompat.** { *; } +-keep class com.mobile.auth.** { *; } +-keep class com.cmic.** { *; } +-keep class com.alibaba.sdk.android.** { *; } +-keep class com.alicom.** { *; } diff --git a/airhub_app/assets/www/auth_bg_gradient.png b/airhub_app/assets/www/auth_bg_gradient.png new file mode 100644 index 0000000..ea2ff59 Binary files /dev/null and b/airhub_app/assets/www/auth_bg_gradient.png differ diff --git a/airhub_app/assets/www/login_btn_normal.png b/airhub_app/assets/www/login_btn_normal.png new file mode 100644 index 0000000..ddaedff Binary files /dev/null and b/airhub_app/assets/www/login_btn_normal.png differ diff --git a/airhub_app/assets/www/login_btn_press.png b/airhub_app/assets/www/login_btn_press.png new file mode 100644 index 0000000..0436367 Binary files /dev/null and b/airhub_app/assets/www/login_btn_press.png differ diff --git a/airhub_app/assets/www/login_btn_unable.png b/airhub_app/assets/www/login_btn_unable.png new file mode 100644 index 0000000..e61a0b5 Binary files /dev/null and b/airhub_app/assets/www/login_btn_unable.png differ diff --git a/airhub_app/ios/Podfile.lock b/airhub_app/ios/Podfile.lock index c650a4f..d68fb16 100644 --- a/airhub_app/ios/Podfile.lock +++ b/airhub_app/ios/Podfile.lock @@ -1,4 +1,8 @@ PODS: + - ali_auth (1.3.7): + - Flutter + - MJExtension + - SDWebImage - audio_session (0.0.1): - Flutter - Flutter (1.0.0) @@ -10,8 +14,12 @@ PODS: - just_audio (0.0.1): - Flutter - FlutterMacOS + - MJExtension (3.4.2) - permission_handler_apple (9.3.0): - Flutter + - SDWebImage (5.21.6): + - SDWebImage/Core (= 5.21.6) + - SDWebImage/Core (5.21.6) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -20,6 +28,7 @@ PODS: - FlutterMacOS DEPENDENCIES: + - ali_auth (from `.symlinks/plugins/ali_auth/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - Flutter (from `Flutter`) - flutter_blue_plus_darwin (from `.symlinks/plugins/flutter_blue_plus_darwin/darwin`) @@ -29,7 +38,14 @@ DEPENDENCIES: - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) +SPEC REPOS: + trunk: + - MJExtension + - SDWebImage + EXTERNAL SOURCES: + ali_auth: + :path: ".symlinks/plugins/ali_auth/ios" audio_session: :path: ".symlinks/plugins/audio_session/ios" Flutter: @@ -48,12 +64,15 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: + ali_auth: fe9a6188a90eb39227f3674c05a71383ac4ec6a2 audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3 image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed + MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d + SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d diff --git a/airhub_app/lib/core/network/api_client.dart b/airhub_app/lib/core/network/api_client.dart index a37fafc..3b3b8bb 100644 --- a/airhub_app/lib/core/network/api_client.dart +++ b/airhub_app/lib/core/network/api_client.dart @@ -44,12 +44,27 @@ class ApiClient { } /// POST 请求,返回 data 字段 + /// 如果 path 以 http:// 或 https:// 开头,则作为完整 URL 使用 Future post( String path, { dynamic data, }) async { final response = await _request( - () => _dio.post(path, data: data), + () { + // 如果是完整 URL,使用 Options 覆盖 baseUrl + if (path.startsWith('http://') || path.startsWith('https://')) { + return _dio.post( + path, + data: data, + options: Options( + headers: {'Content-Type': 'application/json'}, + // 使用空字符串作为 baseUrl,让 Dio 使用完整路径 + extra: {'useFullUrl': true}, + ), + ); + } + return _dio.post(path, data: data); + }, ); return response; } diff --git a/airhub_app/lib/core/network/api_config.dart b/airhub_app/lib/core/network/api_config.dart index cc20d06..36c0aa5 100644 --- a/airhub_app/lib/core/network/api_config.dart +++ b/airhub_app/lib/core/network/api_config.dart @@ -1,7 +1,10 @@ class ApiConfig { - /// 后端服务器地址(开发环境请替换为实际 IP) + /// 后端服务器地址(本地开发环境) static const String baseUrl = 'http://192.168.124.8:8000'; + /// 一键授权登录专用域名(HTTPS,用于阿里云号码认证) + static const String authBaseUrl = 'https://qiyuan-rtc-api.airlabs.art'; + /// App 端 API 前缀 static const String apiPrefix = '/api/v1'; @@ -13,4 +16,7 @@ class ApiConfig { /// 拼接完整 URL static String get fullBaseUrl => '$baseUrl$apiPrefix'; + + /// 拼接一键授权登录完整 URL + static String get fullAuthBaseUrl => '$authBaseUrl$apiPrefix'; } diff --git a/airhub_app/lib/core/services/phone_auth_service.dart b/airhub_app/lib/core/services/phone_auth_service.dart index 30f44a8..5f9e136 100644 --- a/airhub_app/lib/core/services/phone_auth_service.dart +++ b/airhub_app/lib/core/services/phone_auth_service.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show debugPrint, kIsWeb; import 'package:riverpod_annotation/riverpod_annotation.dart'; - -// 本地 Web 调试:始终使用 stub(ali_auth 不兼容当前 Dart 版本) -import 'phone_auth_service_stub.dart'; +import 'package:ali_auth/ali_auth.dart'; part 'phone_auth_service.g.dart'; @@ -19,13 +17,132 @@ PhoneAuthService phoneAuthService(Ref ref) { return PhoneAuthService(); } +/// 登录流程的终止性错误码(收到即表示登录失败,需结束等待) +const _terminalErrorCodes = { + '600002', // 唤起授权页失败 + '600004', // 获取运营商配置信息失败 + '600005', // 手机终端不安全 + '600007', // 未检测到SIM卡 + '600008', // 蜂窝网络未开启 + '600009', // 无法判断运营商 + '600010', // 未知异常 + '600011', // 获取token失败 + '600012', // 预取号失败 + '600013', // 运营商维护升级 + '600014', // 已达最大调用次数 + '600015', // 接口超时 + '600017', // AppID/Appkey解析失败 + '600018', // 请先初始化SDK + '600021', // 运营商已切换 + '600023', // 加载自定义控件异常 + '600025', // 检测参数错误 + '600026', // 授权页已加载时不允许调用 + '500000', // 参数获取异常 + '500001', // 密钥不能为空 + '500003', // 未初始化就调用延时登录 +}; + class PhoneAuthService { bool _initialized = false; String? _lastError; + /// 用于等待 initSdk 通过 EventChannel 返回结果 + Completer? _initCompleter; + + /// 用于等待 login 通过 EventChannel 返回 token + Completer? _loginCompleter; + + /// 收到的所有事件码(调试用) + final List _eventLog = []; + /// 最近一次错误信息(用于 UI 展示) String? get lastError => _lastError; + /// 统一的 EventChannel 事件处理 + void _handleEvent(dynamic event) { + debugPrint('[AliAuth] event: $event'); + final code = event['code'] as String?; + if (code == null) return; + _eventLog.add('$code:${event['msg'] ?? ''}'); + + // ========== 终止性事件:登录成功 ========== + if (code == '600000') { + final token = event['data'] as String?; + if (token != null && token.isNotEmpty) { + debugPrint('[AliAuth] 登录成功,获取到 token'); + // 主动关闭授权页面和loading(Android原生端未自动关闭) + AliAuth.quitPage(); + if (_loginCompleter != null && !_loginCompleter!.isCompleted) { + _loginCompleter!.complete(token); + } + } + return; + } + + // ========== 终止性事件:用户取消 ========== + if (code == '700000') { + _lastError = '用户取消登录'; + debugPrint('[AliAuth] $_lastError'); + if (_loginCompleter != null && !_loginCompleter!.isCompleted) { + _loginCompleter!.complete(null); + } + return; + } + + // ========== 终止性事件:用户切换其他登录方式 ========== + if (code == '700001') { + _lastError = '切换其他方式'; + debugPrint('[AliAuth] $_lastError'); + if (_loginCompleter != null && !_loginCompleter!.isCompleted) { + _loginCompleter!.complete(null); + } + return; + } + + // ========== 终止性事件:授权页已销毁(安全兜底) ========== + if (code == '700020') { + debugPrint('[AliAuth] 授权页已销毁'); + if (_loginCompleter != null && !_loginCompleter!.isCompleted) { + _lastError ??= '授权页已关闭'; + _loginCompleter!.complete(null); + } + return; + } + + // ========== 终止性事件:已知错误码 ========== + if (_terminalErrorCodes.contains(code)) { + _lastError = '${event['msg'] ?? '错误码$code'}'; + debugPrint('[AliAuth] 终止性错误: $_lastError'); + if (_initCompleter != null && !_initCompleter!.isCompleted) { + _initCompleter!.completeError(_lastError!); + } + if (_loginCompleter != null && !_loginCompleter!.isCompleted) { + _loginCompleter!.complete(null); + } + return; + } + + // ========== 初始化阶段事件 ========== + if (code == '600016') { + debugPrint('[AliAuth] 预取号成功,SDK就绪'); + if (_initCompleter != null && !_initCompleter!.isCompleted) { + _initCompleter!.complete(); + } + return; + } + + // ========== 中间事件:仅记录日志,不影响流程 ========== + // 500004: SDK版本信息 + // 600001: 唤起授权页成功 + // 600024: 环境检查通过 + // 700002: 用户点击登录按钮(中间态,等待SDK返回600000或错误) + // 700003: 用户勾选协议选项 + // 700004: 用户点击协议富文本 + // 700005: 点击第三方按钮 + // 700006~700010: 二次弹窗相关事件 + debugPrint('[AliAuth] 中间事件 $code: ${event['msg'] ?? ''}'); + } + /// 初始化 SDK(只需调用一次) Future init() async { debugPrint('[AliAuth] init() called, _initialized=$_initialized, kIsWeb=$kIsWeb'); @@ -35,16 +152,91 @@ class PhoneAuthService { return; } + _initCompleter = Completer(); + try { - await AliAuth.initSdk( + // 【关键】必须先建立 EventChannel 监听,再调用 initSdk + // iOS 原生 initSdk 要求 _eventSink 不为 nil,否则直接返回错误 + AliAuth.loginListen(onEvent: _handleEvent); + + // 不要 await initSdk —— iOS 原生端不通过 MethodChannel 返回结果 + // 所有结果通过 EventChannel 返回 + // isDelay:true → 仅做初始化+预取号,不弹出登录页 + AliAuth.initSdk( AliAuthModel( PhoneAuthConfig.androidSk, PhoneAuthConfig.iosSk, isDebug: true, + isDelay: true, autoQuitPage: true, - pageType: PageType.fullPort, + + // ========== 弹窗模式(与 iOS 一致) ========== + pageType: PageType.dialogPort, + dialogWidth: 300, + dialogHeight: 340, + dialogCornerRadiusArray: [12, 12, 12, 12], + dialogAlpha: 0.5, + tapAuthPageMaskClosePage: true, + + // ========== 导航栏(弹窗模式下隐藏) ========== + navHidden: true, + navReturnHidden: true, + + // ========== Logo ========== + logoHidden: false, + logoImgPath: 'assets/www/logo.png', + logoWidth: 60, + logoHeight: 60, + logoOffsetY: 20, + + // ========== 号码栏 ========== + numberColor: '#333333', + numFieldOffsetY: 95, + + // ========== Slogan ========== + sloganHidden: true, + + // ========== 登录按钮 ========== + logBtnText: '本机号码一键登录', + logBtnTextColor: '#FFFFFF', + logBtnWidth: 250, + logBtnHeight: 44, + logBtnOffsetY: 140, + + // ========== 切换其他方式 ========== + switchAccText: '其他方式登录', + switchAccTextColor: '#999999', + switchOffsetY: 195, + + // ========== 协议 ========== + privacyState: true, + privacyBefore: '我已阅读并同意', + privacyEnd: '', + vendorPrivacyPrefix: '《', + vendorPrivacySuffix: '》', + protocolOneName: '用户协议', + protocolOneURL: 'https://qiyuan-rtc-api.airlabs.art/agreement/', + protocolTwoName: '隐私政策', + protocolTwoURL: 'https://qiyuan-rtc-api.airlabs.art/privacy/', + privacyOffsetY_B: 15, + + // ========== 背景 ========== + pageBackgroundPath: '', + backgroundColor: '#FFFFFF', + pageBackgroundRadius: 12, + isHiddenCustom: true, ), ); + + // 等待 EventChannel 返回预取号成功(600016) + await _initCompleter!.future.timeout( + const Duration(seconds: 10), + onTimeout: () { + throw TimeoutException( + '初始化超时(10s) events=${_eventLog.join(",")}'); + }, + ); + _initialized = true; _lastError = null; debugPrint('[AliAuth] SDK 初始化成功'); @@ -52,6 +244,8 @@ class PhoneAuthService { _initialized = false; _lastError = 'SDK初始化失败: $e'; debugPrint('[AliAuth] $_lastError'); + } finally { + _initCompleter = null; } } @@ -67,34 +261,25 @@ class PhoneAuthService { return null; } - final completer = Completer(); + _loginCompleter = Completer(); - AliAuth.loginListen(onEvent: (event) { - debugPrint('[AliAuth] loginListen event: $event'); - final code = event['code'] as String?; - - if (code == '600000' && event['data'] != null) { - if (!completer.isCompleted) { - completer.complete(event['data'] as String); - } - } else if (code == '700000' || code == '700001') { - _lastError = '用户取消'; - if (!completer.isCompleted) { - completer.complete(null); - } - } else if (code != null && code.startsWith('6') && code != '600000') { - _lastError = '错误码$code: ${event['msg']}'; - debugPrint('[AliAuth] $_lastError'); - if (!completer.isCompleted) { - completer.complete(null); - } + // 调用 login() 弹出运营商授权页面 + // 不要 await —— iOS 原生端不通过 MethodChannel 返回结果 + try { + debugPrint('[AliAuth] 调用 AliAuth.login() 触发登录'); + AliAuth.login(); + } catch (e) { + _lastError = '调用登录失败: $e'; + debugPrint('[AliAuth] $_lastError'); + if (!_loginCompleter!.isCompleted) { + _loginCompleter!.complete(null); } - }); + } - return completer.future.timeout( - const Duration(seconds: 30), + return _loginCompleter!.future.timeout( + const Duration(seconds: 15), onTimeout: () { - _lastError = '请求超时(30s)'; + _lastError = '登录超时(15s) events=${_eventLog.join(",")}'; debugPrint('[AliAuth] $_lastError'); return null; }, diff --git a/airhub_app/lib/core/services/phone_auth_service_stub.dart b/airhub_app/lib/core/services/phone_auth_service_stub.dart index 802866f..a71a7fd 100644 --- a/airhub_app/lib/core/services/phone_auth_service_stub.dart +++ b/airhub_app/lib/core/services/phone_auth_service_stub.dart @@ -2,6 +2,7 @@ class AliAuth { static Future initSdk(dynamic model) async {} static void loginListen({required Function(Map) onEvent}) {} + static Future login({int timeout = 5000}) async {} } class AliAuthModel { diff --git a/airhub_app/lib/features/auth/data/datasources/auth_remote_data_source.dart b/airhub_app/lib/features/auth/data/datasources/auth_remote_data_source.dart index 83c714c..717c4f3 100644 --- a/airhub_app/lib/features/auth/data/datasources/auth_remote_data_source.dart +++ b/airhub_app/lib/features/auth/data/datasources/auth_remote_data_source.dart @@ -1,5 +1,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../core/network/api_client.dart'; +import '../../../../core/network/api_config.dart'; import '../../domain/entities/auth_tokens.dart'; import '../../domain/entities/user.dart'; @@ -44,8 +45,9 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource { @override Future<({User user, AuthTokens tokens, bool isNewUser})> tokenLogin( String token) async { + // 一键授权登录使用 HTTPS 域名(阿里云号码认证要求) final data = await _apiClient.post( - '/auth/phone-login/', + '${ApiConfig.fullAuthBaseUrl}/auth/phone-login/', data: {'token': token}, ); return _parseLoginResponse(data as Map); diff --git a/airhub_app/packages/ali_auth/analysis_options.yaml b/airhub_app/packages/ali_auth/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/airhub_app/packages/ali_auth/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/airhub_app/packages/ali_auth/android/.gitignore b/airhub_app/packages/ali_auth/android/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/airhub_app/packages/ali_auth/android/build-clone.gradle b/airhub_app/packages/ali_auth/android/build-clone.gradle new file mode 100644 index 0000000..5a07cc4 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/build-clone.gradle @@ -0,0 +1,95 @@ +apply plugin: 'com.android.library' +group 'com.sean.rao.ali_auth' +version '1.3.1' + +buildscript { + repositories { + mavenLocal() +// google() +// jcenter() + // JitPack 远程仓库:https://jitpack.io + maven { url 'https://jitpack.io' } + maven { url 'https://download.flutter.io'} + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/google' } + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + } +} + +rootProject.allprojects { + repositories { + mavenLocal() +// google() +// jcenter() + // JitPack 远程仓库:https://jitpack.io + maven { url 'https://jitpack.io' } + maven { url 'https://download.flutter.io'} + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/google' } + } +} + +android { + if (project.android.hasProperty("namespace")) { + namespace = "com.sean.rao.ali_auth" + } + + compileSdkVersion 34 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + } + + defaultConfig { + minSdkVersion 21 + } + + aaptOptions { + noCompress "mov" //表示不让aapt压缩的文件后缀 + } + + buildTypes { + release { + debuggable false + // minifyEnabled true + proguardFile file('proguard-rules.pro') + zipAlignEnabled false + multiDexEnabled true + } + + debug { + // minifyEnabled false + zipAlignEnabled false + multiDexEnabled true + } + } + + dependencies { + implementation "androidx.appcompat:appcompat:1.7.1" + + // 兼容安卓版本的fastjson库 + implementation 'com.alibaba.fastjson2:fastjson2:2.0.51.android5' + // implementation 'com.google.code.gson:gson:2.10.1' + // implementation 'com.alibaba:fastjson:1.2.83' + + implementation files("libs/auth_number_product-${authLibVersion}-release.aar") + implementation files("libs/logger-${loggerVersion}-release.aar") + implementation files("libs/main-${mainVersion}-release.aar") + + // 吐司框架:https://github.com/getActivity/Toaster + implementation 'com.github.getActivity:Toaster:12.2' + + // uc的crash收集库,客户可自行选择是否需要集成 + // implementation 'com.ucweb.wpk:crashsdk:3.2.2.2' + } +} \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/build.gradle b/airhub_app/packages/ali_auth/android/build.gradle new file mode 100644 index 0000000..2170b7c --- /dev/null +++ b/airhub_app/packages/ali_auth/android/build.gradle @@ -0,0 +1,107 @@ +apply plugin: 'com.android.library' +group 'com.sean.rao.ali_auth' +version '1.3.7' + +buildscript { + repositories { + mavenLocal() + mavenCentral() +// google() +// jcenter() + maven { url 'https://dl.bintray.com/jetbrains/anko'} + maven { url 'https://download.flutter.io'} + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/google' } + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + } +} + +// 定义一个方法, 用于获取当前moudle的dir +def getCurrentProjectDir() { + String result = "" + rootProject.allprojects { project -> + if (project.properties.get("identityPath").toString() == ":ali_auth") { // 这里是flutter的约定, 插件的module名是插件名, :是gradle的约定. project前加: + result = project.properties.get("projectDir").toString() + } + } + return result +} + +rootProject.allprojects { + def dir = getCurrentProjectDir() + repositories { + mavenLocal() + mavenCentral() +// google() +// jcenter() + maven { url 'https://dl.bintray.com/jetbrains/anko'} + maven { url 'https://download.flutter.io'} + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/google' } + + // 一键登录本地依赖 + flatDir { + dirs project(':ali_auth').file('libs') + } + } +} + +android { + if (project.android.hasProperty("namespace")) { + namespace = "com.sean.rao.ali_auth" + } + + compileSdkVersion 35 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 21 + } + + aaptOptions { + noCompress "mov" //表示不让aapt压缩的文件后缀 + } + + buildTypes { + release { + debuggable false + // minifyEnabled true + proguardFile file('proguard-rules.pro') + zipAlignEnabled false + multiDexEnabled true + } + + debug { + // minifyEnabled false + zipAlignEnabled false + multiDexEnabled true + } + } + +} + +dependencies { + implementation "androidx.appcompat:appcompat:1.3.0" + + // 兼容安卓版本的fastjson库 + implementation "com.alibaba.fastjson2:fastjson2:2.0.51.android5" + // implementation 'com.google.code.gson:gson:2.10.1' + // implementation 'com.alibaba:fastjson:1.2.83' + + implementation(name: "auth_number_product-${authLibVersion}-release", ext:'aar') + implementation(name: "logger-${loggerVersion}-release", ext:'aar') + implementation(name: "main-${mainVersion}-release", ext:'aar') + + // 吐司框架:https://github.com/getActivity/Toaster + // implementation 'com.github.getActivity:Toaster:12.2' + + // uc的crash收集库,客户可自行选择是否需要集成 + // implementation 'com.ucweb.wpk:crashsdk:3.2.2.2' +} \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/gradle.properties b/airhub_app/packages/ali_auth/android/gradle.properties new file mode 100644 index 0000000..c5699a9 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +android.injected.testOnly=false + +authLibVersion=2.14.19 +loggerVersion=2.2.2 +mainVersion=2.2.3 \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/libs/auth_number_product-2.14.19-release.aar b/airhub_app/packages/ali_auth/android/libs/auth_number_product-2.14.19-release.aar new file mode 100644 index 0000000..4833acb Binary files /dev/null and b/airhub_app/packages/ali_auth/android/libs/auth_number_product-2.14.19-release.aar differ diff --git a/airhub_app/packages/ali_auth/android/libs/deploy.sh b/airhub_app/packages/ali_auth/android/libs/deploy.sh new file mode 100644 index 0000000..9674aa6 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/libs/deploy.sh @@ -0,0 +1,3 @@ +mvn deploy:deploy-file -Dfile=sdk.aar -Durl="file://." -DgroupId="com.pgyer" -DartifactId="sdk" -Dversion="3.0.9" + +mvn deploy:deploy-file -Dfile=sdk.aar -Durl="file://." -DgroupId="com.mobile.auth" -DartifactId="auth-number-product" -Dversion="2.14.14" \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/libs/logger-2.2.2-release.aar b/airhub_app/packages/ali_auth/android/libs/logger-2.2.2-release.aar new file mode 100644 index 0000000..cd9d4aa Binary files /dev/null and b/airhub_app/packages/ali_auth/android/libs/logger-2.2.2-release.aar differ diff --git a/airhub_app/packages/ali_auth/android/libs/main-2.2.3-release.aar b/airhub_app/packages/ali_auth/android/libs/main-2.2.3-release.aar new file mode 100644 index 0000000..277947a Binary files /dev/null and b/airhub_app/packages/ali_auth/android/libs/main-2.2.3-release.aar differ diff --git a/airhub_app/packages/ali_auth/android/proguard-rules.pro b/airhub_app/packages/ali_auth/android/proguard-rules.pro new file mode 100644 index 0000000..299af4f --- /dev/null +++ b/airhub_app/packages/ali_auth/android/proguard-rules.pro @@ -0,0 +1,10 @@ +# 感谢 ZengJo的贡献 +-keep class android.app.ActivityThread {*;} +-keep class android.os.SystemProperties {*;} + +-keep class com.mobile.auth.** { *; } +-keep class com.alibaba.sdk.android.* { *; } +-keep class com.alicom.* { *; } +-keep class com.taobao.* { *; } +-keep class androidx.appcompat.** { *; } +-keep class com.cmic.** { *; } \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/settings.gradle b/airhub_app/packages/ali_auth/android/settings.gradle new file mode 100644 index 0000000..344769d --- /dev/null +++ b/airhub_app/packages/ali_auth/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'ali_auth' diff --git a/airhub_app/packages/ali_auth/android/src/main/AndroidManifest.xml b/airhub_app/packages/ali_auth/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cc23467 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/AndroidManifest.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/AliAuthPlugin.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/AliAuthPlugin.java new file mode 100644 index 0000000..aa2870b --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/AliAuthPlugin.java @@ -0,0 +1,306 @@ +package com.sean.rao.ali_auth; + +import android.app.Activity; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkRequest; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.FlutterEngineCache; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +import com.sean.rao.ali_auth.common.LoginParams; +import com.sean.rao.ali_auth.login.OneKeyLoginPublic; +import com.sean.rao.ali_auth.utils.UtilTool; + +/** AliAuthPlugin */ +public class AliAuthPlugin extends FlutterActivity implements FlutterPlugin, ActivityAware, MethodCallHandler, EventChannel.StreamHandler { + private static final String TAG = "ali_auth 一键登录插件"; + + private Context mContext; + private Activity mActivity; + /// The MethodChannel that will the communication between Flutter and native Android + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + + private FlutterEngine flutterEngine; + + private FlutterEngineCache flutterEngineCache; + + public static EventChannel.EventSink _events; + + private static final String METHOD_CHANNEL = "ali_auth"; + private static final String EVENT_CHANNEL = "ali_auth/event"; + + private ConnectivityManager.NetworkCallback callback; + + /** + * 延时登录 + */ + private OneKeyLoginPublic oneKeyLoginPublic; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), METHOD_CHANNEL); + + // 原生通讯 + EventChannel eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), EVENT_CHANNEL); + eventChannel.setStreamHandler(this); +// // Activity +// flutterView.getLookupKeyForAsset("images/ic_launcher.png"); +// // Fragment +// (FlutterView) getView().getLookupKeyForAsset("images/ic_launcher.png"); +// // 通用 +// FlutterMain.getLookupKeyForAsset("images/ic_launcher.png"); + + // 创建flutter发动机 + flutterEngineCache = FlutterEngineCache.getInstance(); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + switch (call.method){ + case "getPlatformVersion": + result.success("当前Android信息:" + android.os.Build.VERSION.RELEASE); + break; + case "getCurrentCarrierName": + // CMCC(移动)、CUCC(联通)、CTCC(电信) + String carrierName = "获取失败"; + if (oneKeyLoginPublic == null) { + PhoneNumberAuthHelper phoneNumberAuthHelper = PhoneNumberAuthHelper.getInstance(mActivity, null); + carrierName = phoneNumberAuthHelper.getCurrentCarrierName(); + } else { + carrierName = oneKeyLoginPublic.getCurrentCarrierName(); + } + if (carrierName.contains("CMCC")) { + carrierName = "中国移动"; + } else if (carrierName.contains("CUCC")) { + carrierName = "中国联通"; + } else if (carrierName.contains("CTCC")) { + carrierName = "中国电信"; + } + result.success(carrierName); + break; + case "initSdk": + JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(call.arguments)); + if (_events == null) { + result.error("500001", "请先对插件进行监听!", null); + } else { + boolean isDelay = jsonObject.getBoolean("isDelay"); + /// 判断是否初始化过或者是否是同步登录,如果是将进行再次初始化 + if (oneKeyLoginPublic == null || !isDelay) { + try { + oneKeyLoginPublic = new OneKeyLoginPublic(mActivity, _events, call.arguments); + } catch (Exception e) { + Log.e(TAG, "initSdk 初始化异常: " + e.getMessage(), e); + _events.success(UtilTool.resultFormatData("500000", "SDK初始化异常: " + e.getMessage(), "")); + } + } + result.success(null); + } + break; + case "login": + if (oneKeyLoginPublic != null) { + oneKeyLoginPublic.startLogin(LoginParams.jsonObject.getIntValue("timeout", 5000)); + } else { + _events.success(UtilTool.resultFormatData("500003", null, "")); + } + result.success(null); + break; + case "checkEnvAvailable": + oneKeyLoginPublic.checkEnvAvailable(2); + result.success(null); + break; + case "queryCheckBoxIsChecked": + boolean status = oneKeyLoginPublic.queryCheckBoxIsChecked(); + result.success(status); + break; + case "setCheckboxIsChecked": + boolean checked = oneKeyLoginPublic.setCheckBoxIsChecked(); + result.success(checked); + break; + case "checkCellularDataEnable": + isNetEnabled(mContext, result); + break; + case "quitPage": + oneKeyLoginPublic.quitPage(); + result.success(null); + break; + case "hideLoading": + oneKeyLoginPublic.hideLoading(); + result.success(null); + break; + case "openPage": + if (flutterEngine == null) { + flutterEngine = new FlutterEngine(mContext); + } + //指定想要跳转的flutter页面 这里要和下图对应上 记住他 + flutterEngine.getNavigationChannel().setInitialRoute(call.argument("pageRoute")); + flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault()); + //这里做一个缓存 可以在适当的地方执行他 比如MyApp里 或者未跳转flutterr之前 在flutter页面执行前预加载 + //缓存可以缓存好多个 以不同的的id区分 + flutterEngineCache.put("default_engine_id", flutterEngine); + //上面的代码一般在执行跳转操作之前调用 这样可以预加载页面 是的跳转的时候速度加快 + //跳转页面 + mActivity.startActivity(FlutterActivity.withCachedEngine("default_engine_id").build(mContext)); + result.success("调用成功!"); + break; + default: + result.notImplemented(); + } + } + + @Override + public void onListen(Object o, EventChannel.EventSink eventSink) { + Log.d(TAG, "listen 初始化完毕!"); + String version = PhoneNumberAuthHelper.getVersion(); + eventSink.success(UtilTool.resultFormatData("500004", String.format("插件启动监听成功, 当前SDK版本: %s", version), "")); + if( _events == null ){ + _events = eventSink; + } + } + + @Override + public void onCancel(Object o) { + if( _events != null){ + _events = null; + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } + + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + mActivity = binding.getActivity(); + mContext = mActivity.getBaseContext(); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + } + + @Override + public void onDetachedFromActivity() { + if( _events != null){ + _events.endOfStream(); + } + mActivity = null; + } + + /** + * 判断移动网络是否开启 + * + * @param context + * @return + */ + public void isNetEnabled(Context context, @NonNull Result result) { + JSONObject resultObject = new JSONObject(); + resultObject.put("code", 0); + resultObject.put("msg", "未检测到网络!"); + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { +// callback = new ConnectivityManager.NetworkCallback() { +// // 可用网络接入 +// public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { +// // 一般在此处获取网络类型然后判断网络类型,就知道时哪个网络可以用connected +// System.out.println(network); +// System.out.println(networkCapabilities); +// } +// // 网络断开 +// public void onLost(Network network) { +// System.out.println(network); +// // 如果通过ConnectivityManager#getActiveNetwork()返回null,表示当前已经没有其他可用网络了。 +// } +// }; +// registerNetworkCallback(context); + + Network network =cm.getActiveNetwork(); + if(network!=null){ + NetworkCapabilities nc=cm.getNetworkCapabilities(network); + if(nc!=null){ + resultObject.put("code", 1); + if(nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)){ // WIFI + resultObject.put("msg", "WIFI网络已开启"); + }else if(nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)){ // 移动数据 + resultObject.put("msg", "蜂窝网络已开启"); + } + } + } + } else { + NetworkInfo mWiFiNetworkInfo = cm.getActiveNetworkInfo(); + if (mWiFiNetworkInfo != null) { + resultObject.put("code", 1); + if (mWiFiNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { // WIFI + resultObject.put("msg", "WIFI网络已开启"); + } else if (mWiFiNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE) { // 移动数据 + resultObject.put("msg", "蜂窝网络已开启"); + } + } + } + result.success(resultObject); + } + + // 注册回调 + private void registerNetworkCallback(Context context) { + ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + cm.registerNetworkCallback(builder.build(), callback); + } + } + + // 注销回调 + private void unregisterNetworkCallback(Context context) { + ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cm.unregisterNetworkCallback(callback); + } + } + + /** + * 判断移动网络是否连接成功 + * + * @param context + * @return + */ + public boolean isNetContected(Context context) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + if (info != null && info.isConnected()) { + Log.i(TAG, "移动网络连接成功"); + return true; + } + Log.i(TAG, "移动网络连接失败"); + return false; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/CustomWebViewActivity.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/CustomWebViewActivity.java new file mode 100644 index 0000000..d9996ac --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/CustomWebViewActivity.java @@ -0,0 +1,73 @@ +package com.sean.rao.ali_auth.auth; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Toolbar; + +import com.mobile.auth.gatewayauth.Constant; +import com.sean.rao.ali_auth.R; + +import org.jetbrains.annotations.Nullable; + +/** + * @ProjectName: NumberAuthSDK_Standard_Android + * @Package: com.aliqin.mytel.auth + * @ClassName: CustomWebView + * @Description: 自定义协议展示页 + * @Author: liuqi + * @CreateDate: 2021/3/25 4:04 PM + * @Version: 1.0 + */ +public class CustomWebViewActivity extends Activity { + private WebView mWebView; + private Toolbar mToolbar; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_custom_web); + String mUrl = getIntent().getStringExtra(Constant.PROTOCOL_WEB_VIEW_URL); + String mName = getIntent().getStringExtra(Constant.PROTOCOL_WEB_VIEW_NAME); + setRequestedOrientation( + getIntent().getIntExtra(Constant.PROTOCOL_WEB_VIEW_ORIENTATION, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)); + mWebView = findViewById(R.id.webView); + mToolbar = findViewById(R.id.toolbar); + +// mToolbar.setSubtitle(mName); + initWebView(); + mWebView.loadUrl(mUrl); + } + + @SuppressLint("SetJavaScriptEnabled") + private void initWebView() { + WebSettings wvSettings = mWebView.getSettings(); + // 是否阻止网络图像 + wvSettings.setBlockNetworkImage(false); + // 是否阻止网络请求 + wvSettings.setBlockNetworkLoads(false); + // 是否加载JS + wvSettings.setJavaScriptEnabled(true); + wvSettings.setJavaScriptCanOpenWindowsAutomatically(true); + //覆盖方式启动缓存 + wvSettings.setCacheMode(WebSettings.LOAD_DEFAULT); + // 使用广泛视窗 + wvSettings.setUseWideViewPort(true); + wvSettings.setLoadWithOverviewMode(true); + wvSettings.setDomStorageEnabled(true); + //是否支持缩放 + wvSettings.setBuiltInZoomControls(false); + wvSettings.setSupportZoom(false); + //不显示缩放按钮 + wvSettings.setDisplayZoomControls(false); + wvSettings.setAllowFileAccess(true); + wvSettings.setDatabaseEnabled(true); + //缓存相关 + // wvSettings.setAppCacheEnabled(true); + wvSettings.setDomStorageEnabled(true); + wvSettings.setDatabaseEnabled(true); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/NumberAuthActivity.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/NumberAuthActivity.java new file mode 100644 index 0000000..ce2800b --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/auth/NumberAuthActivity.java @@ -0,0 +1,145 @@ +package com.sean.rao.ali_auth.auth; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import org.jetbrains.annotations.Nullable; + +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import com.mobile.auth.gatewayauth.PreLoginResultListener; +import com.mobile.auth.gatewayauth.ResultCode; +import com.mobile.auth.gatewayauth.TokenResultListener; +import com.mobile.auth.gatewayauth.model.TokenRet; +import com.sean.rao.ali_auth.utils.ExecutorManager; + +public class NumberAuthActivity extends Activity { + private static final String TAG = NumberAuthActivity.class.getSimpleName(); + private PhoneNumberAuthHelper mAuthHelper; + private TokenResultListener mVerifyListener; + private Button mAuthButton; + private EditText mNumberEt; + private String phoneNumber; + private ProgressDialog mProgressDialog; + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); +// setContentView(R.layout.activity_auth); +// mAuthButton = findViewById(R.id.auth_btn); +// mNumberEt = findViewById(R.id.et_number); + mAuthButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + phoneNumber = mNumberEt.getText().toString(); + //判断手机号是否合法 + if (!TextUtils.isEmpty(phoneNumber)) { + showLoadingDialog("正在进行本机号码校验"); + numberAuth(5000); + } + } + }); + sdkInit(); + accelerateVerify(5000); + } + + private void sdkInit() { + mVerifyListener = new TokenResultListener() { + @Override + public void onTokenSuccess(String s) { + Log.i(TAG, "获取token成功:" + s); + try { + TokenRet pTokenRet = TokenRet.fromJson(s); + if (ResultCode.CODE_SUCCESS.equals(pTokenRet.getCode()) && !TextUtils.isEmpty(pTokenRet.getToken())) { + getResultWithToken(pTokenRet.getToken(), phoneNumber); + } + mAuthHelper.setAuthListener(null); + } catch (Exception e) { + e.fillInStackTrace(); + } + } + + @Override + public void onTokenFailed(final String s) { + Log.i(TAG, "获取token失败:" + s); + NumberAuthActivity.this.runOnUiThread(new Runnable() { + @Override + public void run() { + hideLoadingDialog(); + setResult(2); + finish(); + } + }); + mAuthHelper.setAuthListener(null); + } + }; + mAuthHelper = PhoneNumberAuthHelper.getInstance(getApplicationContext(), mVerifyListener); + } + + /** + * 加速校验 + * 进入输入手机号页面调用此接口,用户输入完手机号点击确定可以更快的获取token + * @param timeout + */ + public void accelerateVerify(int timeout) { + mAuthHelper.accelerateVerify(timeout, new PreLoginResultListener() { + @Override + public void onTokenSuccess(String vendor) { + //成功时返回运营商简称 + Log.i(TAG, "accelerateVerify:" + vendor); + } + + @Override + public void onTokenFailed(String vendor, String errorMsg) { + Log.e(TAG, "accelerateVerify:" + vendor + ", " + errorMsg); + } + }); + } + + public void numberAuth(int timeout) { + mAuthHelper.setAuthListener(mVerifyListener); + mAuthHelper.getVerifyToken(timeout); + } + + public void getResultWithToken(final String token, final String phoneNumber) { + ExecutorManager.run(new Runnable() { + @Override + public void run() { + // final String result = verifyNumber(token, phoneNumber); + NumberAuthActivity.this.runOnUiThread(new Runnable() { + @Override + public void run() { + hideLoadingDialog(); + Intent pIntent = new Intent(); + pIntent.putExtra("result", phoneNumber); + setResult(1, pIntent); + finish(); + } + }); + } + }); + } + + public void showLoadingDialog(String hint) { + if (mProgressDialog == null) { + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + } + mProgressDialog.setMessage(hint); + mProgressDialog.setCancelable(true); + mProgressDialog.show(); + } + + public void hideLoadingDialog() { + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CacheManage.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CacheManage.java new file mode 100644 index 0000000..abfb488 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CacheManage.java @@ -0,0 +1,213 @@ +package com.sean.rao.ali_auth.common; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; +import android.util.LruCache; + +import com.nirvana.tools.core.CryptUtil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Package: com.aliqin.mytel + * @ClassName: CacheManage + * @Description: 类作用描述 + * @Author: liuqi + * @CreateDate: 2021/5/13 9:55 AM + * @Version: 1.0 + */ +public class CacheManage { + Context context; + private LruCache mBitmapLruCache = null; + private ConcurrentHashMap mFilePathCache = null; + private final static String CACHE_FILE_DIR = "ALSDK_FILE_CACHE"; + private final static String CACHE_COVER_DIR = "ALSDK_COVER_CACHE"; + + public CacheManage(Context context) { + this.context = context; + final File dir = new File(context.getCacheDir(), CACHE_FILE_DIR); + if (dir.exists()) { + mFilePathCache = new ConcurrentHashMap(); + File[] listFiles = dir.listFiles(); + if (null != listFiles && listFiles.length > 0) { + for (File file : listFiles) { + String fileName = file.getName(); + String key = fileName.substring(0, fileName.lastIndexOf(".")); + mFilePathCache.put(key, file.getAbsolutePath()); + } + } + } + } + + /** + * 创建缓存文件 + * + * @param url + * @param ext 文件后缀 + * @return + */ + public File createCacheFile(String url, String ext) { + final File dir = new File(context.getCacheDir(), CACHE_FILE_DIR); + if (!dir.exists()) { + dir.mkdirs(); + } + String key = url; + try { + key = CryptUtil.md5Hex(url); + } catch (Exception e) { + key = url; + } + return new File(dir, key + ext); + } + + /** + * 获取bitma内存缓存 + * + * @return + */ + public LruCache getBitmapLruCache() { + if (mBitmapLruCache == null) { + long mTotalSize = Runtime.getRuntime().totalMemory(); + mBitmapLruCache = new LruCache<>((int)(mTotalSize / 5)); + } + return mBitmapLruCache; + } + + /** + * 获取文件缓存 + * + * @param url + * @return + */ + public String getCacheFilePath(String url) { + String key = url; + try { + key = CryptUtil.md5Hex(url); + } catch (Exception e) { + key = url; + } + if (mFilePathCache == null) { + return null; + } else { + return mFilePathCache.get(key); + } + } + + /** + * 缓存bitmap到内存 + * + * @param url + * @param bitmap + */ + public void cacheBitmap(String url, Bitmap bitmap) { + String key = url; + try { + key = CryptUtil.md5Hex(url); + } catch (Exception e) { + key = url; + } + checkAndCreateBitmapCache(); + mBitmapLruCache.put(key, bitmap); + SaveImageDisk(bitmap, key); + } + + /** + * 获取内存缓存的bitmap + * + * @param url + * @return + */ + public Bitmap getBitmap(String url) { + String key = url; + try { + key = CryptUtil.md5Hex(url); + } catch (Exception e) { + key = url; + } + if (mBitmapLruCache == null) { + return getBitmapFromDisk(key + ".jpg"); + } else { + return mBitmapLruCache.get(key); + } + } + + /** + * 检查和创建内存缓存 + */ + public void checkAndCreateBitmapCache() { + if (mBitmapLruCache == null) { + long mTotalSize = Runtime.getRuntime().totalMemory(); + mBitmapLruCache = new LruCache<>((int)(mTotalSize / 5)); + final File dir = new File(context.getCacheDir(), CACHE_COVER_DIR); + if (dir.exists()) { + File[] listFiles = dir.listFiles(); + if (null != listFiles && listFiles.length > 0) { + for (File file : listFiles) { + String fileName = file.getName(); + fileName = fileName.substring(0, fileName.lastIndexOf(".")); + String key = fileName; + mBitmapLruCache.put(key, getBitmapFromDisk(file.getAbsolutePath())); + } + } + } + } + } + + /** + * 缓存图片到硬盘 + * @param image + * @param url + */ + public void SaveImageDisk(Bitmap image, String url) { + final File dir = new File(context.getCacheDir(), CACHE_COVER_DIR); + if (!dir.exists()) { + dir.mkdirs(); + } + String key = url; + try { + key = CryptUtil.md5Hex(key); + } catch (Exception e) { + key = url; + } + File imageDir = new File(dir, key + ".jpg"); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(imageDir); + image.compress(Bitmap.CompressFormat.JPEG, 100, fos); + fos.flush(); + fos.close(); + } catch (FileNotFoundException e) { + Log.e("CacheManage", "SaveImage:e" + e.getMessage()); + } catch (IOException e) { + Log.e("CacheManage", "SaveImage:e" + e.getMessage()); + } + } + + /** + * 从硬盘读取文件 + * @param path + * @return + */ + public Bitmap getBitmapFromDisk(String path) { + Bitmap bitmap = null; + FileInputStream fis = null; + File f = new File(path); + if (!f.exists()) { + return null; + } + try { + fis = new FileInputStream(path); + bitmap = BitmapFactory.decodeStream(fis); + } catch (FileNotFoundException e) { + Log.e("CacheManage", "getBitmap:e" + e.getMessage()); + } + return bitmap; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/Constant.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/Constant.java new file mode 100644 index 0000000..c996b24 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/Constant.java @@ -0,0 +1,55 @@ +package com.sean.rao.ali_auth.common; + + +public class Constant { + public static final String[] TYPES = { + "全屏(竖屏)", "全屏(横屏)", "弹窗(竖屏)", + "弹窗(横屏)", "底部弹窗", "自定义View", "自定义View(Xml)","自定义Gif背景","自定义视频背景(mov,mp4)","自定义图片背景" + }; + /** + * 全屏(竖屏) + */ + public static final int FULL_PORT = 0; + /** + * 全屏(横屏) + */ + public static final int FULL_LAND = 1; + /** + * 弹窗(竖屏) + */ + public static final int DIALOG_PORT = 2; + /** + * "弹窗(横屏) + */ + public static final int DIALOG_LAND = 3; + /** + * 底部弹窗 + */ + public static final int DIALOG_BOTTOM = 4; + /** + * 自定义View + */ + public static final int CUSTOM_VIEW = 5; + /** + * 自定义View(Xml) + */ + public static final int CUSTOM_XML = 6; + /** + * 自定义背景GIF + */ + public static final int CUSTOM_GIF = 7; + /** + * 自定义背景视频 + */ + public static final int CUSTOM_MOV = 8; + /** + * 自定义背景图片 + */ + public static final int CUSTOM_PIC = 9; + + public static final String THEME_KEY = "theme"; + + public static final String LOGIN_TYPE = "login_type"; + public static final int LOGIN = 1; + public static final int LOGIN_DELAY = 2; +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomAuthUIControlClickListener.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomAuthUIControlClickListener.java new file mode 100644 index 0000000..170f38d --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomAuthUIControlClickListener.java @@ -0,0 +1,73 @@ +package com.sean.rao.ali_auth.common; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import com.alibaba.fastjson2.JSONException; +import com.alibaba.fastjson2.JSONObject; +import com.mobile.auth.gatewayauth.AuthUIConfig; +import com.mobile.auth.gatewayauth.AuthUIControlClickListener; +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import com.mobile.auth.gatewayauth.ResultCode; +import com.sean.rao.ali_auth.utils.StatusAll; +import com.sean.rao.ali_auth.utils.UtilTool; + +import io.flutter.plugin.common.EventChannel; + +/** + * @ProjectName: android + * @Package: com.sean.rao.ali_auth.common + * @ClassName: CUstomAuthUIControlClickListener + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 5/12/22 5:26 PM + * @UpdateUser: 更新者 + * @UpdateDate: 5/12/22 5:26 PM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +public class CustomAuthUIControlClickListener extends LoginParams implements AuthUIControlClickListener { + private final String TAG = "CustomAuth: "; + + public CustomAuthUIControlClickListener() { + } + + @Override + public void onClick(String code, Context context, String jsonString) { + JSONObject jsonDataObj = new JSONObject(); + if(!TextUtils.isEmpty(jsonString)) { + jsonDataObj = JSONObject.parseObject(jsonString); + } + + switch (code) { + //点击授权页默认样式的返回按钮 + case ResultCode.CODE_ERROR_USER_CANCEL: + Log.e(TAG, "点击了授权页默认返回按钮"); + break; + //点击授权页默认样式的切换其他登录方式 会关闭授权页 + //如果不希望关闭授权页那就setSwitchAccHidden(true)隐藏默认的 通过自定义view添加自己的 + case ResultCode.CODE_ERROR_USER_SWITCH: + Log.e(TAG, "点击了授权页默认切换其他登录方式"); + break; + //点击一键登录按钮会发出此回调 + //当协议栏没有勾选时 点击按钮会有默认toast 如果不需要或者希望自定义内容 setLogBtnToastHidden(true)隐藏默认Toast + //通过此回调自己设置toast + case ResultCode.CODE_ERROR_USER_LOGIN_BTN: + break; + //checkbox状态改变触发此回调 + case ResultCode.CODE_ERROR_USER_CHECKBOX: + isChecked = jsonDataObj.getBooleanValue("isChecked"); + Log.e(TAG, "checkbox状态变为" + jsonDataObj.getBooleanValue("isChecked")); + jsonDataObj = null; + break; + //点击协议栏触发此回调 + case ResultCode.CODE_ERROR_USER_PROTOCOL_CONTROL: + Log.e(TAG, "点击协议," + "name: " + jsonDataObj.getString("name") + ", url: " + jsonDataObj.getString("url")); + break; + default: + break; + } + showResult(code, null, jsonDataObj); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomRoundedDrawable.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomRoundedDrawable.java new file mode 100644 index 0000000..99e29b1 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/CustomRoundedDrawable.java @@ -0,0 +1,73 @@ +package com.sean.rao.ali_auth.common; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class CustomRoundedDrawable extends Drawable { + + private final Bitmap mBitmap; + private final Paint mPaint; + private final RectF mRectF; + private final float mRadius; + + public CustomRoundedDrawable(Bitmap bitmap, float radius) { + mBitmap = bitmap; + mRadius = radius; + + BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setShader(shader); + + mRectF = new RectF(); + } + + @Override + public void draw(@NonNull Canvas canvas) { + mRectF.set(getBounds()); + // 只对左上角和右上角设置圆角 + float[] radii = {mRadius, 0, 0, mRadius, 0f, 0f, 0f, 0f}; +// canvas.drawRoundRect(mRectF, mRadius, mRadius, mPaint); +// canvas.drawRoundRect(mRectF, radii, mPaint); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); +// paint.setColor(0xFF0000FF); + Path path = new Path(); + path.addRoundRect(mRectF, radii, Path.Direction.CW); + + canvas.drawPath(path, paint); // 绘制具有四个不同圆角的矩形 + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return mPaint.getAlpha(); + } + + @Override + public int getIntrinsicWidth() { + return mBitmap.getWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mBitmap.getHeight(); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/DownloadFile.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/DownloadFile.java new file mode 100644 index 0000000..3979109 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/DownloadFile.java @@ -0,0 +1,75 @@ +package com.sean.rao.ali_auth.common; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; + +import static android.os.FileUtils.copy; + +/** + * @ProjectName: android + * @Package: com.sean.rao.ali_auth.common + * @ClassName: DownloadFile + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 5/1/22 11:01 AM + * @UpdateUser: 更新者 + * @UpdateDate: 5/1/22 11:01 AM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +class DownloadFile { + public static InputStream getImageToInputStream(String path) { //连接远程网址 + InputStream inputStream = null; + try { + URL url = new URL(path); + HttpURLConnection conn=(HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5000); + conn.setRequestMethod("GET"); + if (conn.getResponseCode() == 200) { + inputStream = conn.getInputStream(); + } + } catch (IOException e) { + e.fillInStackTrace(); + } + + return inputStream; + } + + public static Bitmap getImageToBitmap(String path) { //连接远程网址 + Bitmap bitmap = null; + try { + URL url = new URL(path); + HttpURLConnection conn=(HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5000); + conn.setRequestMethod("GET"); + if (conn.getResponseCode() == 200) { + InputStream inputStream = conn.getInputStream(); + final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + OutputStream outputStream = new BufferedOutputStream(dataStream, 4 * 1024); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + copy(inputStream,outputStream); + } + outputStream.flush(); + final byte[] data =dataStream.toByteArray(); + bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); + } + } catch (IOException e) { + e.fillInStackTrace(); + } + + return bitmap; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/GifAnimationDrawable.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/GifAnimationDrawable.java new file mode 100644 index 0000000..4207f1e --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/GifAnimationDrawable.java @@ -0,0 +1,1098 @@ +package com.sean.rao.ali_auth.common; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.os.SystemClock; +import android.util.Log; +import android.view.Gravity; + +import androidx.annotation.NonNull; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; + +public class GifAnimationDrawable extends Drawable implements Runnable, Animatable { + private static final String TAG = "GifAnimationDrawable"; + private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG; + + private int mCurFrame = -1; + + private int mLoop = 0; + private boolean mMutated; + private GifDecoder decoder; + private boolean mOneShot; + private final Rect mDstRect = new Rect(); + private boolean mApplyGravity; + private Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); + private int mGravity = Gravity.FILL; + private Bitmap mBitmap; + + @Override + protected void onBoundsChange(@NonNull Rect bounds) { + super.onBoundsChange(bounds); + mApplyGravity = true; + } + + public final Paint getPaint() { + return mPaint; + } + + public void setAntiAlias(boolean aa) { + mPaint.setAntiAlias(aa); + } + + @Override + public void setFilterBitmap(boolean filter) { + mPaint.setFilterBitmap(filter); + } + + @Override + public void setDither(boolean dither) { + mPaint.setDither(dither); + } + + public void setGravity(int gravity) { + mGravity = gravity; + mApplyGravity = true; + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (mBitmap == null) { + Log.e(TAG, "bmp is invalid"); + return; + } + + if (mApplyGravity) { + Gravity.apply(mGravity, decoder.getWidth(), decoder.getHeight(), getBounds(), mDstRect); + mApplyGravity = false; + } + canvas.drawBitmap(mBitmap, null, mDstRect, mPaint); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (visible) { + if (changed || restart) { + setFrame(0, true, false); + } + } else { + unscheduleSelf(this); + } + return changed; + } + + @Override + public int getOpacity() { + if (mGravity != Gravity.FILL) { + return PixelFormat.TRANSLUCENT; + } + + Bitmap bm = mBitmap; + return (bm == null || bm.hasAlpha() || mPaint.getAlpha() < 255) ? + PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; + } + + /** + * @return current loop number, this value is not resets + */ + public int getLoop() { + return mLoop; + } + + /** + *

Starts the animation, looping if necessary. This method has no effect + * if the animation is running.

+ * + * @see #isRunning() + * @see #stop() + */ + @Override + public void start() { + if (!isRunning()) { + run(); + } + } + + /** + *

Stops the animation. This method has no effect if the animation is + * not running.

+ * + * @see #isRunning() + * @see #start() + */ + @Override + public void stop() { + if (isRunning()) { + unscheduleSelf(this); + } + } + + /** + *

Indicates whether the animation is currently running or not.

+ * + * @return true if the animation is running, false otherwise + */ + @Override + public boolean isRunning() { + return mCurFrame > -1; + } + + /** + *

This method exists for implementation purpose only and should not be + * called directly. Invoke {@link #start()} instead.

+ * + * @see #start() + */ + @Override + public void run() { + nextFrame(false); + } + + public void preloadFirstFrame() { + if (!isRunning()) { + mBitmap = decoder.getFirstFrame(); + } + } + + @Override + public void unscheduleSelf(@NonNull Runnable what) { + mCurFrame = -1; + preloadFirstFrame(); + super.unscheduleSelf(what); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + /** + * @return The number of frames in the animation + */ + public int getNumberOfFrames() { + return decoder.getFrameCount(); + } + + /** + * @return The duration in milliseconds of the frame at the + * specified index + */ + public int getDuration(int i) { + return decoder.getDelay(i); + } + + /** + * @return True of the animation will play once, false otherwise + */ + public boolean isOneShot() { + return mOneShot; + } + + /** + * Sets whether the animation should play once or repeat. + * + * @param oneShot Pass true if the animation should only play once + */ + public void setOneShot(boolean oneShot) { + mOneShot = oneShot; + } + + private void nextFrame(boolean unschedule) { + int next = mCurFrame + 1; + final int N = decoder.getFrameCount(); + if (next >= N) { + mLoop++; + next = 0; + } + // assert next == decoder.getFramePointer(); + setFrame(next, unschedule, !mOneShot || next < (N - 1)); + } + + private void setFrame(int frame, boolean unschedule, boolean animate) { + if (frame >= decoder.getFrameCount()) { + return; + } + mCurFrame = frame; + //selectDrawable(frame); + decoder.advance(); + mBitmap = decoder.getNextFrame(); + invalidateSelf(); + if (unschedule) { + unscheduleSelf(this); + } + if (animate) { + scheduleSelf(this, SystemClock.uptimeMillis() + decoder.getNextDelay()); + } + } + + @Override + public int getIntrinsicHeight() { + return decoder.getHeight(); + } + + @Override + public int getIntrinsicWidth() { + return decoder.getWidth(); + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mMutated = true; + } + return this; + } + + public GifAnimationDrawable(InputStream in) { + decoder = new GifDecoder(); + decoder.read(in, 0); + } + + public GifAnimationDrawable(String string) throws IOException { + FileInputStream fis = new FileInputStream(string); + try { + decoder = new GifDecoder(); + decoder.read(fis, fis.available()); + decoder.advance(); + } finally { + fis.close(); + } + } + + /** + * Copyright (c) 2013 Xcellent Creations, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /** + * Reads frame data from a GIF image source and decodes it into individual frames + * for animation purposes. Image data can be read from either and InputStream source + * or a byte[]. + * + * This class is optimized for running animations with the frames, there + * are no methods to get individual frame images, only to decode the next frame in the + * animation sequence. Instead, it lowers its memory footprint by only housing the minimum + * data necessary to decode the next frame in the animation sequence. + * + * The animation must be manually moved forward using {@link #advance()} before requesting the next + * frame. This method must also be called before you request the first frame or an error will + * occur. + * + * Implementation adapted from sample code published in Lyons. (2004). Java for Programmers, + * republished under the MIT Open Source License + */ + private static class GifDecoder { + private static final String TAG = "GifDecoder"; + + /** + * File read status: No errors. + */ + public static final int STATUS_OK = 0; + /** + * File read status: Error decoding file (may be partially decoded) + */ + public static final int STATUS_FORMAT_ERROR = 1; + /** + * File read status: Unable to open source. + */ + public static final int STATUS_OPEN_ERROR = 2; + /** + * max decoder pixel stack size + */ + protected static final int MAX_STACK_SIZE = 4096; + + /** + * GIF Disposal Method meaning take no action + */ + private static final int DISPOSAL_UNSPECIFIED = 0; + /** + * GIF Disposal Method meaning leave canvas from previous frame + */ + private static final int DISPOSAL_NONE = 1; + /** + * GIF Disposal Method meaning clear canvas to background color + */ + private static final int DISPOSAL_BACKGROUND = 2; + /** + * GIF Disposal Method meaning clear canvas to frame before last + */ + private static final int DISPOSAL_PREVIOUS = 3; + private static final int MIN_DELAY = 60; // 60 milliseconds + + /** + * Global status code of GIF data parsing + */ + protected int status; + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + //Global File Header values and parsing flags + protected int width; // full image width + protected int height; // full image height + protected boolean gctFlag; // global color table used + protected int gctSize; // size of global color table + protected int loopCount = 1; // iterations; 0 = repeat forever + protected int[] gct; // global color table + protected int[] act; // active color table + protected int bgIndex; // background color index + protected int bgColor; // background color + protected int pixelAspect; // pixel aspect ratio + protected boolean lctFlag; // local color table flag + protected int lctSize; // local color table size + + // Raw GIF data from input source + protected ByteBuffer rawData; + + // Raw data read working array + protected byte[] block = new byte[256]; // current data block + protected int blockSize = 0; // block size last graphic control extension info + + // LZW decoder working arrays + protected short[] prefix; + protected byte[] suffix; + protected byte[] pixelStack; + protected byte[] mainPixels; + protected int[] mainScratch, copyScratch; + + protected ArrayList frames; // frames read from current file + protected GifFrame currentFrame; + protected Bitmap previousImage, currentImage; + + public int getFramePointer() { + return framePointer; + } + + protected int framePointer; + protected int frameCount; + + public void reset() { + framePointer = -1; + } + + /** + * Inner model class housing metadata for each frame + */ + private static class GifFrame { + public int ix, iy, iw, ih; + /* Control Flags */ + public boolean interlace; + public boolean transparency; + /* Disposal Method */ + public int dispose; + /* Transparency Index */ + public int transIndex; + /* Delay, in ms, to next frame */ + public int delay; + /* Index in the raw buffer where we need to start reading to decode */ + public int bufferFrameStart; + /* Local Color Table */ + public int[] lct; + } + + /** + * Move the animation frame counter forward + */ + public void advance() { + framePointer = (framePointer + 1) % frameCount; + } + + /** + * Gets display duration for specified frame. + * + * @param n int index of frame + * @return delay in milliseconds + */ + public int getDelay(int n) { + int delay = -1; + if ((n >= 0) && (n < frameCount)) { + delay = frames.get(n).delay; + } + return delay; + } + + /** + * Gets display duration for the upcoming frame + */ + public int getNextDelay() { + if (frameCount <= 0 || framePointer < 0) { + return -1; + } + + return getDelay(framePointer); + } + + /** + * Gets the number of frames read from file. + * + * @return frame count + */ + public int getFrameCount() { + return frameCount; + } + + /** + * Gets the current index of the animation frame, or -1 if animation hasn't not yet started + * + * @return frame index + */ + public int getCurrentFrameIndex() { + return framePointer; + } + + /** + * Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitiely. + * + * @return iteration count if one was specified, else 1. + */ + public int getLoopCount() { + return loopCount; + } + + /** + * + */ + public Bitmap getFirstFrame() { + if (frameCount <= 0) { + return null; + } + framePointer = 0; + Bitmap ret = getNextFrame(); + framePointer = -1; + return ret; + } + + /** + * Get the next frame in the animation sequence. + * + * @return Bitmap representation of frame + */ + public Bitmap getNextFrame() { + if (frameCount <= 0 || framePointer < 0 || currentImage == null) { + return null; + } + + GifFrame frame = frames.get(framePointer); + + //Set the appropriate color table + if (frame.lct == null) { + act = gct; + } else { + act = frame.lct; + if (bgIndex == frame.transIndex) { + bgColor = 0; + } + } + + int save = 0; + if (frame.transparency) { + save = act[frame.transIndex]; + act[frame.transIndex] = 0; // set transparent color if specified + } + if (act == null) { + Log.w(TAG, "No Valid Color Table"); + status = STATUS_FORMAT_ERROR; // no color table defined + return null; + } + + setPixels(framePointer); // transfer pixel data to image + + // Reset the transparent pixel in the color table + if (frame.transparency) { + act[frame.transIndex] = save; + } + + return currentImage; + } + + /** + * Reads GIF image from stream + * + * @param is containing GIF file. + * @return read status code (0 = no errors) + */ + public int read(InputStream is, int contentLength) { + if (is != null) { + Log.v(TAG, "read start"); + try { + int capacity = (contentLength > 0) ? (contentLength + 4096) : 4096; + ByteArrayOutputStream buffer = new ByteArrayOutputStream(capacity); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + + Log.v(TAG, "buffer ready"); + read(buffer.toByteArray()); + } catch (IOException e) { + Log.w(TAG, "Error reading data from stream", e); + } finally { + try { + is.close(); + } catch (Exception e) { + Log.w(TAG, "Error closing stream", e); + } + } + } else { + status = STATUS_OPEN_ERROR; + } + Log.v(TAG, "read2 finished"); + return status; + } + + /** + * Reads GIF image from byte array + * + * @param data containing GIF file. + * @return read status code (0 = no errors) + */ + public int read(byte[] data) { + init(); + if (data != null) { + //Initiliaze the raw data buffer + rawData = ByteBuffer.wrap(data); + rawData.rewind(); + rawData.order(ByteOrder.LITTLE_ENDIAN); + Log.v(TAG, "read Header start"); + readHeader(); + if (!err()) { + Log.v(TAG, "read Contents start"); + readContents(); + if (frameCount < 0) { + status = STATUS_FORMAT_ERROR; + } + } + } else { + status = STATUS_OPEN_ERROR; + } + Log.v(TAG, "read finished"); + + return status; + } + + /** + * Creates new frame image from current data (and previous frames as specified by their disposition codes). + */ + protected void setPixels(int frameIndex) { + GifFrame currentFrame = frames.get(frameIndex); + GifFrame previousFrame = null; + int previousIndex = frameIndex - 1; + if (previousIndex >= 0) { + previousFrame = frames.get(previousIndex); + } + + // final location of blended pixels + final int[] dest = mainScratch; + + // fill in starting image contents based on last image's dispose code + if (previousFrame != null && previousFrame.dispose > DISPOSAL_UNSPECIFIED) { + if (previousFrame.dispose == DISPOSAL_NONE && currentImage != null) { + // Start with the current image + currentImage.getPixels(dest, 0, width, 0, 0, width, height); + } + if (previousFrame.dispose == DISPOSAL_BACKGROUND) { + // Start with a canvas filled with the background color + int c = 0; + if (!currentFrame.transparency) { + c = bgColor; + } + for (int i = 0; i < previousFrame.ih; i++) { + int n1 = (previousFrame.iy + i) * width + previousFrame.ix; + int n2 = n1 + previousFrame.iw; + for (int k = n1; k < n2; k++) { + dest[k] = c; + } + } + } + if (previousFrame.dispose == DISPOSAL_PREVIOUS && previousImage != null) { + // Start with the previous frame + previousImage.getPixels(dest, 0, width, 0, 0, width, height); + } + } + + //Decode pixels for this frame into the global pixels[] scratch + decodeBitmapData(currentFrame, mainPixels); // decode pixel data + + // copy each source line to the appropriate place in the destination + int pass = 1; + int inc = 8; + int iline = 0; + for (int i = 0; i < currentFrame.ih; i++) { + int line = i; + if (currentFrame.interlace) { + if (iline >= currentFrame.ih) { + pass++; + switch (pass) { + case 2: + iline = 4; + break; + case 3: + iline = 2; + inc = 4; + break; + case 4: + iline = 1; + inc = 2; + break; + default: + break; + } + } + line = iline; + iline += inc; + } + line += currentFrame.iy; + if (line < height) { + int k = line * width; + int dx = k + currentFrame.ix; // start of line in dest + int dlim = dx + currentFrame.iw; // end of dest line + if ((k + width) < dlim) { + dlim = k + width; // past dest edge + } + int sx = i * currentFrame.iw; // start of line in source + while (dx < dlim) { + // map color and insert in destination + int index = ((int)mainPixels[sx++]) & 0xff; + int c = act[index]; + if (c != 0) { + dest[dx] = c; + } + dx++; + } + } + } + + //Copy pixels into previous image + currentImage.getPixels(copyScratch, 0, width, 0, 0, width, height); + previousImage.setPixels(copyScratch, 0, width, 0, 0, width, height); + //Set pixels for current image + currentImage.setPixels(dest, 0, width, 0, 0, width, height); + } + + /** + * Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick. + */ + protected void decodeBitmapData(GifFrame frame, byte[] dstPixels) { + if (frame != null) { + //Jump to the frame start position + rawData.position(frame.bufferFrameStart); + } + + int nullCode = -1; + int npix = (frame == null) ? width * height : frame.iw * frame.ih; + int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, + datum, data_size, first, top, bi, pi; + + if (dstPixels == null || dstPixels.length < npix) { + dstPixels = new byte[npix]; // allocate new pixel array + } + if (prefix == null) { + prefix = new short[MAX_STACK_SIZE]; + } + if (suffix == null) { + suffix = new byte[MAX_STACK_SIZE]; + } + if (pixelStack == null) { + pixelStack = new byte[MAX_STACK_SIZE + 1]; + } + + // Initialize GIF data stream decoder. + data_size = read(); + clear = 1 << data_size; + end_of_information = clear + 1; + available = clear + 2; + old_code = nullCode; + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + for (code = 0; code < clear; code++) { + prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException + suffix[code] = (byte)code; + } + + // Decode GIF pixel stream. + datum = bits = count = first = top = pi = bi = 0; + for (i = 0; i < npix; ) { + if (top == 0) { + if (bits < code_size) { + // Load bytes until there are enough bits for a code. + if (count == 0) { + // Read a new data block. + count = readBlock(); + if (count <= 0) { + break; + } + bi = 0; + } + datum += (((int)block[bi]) & 0xff) << bits; + bits += 8; + bi++; + count--; + continue; + } + // Get the next code. + code = datum & code_mask; + datum >>= code_size; + bits -= code_size; + // Interpret the code + if ((code > available) || (code == end_of_information)) { + break; + } + if (code == clear) { + // Reset decoder. + code_size = data_size + 1; + code_mask = (1 << code_size) - 1; + available = clear + 2; + old_code = nullCode; + continue; + } + if (old_code == nullCode) { + pixelStack[top++] = suffix[code]; + old_code = code; + first = code; + continue; + } + in_code = code; + if (code == available) { + pixelStack[top++] = (byte)first; + code = old_code; + } + while (code > clear) { + pixelStack[top++] = suffix[code]; + code = prefix[code]; + } + first = ((int)suffix[code]) & 0xff; + // Add a new string to the string table, + if (available >= MAX_STACK_SIZE) { + break; + } + pixelStack[top++] = (byte)first; + prefix[available] = (short)old_code; + suffix[available] = (byte)first; + available++; + if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) { + code_size++; + code_mask += available; + } + old_code = in_code; + } + // Pop a pixel off the pixel stack. + top--; + dstPixels[pi++] = pixelStack[top]; + i++; + } + + for (i = pi; i < npix; i++) { + dstPixels[i] = 0; // clear missing pixels + } + } + + /** + * Returns true if an error was encountered during reading/decoding + */ + protected boolean err() { + return status != STATUS_OK; + } + + /** + * Initializes or re-initializes reader + */ + protected void init() { + status = STATUS_OK; + frameCount = 0; + frames = new ArrayList(); + reset(); + gct = null; + } + + /** + * Reads a single byte from the input stream. + */ + protected int read() { + int curByte = 0; + try { + curByte = (rawData.get() & 0xFF); + } catch (Exception e) { + status = STATUS_FORMAT_ERROR; + } + return curByte; + } + + /** + * Reads next variable length block from input. + * + * @return number of bytes stored in "buffer" + */ + protected int readBlock() { + blockSize = read(); + int n = 0; + if (blockSize > 0) { + try { + int count; + while (n < blockSize) { + count = blockSize - n; + rawData.get(block, n, count); + + n += count; + } + } catch (Exception e) { + Log.w(TAG, "Error Reading Block", e); + status = STATUS_FORMAT_ERROR; + } + } + return n; + } + + /** + * Reads color table as 256 RGB integer values + * + * @param ncolors int number of colors to read + * @return int array containing 256 colors (packed ARGB with full alpha) + */ + protected int[] readColorTable(int ncolors) { + int nbytes = 3 * ncolors; + int[] tab = null; + byte[] c = new byte[nbytes]; + + try { + rawData.get(c); + + tab = new int[256]; // max size to avoid bounds checks + int i = 0; + int j = 0; + while (i < ncolors) { + int r = ((int)c[j++]) & 0xff; + int g = ((int)c[j++]) & 0xff; + int b = ((int)c[j++]) & 0xff; + tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; + } + } catch (BufferUnderflowException e) { + Log.w(TAG, "Format Error Reading Color Table", e); + status = STATUS_FORMAT_ERROR; + } + + return tab; + } + + /** + * Main file parser. Reads GIF content blocks. + */ + protected void readContents() { + // read GIF file content blocks + boolean done = false; + while (!(done || err())) { + int code = read(); + switch (code) { + case 0x2C: // image separator + readBitmap(); + break; + case 0x21: // extension + code = read(); + switch (code) { + case 0xf9: // graphics control extension + //Start a new frame + currentFrame = new GifFrame(); + readGraphicControlExt(); + break; + case 0xff: // application extension + readBlock(); + String app = ""; + for (int i = 0; i < 11; i++) { + app += (char)block[i]; + } + if (("NETSCAPE2.0").equals(app)) { + readNetscapeExt(); + } else { + skip(); // don't care + } + break; + case 0xfe:// comment extension + skip(); + break; + case 0x01:// plain text extension + skip(); + break; + default: // uninteresting extension + skip(); + } + break; + case 0x3b: // terminator + done = true; + break; + case 0x00: // bad byte, but keep going and see what happens break; + default: + status = STATUS_FORMAT_ERROR; + } + } + } + + /** + * Reads GIF file header information. + */ + protected void readHeader() { + String id = ""; + for (int i = 0; i < 6; i++) { + id += (char)read(); + } + if (!id.startsWith("GIF")) { + status = STATUS_FORMAT_ERROR; + return; + } + readLSD(); + if (gctFlag && !err()) { + gct = readColorTable(gctSize); + bgColor = gct[bgIndex]; + } + } + + /** + * Reads Graphics Control Extension values + */ + protected void readGraphicControlExt() { + read(); // block size + int packed = read(); // packed fields + currentFrame.dispose = (packed & 0x1c) >> 2; // disposal method + if (currentFrame.dispose == 0) { + currentFrame.dispose = 1; // elect to keep old image if discretionary + } + currentFrame.transparency = (packed & 1) != 0; + currentFrame.delay = Math.max(MIN_DELAY, readShort() * 10); // delay in milliseconds + currentFrame.transIndex = read(); // transparent color index + read(); // block terminator + } + + /** + * Reads next frame image + */ + protected void readBitmap() { + currentFrame.ix = readShort(); // (sub)image position & size + currentFrame.iy = readShort(); + currentFrame.iw = readShort(); + currentFrame.ih = readShort(); + + int packed = read(); + lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace + lctSize = (int) Math.pow(2, (packed & 0x07) + 1); + // 3 - sort flag + // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color + // table size + currentFrame.interlace = (packed & 0x40) != 0; + if (lctFlag) { + currentFrame.lct = readColorTable(lctSize); // read table + } else { + currentFrame.lct = null; //No local color table + } + + currentFrame.bufferFrameStart = rawData.position(); //Save this as the decoding position pointer + + decodeBitmapData(null, mainPixels); // false decode pixel data to advance buffer + skip(); + if (err()) { + return; + } + + frameCount++; + frames.add(currentFrame); // add image to frame + } + + /** + * Reads Logical Screen Descriptor + */ + protected void readLSD() { + // logical screen size + width = readShort(); + height = readShort(); + // packed fields + int packed = read(); + gctFlag = (packed & 0x80) != 0; // 1 : global color table flag + // 2-4 : color resolution + // 5 : gct sort flag + gctSize = 2 << (packed & 7); // 6-8 : gct size + bgIndex = read(); // background color index + pixelAspect = read(); // pixel aspect ratio + + //Now that we know the size, init scratch arrays + mainPixels = new byte[width * height]; + mainScratch = new int[width * height]; + copyScratch = new int[width * height]; + + previousImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + currentImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + } + + /** + * Reads Netscape extenstion to obtain iteration count + */ + protected void readNetscapeExt() { + do { + readBlock(); + if (block[0] == 1) { + // loop count sub-block + int b1 = ((int)block[1]) & 0xff; + int b2 = ((int)block[2]) & 0xff; + loopCount = (b2 << 8) | b1; + } + } while ((blockSize > 0) && !err()); + } + + /** + * Reads next 16-bit value, LSB first + */ + protected int readShort() { + // read 16-bit value + return rawData.getShort(); + } + + /** + * Skips variable length blocks up to and including next zero length block. + */ + protected void skip() { + do { + readBlock(); + } while ((blockSize > 0) && !err()); + } + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/LoginParams.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/LoginParams.java new file mode 100644 index 0000000..be6a765 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/LoginParams.java @@ -0,0 +1,62 @@ +package com.sean.rao.ali_auth.common; + +import android.app.Activity; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + +import com.alibaba.fastjson2.JSONObject; +import com.mobile.auth.gatewayauth.AuthUIConfig; +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import com.mobile.auth.gatewayauth.TokenResultListener; +import com.sean.rao.ali_auth.config.BaseUIConfig; +import com.sean.rao.ali_auth.utils.StatusAll; +import com.sean.rao.ali_auth.utils.UtilTool; + +import org.jetbrains.annotations.Nullable; + +import io.flutter.plugin.common.EventChannel; + +public class LoginParams { + public static Activity mActivity; + + public static Context mContext; + + public static BaseUIConfig mUIConfig; + + public static JSONObject jsonObject; + + public static AuthUIConfig.Builder config; + public static boolean sdkAvailable = true; + public static EventChannel.EventSink eventSink; + public static TokenResultListener mTokenResultListener; + public static Boolean isChecked; + + public static PhoneNumberAuthHelper mAuthHelper; + + public void showResult(String code, String message, Object data){ + if (eventSink != null) { + JSONObject result = resultFormatData(code, message, data); + result.put("isChecked", isChecked); + eventSink.success(result); + } + } + + /** + * 返回数据封装 + * @param code + * @param msg + * @param jsonDataObj + * @return + */ + public static JSONObject resultFormatData(String code, @Nullable String msg, @Nullable Object jsonDataObj){ + JSONObject jsonObj = new JSONObject(); + jsonObj.put("code", code); + jsonObj.put("msg", msg != null && !msg.isEmpty() ? msg : StatusAll.getName(code)); + jsonObj.put("data", jsonDataObj != null ? jsonDataObj : ""); + jsonObj.put("isChecked", jsonObject.getBooleanValue("privacyState", false)); + return jsonObj; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/MediaFileUtil.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/MediaFileUtil.java new file mode 100644 index 0000000..dee3fb5 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/MediaFileUtil.java @@ -0,0 +1,207 @@ +package com.sean.rao.ali_auth.common; + +/** + * @ProjectName: android + * @Package: com.sean.rao.ali_auth.common + * @ClassName: MediaFileUtil + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 5/1/22 10:33 AM + * @UpdateUser: 更新者 + * @UpdateDate: 5/1/22 10:33 AM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +import java.util.HashMap; +import java.util.Iterator; + + +/** + * 判断文件的类型 视频 音频 图片 + */ +public class MediaFileUtil { + public static String sFileExtensions; + + // Audio + public static final int FILE_TYPE_MP3 = 1; + public static final int FILE_TYPE_M4A = 2; + public static final int FILE_TYPE_WAV = 3; + public static final int FILE_TYPE_AMR = 4; + public static final int FILE_TYPE_AWB = 5; + public static final int FILE_TYPE_WMA = 6; + public static final int FILE_TYPE_OGG = 7; + private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; + private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_OGG; + + // MIDI + public static final int FILE_TYPE_MID = 11; + public static final int FILE_TYPE_SMF = 12; + public static final int FILE_TYPE_IMY = 13; + private static final int FIRST_MIDI_FILE_TYPE = FILE_TYPE_MID; + private static final int LAST_MIDI_FILE_TYPE = FILE_TYPE_IMY; + + // Video + public static final int FILE_TYPE_MP4 = 21; + public static final int FILE_TYPE_M4V = 22; + public static final int FILE_TYPE_3GPP = 23; + public static final int FILE_TYPE_3GPP2 = 24; + public static final int FILE_TYPE_WMV = 25; + private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4; + private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WMV; + + // Image + public static final int FILE_TYPE_JPEG = 31; + public static final int FILE_TYPE_GIF = 32; + public static final int FILE_TYPE_PNG = 33; + public static final int FILE_TYPE_BMP = 34; + public static final int FILE_TYPE_WBMP = 35; + private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG; + private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WBMP; + + // Playlist + public static final int FILE_TYPE_M3U = 41; + public static final int FILE_TYPE_PLS = 42; + public static final int FILE_TYPE_WPL = 43; + private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; + private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL; + + //静态内部类 + public static class MediaFileType { + + int fileType; + String mimeType; + + MediaFileType(int fileType, String mimeType) { + this.fileType = fileType; + this.mimeType = mimeType; + } + } + + private static HashMap sFileTypeMap + = new HashMap(); + private static HashMap sMimeTypeMap + = new HashMap(); + static void addFileType(String extension, int fileType, String mimeType) { + sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType)); + sMimeTypeMap.put(mimeType, new Integer(fileType)); + } + static { + addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg"); + addFileType("M4A", FILE_TYPE_M4A, "audio/mp4"); + addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav"); + addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); + addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); + addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma"); + addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + + addFileType("MID", FILE_TYPE_MID, "audio/midi"); + addFileType("XMF", FILE_TYPE_MID, "audio/midi"); + addFileType("RTTTL", FILE_TYPE_MID, "audio/midi"); + addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi"); + addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); + + addFileType("MP4", FILE_TYPE_MP4, "video/mp4"); + addFileType("M4V", FILE_TYPE_M4V, "video/mp4"); + addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp"); + addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp"); + addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2"); + addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2"); + addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv"); + + addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg"); + addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg"); + addFileType("GIF", FILE_TYPE_GIF, "image/gif"); + addFileType("PNG", FILE_TYPE_PNG, "image/png"); + addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp"); + addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp"); + + addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl"); + addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls"); + addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl"); + + // compute file extensions list for native Media Scanner + StringBuilder builder = new StringBuilder(); + Iterator iterator = sFileTypeMap.keySet().iterator(); + + while (iterator.hasNext()) { + if (builder.length() > 0) { + builder.append(','); + } + builder.append(iterator.next()); + } + sFileExtensions = builder.toString(); + } + + public static final String UNKNOWN_STRING = ""; + + public static boolean isAudioFileType(int fileType) { + return ((fileType >= FIRST_AUDIO_FILE_TYPE && + fileType <= LAST_AUDIO_FILE_TYPE) || + (fileType >= FIRST_MIDI_FILE_TYPE && + fileType <= LAST_MIDI_FILE_TYPE)); + } + + public static boolean isVideoFileType(int fileType) { + return (fileType >= FIRST_VIDEO_FILE_TYPE && + fileType <= LAST_VIDEO_FILE_TYPE); + } + + public static boolean isImageFileType(int fileType) { + return (fileType >= FIRST_IMAGE_FILE_TYPE && + fileType <= LAST_IMAGE_FILE_TYPE); + } + + public static boolean isPlayListFileType(int fileType) { + return (fileType >= FIRST_PLAYLIST_FILE_TYPE && + fileType <= LAST_PLAYLIST_FILE_TYPE); + } + + public static MediaFileType getFileType(String path) { + int lastDot = path.lastIndexOf("."); + if (lastDot < 0) + return null; + return sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase()); + } + + //根据视频文件路径判断文件类型 + public static boolean isVideoFileType(String path) { + MediaFileType type = getFileType(path); + if(null != type) { + return isVideoFileType(type.fileType); + } + return false; + } + + //根据音频文件路径判断文件类型 + public static boolean isAudioFileType(String path) { + MediaFileType type = getFileType(path); + if(null != type) { + return isAudioFileType(type.fileType); + } + return false; + } + + //根据mime类型查看文件类型 + public static int getFileTypeForMimeType(String mimeType) { + Integer value = sMimeTypeMap.get(mimeType); + return (value == null ? 0 : value.intValue()); + } + + //根据图片文件路径判断是否时gif文件类型 + public static boolean isImageGifFileType(String path) { + MediaFileType type = getFileType(path); + if(null != type) { + return FILE_TYPE_GIF == type.fileType; + } + return false; + } + + //根据图片文件路径判断文件类型 + public static boolean isImageFileType(String path) { + MediaFileType type = getFileType(path); + if(null != type) { + return isImageFileType(type.fileType); + } + return false; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/NativeBackgroundAdapter.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/NativeBackgroundAdapter.java new file mode 100644 index 0000000..d3c901a --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/NativeBackgroundAdapter.java @@ -0,0 +1,584 @@ +package com.sean.rao.ali_auth.common; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Application; +import android.app.Application.ActivityLifecycleCallbacks; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Movie; +import android.graphics.SurfaceTexture; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.AudioManager; +import android.media.MediaMetadataRetriever; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnInfoListener; +import android.media.MediaPlayer.OnPreparedListener; +import android.media.MediaPlayer.OnVideoSizeChangedListener; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; + +import com.alibaba.fastjson2.JSONObject; +import com.mobile.auth.gatewayauth.LoginAuthActivity; +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import com.mobile.auth.gatewayauth.model.TokenRet; +import com.sean.rao.ali_auth.common.CacheManage; +import com.sean.rao.ali_auth.common.GifAnimationDrawable; +import com.sean.rao.ali_auth.utils.AppUtils; +import com.sean.rao.ali_auth.utils.UtilTool; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.concurrent.ExecutorService; + +import io.flutter.plugin.common.EventChannel; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +/** + * @Package: com.aliqin.mytel + * @ClassName: NativeBackgroundAdapter + * @Description: 类作用描述 + * @Author: liuqi + * @CreateDate: 2021/4/29 4:53 PM + * @Version: 1.0 + */ +public class NativeBackgroundAdapter extends LoginParams{ + private volatile OnGifListener onGifListener; + private volatile GifAnimationDrawable gifAnimationDrawable; + private CacheManage mCacheManage; + private ExecutorService mExecutorService; + private String key, path; + + public NativeBackgroundAdapter(CacheManage cacheManage, ExecutorService executorService, String key) { + this.key = key; + this.path = jsonObject.getString("pageBackgroundPath"); + mCacheManage = cacheManage; + mExecutorService = executorService; + mCacheManage.checkAndCreateBitmapCache(); + + if ("gifPath".equals(key)) { + if (null == mCacheManage.getBitmap(path)) { + getAssetGifFirstFrame(mContext, path); + } + } + + if ("videoPath".equals(key)) { + if (null == mCacheManage.getBitmap(path)) { + mExecutorService.execute(new Runnable() { + @Override + public void run() { + Bitmap bitmap = getAssetVideoCoverBitmap(mContext.getAssets(), path); + if(null!=bitmap) { + mCacheManage.cacheBitmap(path, bitmap); + } + } + }); + } + + } + } + + /** + * 加载布局 + * + * @param frameLayout 容器 + * @param backgroundColor 背景颜色 + */ + public void solveView(final FrameLayout frameLayout, String backgroundColor) { + if ("imagePath".equals(key)) { + ImageView imageView = new ImageView(frameLayout.getContext()); + LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT); + imageView.setScaleType(ScaleType.CENTER_CROP); + imageView.setImageDrawable(toDrawable(path, frameLayout.getContext())); + frameLayout.addView(imageView, params); + if (!TextUtils.isEmpty(backgroundColor)) { + imageView.setBackgroundColor(Color.parseColor(backgroundColor)); + } + } else if ("gifPath".equals(key)) { + final ImageView imageView = new ImageView(frameLayout.getContext()); + LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT); + imageView.setScaleType(ScaleType.CENTER_CROP); + if (!TextUtils.isEmpty(backgroundColor)) { + imageView.setBackgroundColor(Color.parseColor(backgroundColor)); + } + frameLayout.addView(imageView, params); + if (gifAnimationDrawable != null) { + playGif(imageView, gifAnimationDrawable); + } else { + if (!TextUtils.isEmpty(backgroundColor)) { + imageView.setBackgroundColor(Color.parseColor(backgroundColor)); + } + onGifListener = new OnGifListener() { + @Override + public void onGifDownloaded(final GifAnimationDrawable drawable) { + if (drawable != null && null != imageView) { + imageView.post(new Runnable() { + @Override + public void run() { + gifAnimationDrawable = drawable; + playGif(imageView, drawable); + } + }); + } + } + + @Override + public void onFirstFrame(final Bitmap bitmap) { + imageView.post(new Runnable() { + @Override + public void run() { + if (null == gifAnimationDrawable) { + imageView.setImageBitmap(bitmap); + } + } + }); + + } + }; + if (null != mCacheManage.getBitmap(path)) { + imageView.setImageBitmap(mCacheManage.getBitmap(path)); + } + readGifAsset(frameLayout.getContext(), path); + } + } else if ("videoPath".equals(key)) { + playVideo(frameLayout, path, backgroundColor); + } + + // 构建关闭按钮 + createImageButton(frameLayout, path); + } + + /** + * 获取asset文件夹视频封面 + * + * @param assetManager + * @param videoPath + * @return + */ + public static Bitmap getAssetVideoCoverBitmap(AssetManager assetManager, String videoPath) { + Bitmap bitmap = null; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + try { + AssetFileDescriptor afd = assetManager.openFd(videoPath); + retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + //获得第一帧图片 + bitmap = retriever.getFrameAtTime(); + try { + retriever.release(); + } catch (RuntimeException ex) { + ex.printStackTrace(); + } + } catch (IllegalArgumentException | IOException ex) { + ex.printStackTrace(); + Log.e("NativeBackgroundAdapter", "getAssetVideoBitmap:" + ex.getMessage()); + } + return bitmap; + } + + /** + * 获取本地gif第一帧 + * + * @param path + */ + public void getAssetGifFirstFrame(final Context context, final String path) { + mExecutorService.execute(new Runnable() { + @Override + public void run() { + try { + InputStream inputStream = context.getAssets().open(path); + // InputStream inputStream = DownloadFile.getImageToInputStream(path); + Movie gif = Movie.decodeStream(inputStream); + final Bitmap firstFrame = Bitmap.createBitmap(gif.width(), gif.height(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(firstFrame); + gif.draw(canvas, 0, 0); + canvas.save(); + mCacheManage.cacheBitmap(path, firstFrame); + OnGifListener gifListener = onGifListener; + if (gifListener != null) { + gifListener.onFirstFrame(firstFrame); + } + inputStream.close(); + } catch (IOException e) { + Log.e("NativeBackgroundAdapter", "getGifFirstFrame:" + e.getMessage()); + } + } + }); + } + + /** + * 播放视频 + * + * @param frameLayout + * @param path url或者路径 + * @param backgroundColor + */ + @TargetApi(VERSION_CODES.JELLY_BEAN) + private void playVideo(final FrameLayout frameLayout, final String path, String backgroundColor) { + final MediaPlayer[] mediaPlayer = {new MediaPlayer()}; + final TextureView textureView = new TextureView(frameLayout.getContext()); + LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT); + frameLayout.addView(textureView, params); + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + textureView.setBackground(null); + } + final ImageView ivCover = new ImageView(frameLayout.getContext()); + LayoutParams paramsIv = new LayoutParams(MATCH_PARENT, MATCH_PARENT); + ivCover.setScaleType(ScaleType.CENTER_CROP); + if (null != mCacheManage.getBitmap(path)) { + ivCover.setImageBitmap(mCacheManage.getBitmap(path)); + } else { + if (!TextUtils.isEmpty(backgroundColor)) { + frameLayout.setBackgroundColor(Color.parseColor(backgroundColor)); + } + } + frameLayout.addView(ivCover, paramsIv); + final ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + @Override + public void onActivityStarted(Activity activity) { + + } + @Override + public void onActivityResumed(Activity activity) { + if (activity instanceof LoginAuthActivity) { + if (mediaPlayer[0] != null && !mediaPlayer[0].isPlaying()) { + mediaPlayer[0].start(); + } + } + } + @Override + public void onActivityPaused(Activity activity) { + if (activity instanceof LoginAuthActivity) { + if (mediaPlayer[0] != null && mediaPlayer[0].isPlaying()) { + mediaPlayer[0].pause(); + } + } + } + @Override + public void onActivityStopped(Activity activity) { + } + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + @Override + public void onActivityDestroyed(Activity activity) { + if (activity instanceof LoginAuthActivity) { + if (mediaPlayer[0] != null && mediaPlayer[0].isPlaying()) { + mediaPlayer[0].stop(); + mediaPlayer[0].release(); + mediaPlayer[0] = null; + } + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + } + } + }; + ((Application)frameLayout.getContext()).registerActivityLifecycleCallbacks( + activityLifecycleCallbacks); + mediaPlayer[0].setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer[0].setLooping(true); + mediaPlayer[0].setVolume(0, 0); + AssetFileDescriptor afd; + try { + afd = frameLayout.getContext().getAssets().openFd(path); + mediaPlayer[0].setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), + afd.getLength()); + } catch (Exception e) { + Log.e("NativeBackgroundAdapter", "playVideo===readAssets:" + e.getMessage()); + } + + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + mediaPlayer[0].setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); + } + try { + mediaPlayer[0].setOnPreparedListener(new OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + mediaPlayer[0].setOnInfoListener(new OnInfoListener() { + @Override + public boolean onInfo(MediaPlayer mp, int what, int extra) { + if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { + + return true; + } else if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { + ivCover.setVisibility(View.GONE); + } + return false; + } + }); + mediaPlayer[0].start(); + } + }); + mediaPlayer[0].setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() { + @Override + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { + int mVideoHeight = mediaPlayer[0].getVideoHeight(); + int mVideoWidth = mediaPlayer[0].getVideoWidth(); + updateTextureViewSizeCenter(textureView, mVideoWidth, mVideoHeight); + } + }); + mediaPlayer[0].prepareAsync(); + textureView.setSurfaceTextureListener(new SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, + int height) { + Surface mSurface = new Surface(surface); + mediaPlayer[0].setSurface(mSurface); + } + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, + int height) { + + } + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + if (mediaPlayer[0] != null && mediaPlayer[0].isPlaying()) { + mediaPlayer[0].pause(); + } + return false; + } + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { + + } + }); + } catch (Exception ignored) { + + } + } + + /** + * 设置视频适应模式为center_crop + * + * @param textureView + * @param mVideoWidth + * @param mVideoHeight + */ + private void updateTextureViewSizeCenter(TextureView textureView, int mVideoWidth, int mVideoHeight) { + + float sx = (float)textureView.getWidth() / (float)mVideoWidth; + float sy = (float)textureView.getHeight() / (float)mVideoHeight; + + Matrix matrix = new Matrix(); + float maxScale = Math.max(sx, sy); + + //第1步:把视频区移动到View区,使两者中心点重合. + matrix.preTranslate((textureView.getWidth() - mVideoWidth) / 2, (textureView.getHeight() - mVideoHeight) / 2); + + //第2步:因为默认视频是fitXY的形式显示的,所以首先要缩放还原回来. + matrix.preScale(mVideoWidth / (float)textureView.getWidth(), mVideoHeight / (float)textureView.getHeight()); + + //第3步,等比例放大或缩小,直到视频区的一边超过View一边, 另一边与View的另一边相等. 因为超过的部分超出了View的范围,所以是不会显示的,相当于裁剪了. + matrix.postScale(maxScale, maxScale, textureView.getWidth() / 2, + textureView.getHeight() / 2);//后两个参数坐标是以整个View的坐标系以参考的 + textureView.setTransform(matrix); + textureView.postInvalidate(); + } + + /** + * 从本地读取gif + * + * @param path + */ + private void readGifAsset(final Context context, final String path) { + mExecutorService.execute(new Runnable() { + @Override + public void run() { + AssetManager assetManager = context.getAssets(); + try { + InputStream inputStream = assetManager.open( + path); + final GifAnimationDrawable gifAnimationDrawable = new GifAnimationDrawable(inputStream); + if (onGifListener != null) { + onGifListener.onGifDownloaded(gifAnimationDrawable); + } + } catch (IOException e) { + Log.e("NativeBackgroundAdapter", "read gif asset:" + e.getMessage()); + } + } + }); + } + + /** + * 播放gif动画 + * + * @param imageView + * @param gifAnimationDrawable + */ + private void playGif(final ImageView imageView, final GifAnimationDrawable gifAnimationDrawable) { + Log.e("NativeBackgroundAdapter", "playGif asset"); + final ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + if (activity instanceof LoginAuthActivity) { + if (gifAnimationDrawable != null && !gifAnimationDrawable.isRunning()) { + imageView.postDelayed(new Runnable() { + @Override + public void run() { + gifAnimationDrawable.start(); + } + }, 50); + + } + } + } + + @Override + public void onActivityPaused(Activity activity) { + if (activity instanceof LoginAuthActivity) { + if (gifAnimationDrawable != null && gifAnimationDrawable.isRunning()) { + gifAnimationDrawable.stop(); + } + } + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + if (activity instanceof LoginAuthActivity) { + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + } + + } + }; + ((Application)imageView.getContext()).registerActivityLifecycleCallbacks( + activityLifecycleCallbacks); + imageView.setImageDrawable(gifAnimationDrawable); + gifAnimationDrawable.start(); + } + + public interface OnGifListener { + /** + * gif下载完成监听 + * + * @param drawable + */ + void onGifDownloaded(GifAnimationDrawable drawable); + + /** + * 获取第一帧完成监听 + * + * @param bitmap + */ + void onFirstFrame(Bitmap bitmap); + } + + /** + * 创建图片按钮 + * @param frameLayout + * @param path + * @return + */ + protected void createImageButton(final FrameLayout frameLayout, final String path){ + JSONObject customRetureBtn = jsonObject.getJSONObject("customReturnBtn"); + if (customRetureBtn != null) { + try{ + LinearLayout linearLayout = new LinearLayout(frameLayout.getContext()); + /// 是否留出状态栏的高度 + // linearLayout.setFitsSystemWindows(false); + LayoutParams linearLayoutParams = new LayoutParams(MATCH_PARENT, WRAP_CONTENT); + ImageButton imageButton = new ImageButton(frameLayout.getContext()); + imageButton.setPadding(0, 0, 0, 0); + imageButton.setBackgroundColor(Color.TRANSPARENT); + imageButton.setScaleType(ScaleType.values()[customRetureBtn.getIntValue("imgScaleType")]); + imageButton.setImageDrawable(UtilTool.getBitmapToBitmapDrawable(frameLayout.getContext(), UtilTool.flutterToPath(customRetureBtn.getString("imgPath")))); + LayoutParams buttonParams = new LayoutParams( + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("width")), + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("height")) + ); + buttonParams.setMargins( + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("left")), + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("top")), + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("right")), + AppUtils.dp2px(frameLayout.getContext(), customRetureBtn.getIntValue("bottom")) + ); + imageButton.setOnClickListener(v -> { + eventSink.success(UtilTool.resultFormatData("700000", null, null)); + mAuthHelper.quitLoginPage(); + }); + + linearLayout.addView(imageButton, buttonParams); + frameLayout.addView(linearLayout, linearLayoutParams); + } catch (IOException e) { + eventSink.success(UtilTool.resultFormatData("500000", null, e.getMessage())); + } + } + } + + protected static Drawable toDrawable(String imgUrl, Context context) { + Drawable drawable = null; + Bitmap bitmap = null; + InputStream inputStream = null; + try { + AssetManager assetManager = context.getAssets(); + inputStream = assetManager.open(imgUrl); + bitmap = BitmapFactory.decodeStream(inputStream); + drawable = new BitmapDrawable(context.getResources(), bitmap); + } catch (Exception var6) { + var6.printStackTrace(); + if (bitmap != null) { + bitmap.recycle(); + } + Log.e("AuthSDK", "e=" + var6.toString()); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return drawable; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/OnListener.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/OnListener.java new file mode 100644 index 0000000..e7463bc --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/common/OnListener.java @@ -0,0 +1,17 @@ +package com.sean.rao.ali_auth.common; + +/** + * @ProjectName: android + * @Package: com.sean.rao.ali_auth.common + * @ClassName: OnListener + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 6/13/22 4:21 PM + * @UpdateUser: 更新者 + * @UpdateDate: 6/13/22 4:21 PM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +public interface OnListener { + void CallBack(boolean status); +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/BaseUIConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/BaseUIConfig.java new file mode 100644 index 0000000..ed18760 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/BaseUIConfig.java @@ -0,0 +1,223 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.graphics.Color; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.Space; +import android.widget.TextView; + + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.sean.rao.ali_auth.common.Constant; + +import com.sean.rao.ali_auth.common.LoginParams; +import com.sean.rao.ali_auth.utils.AppUtils; +import com.sean.rao.ali_auth.utils.UtilTool; + +import java.io.IOException; + +import io.flutter.plugin.common.EventChannel; + +public abstract class BaseUIConfig extends LoginParams { + public int mScreenWidthDp; + public int mScreenHeightDp; + + public static BaseUIConfig init(int type) { + isChecked = false; + switch (type) { + case Constant.FULL_PORT: + return new FullPortConfig(); + case Constant.FULL_LAND: + return new FullLandConfig(); + case Constant.DIALOG_PORT: + return new DialogPortConfig(); + case Constant.DIALOG_LAND: + return new DialogLandConfig(); + case Constant.DIALOG_BOTTOM: + return new DialogBottomConfig(); + case Constant.CUSTOM_XML: + return new CustomXmlConfig(); + default: + if (jsonObject.getString("pageBackgroundPath") != null && !jsonObject.getString("pageBackgroundPath").isEmpty()) { + if (jsonObject.getString("pageBackgroundPath").equals("xml")) { + return new CustomXmlConfig(); + } else if (jsonObject.getString("pageBackgroundPath").equals("view")) { + return new CustomViewConfig(); + } else { + return new CustomAssetsConfig(); + } + } + return null; + } + } + + + /** + * 删除自定义布局参数,防止内存溢出 + */ + public BaseUIConfig() { + // 防止内存泄漏 + mAuthHelper.removeAuthRegisterXmlConfig(); + mAuthHelper.removeAuthRegisterViewConfig(); + } + + /** + * 第三方布局设置 + * @param marginTop + * @return + */ + protected View initSwitchView(int marginTop) { + JSONObject customThirdView = jsonObject.getJSONObject("customThirdView"); + /// 名称列表 + JSONArray customThirdViewName = customThirdView.getJSONArray("viewItemName"); + /// 图片路径列表 + JSONArray customThirdViewItem = customThirdView.getJSONArray("viewItemPath"); + if (customThirdViewName != null && customThirdViewItem != null) { + LinearLayout linearLayout = new LinearLayout(mContext); + // 创建一个最大宽度和适量高度的布局 + LinearLayout.LayoutParams LayoutParams = new LinearLayout.LayoutParams( + customThirdView.getFloatValue("width") > 0 ? AppUtils.dp2px(mContext, customThirdView.getFloatValue("width")) : LinearLayout.LayoutParams.MATCH_PARENT, + customThirdView.getFloatValue("height") > 0 ? AppUtils.dp2px(mContext, customThirdView.getFloatValue("height")) : LinearLayout.LayoutParams.WRAP_CONTENT + ); + + // 居中边距 + LayoutParams.setMargins( + AppUtils.dp2px(mContext, customThirdView.getFloatValue("left") > 0 ? customThirdView.getFloatValue("left") : 10), + AppUtils.dp2px(mContext, customThirdView.getFloatValue("top") > 0 ? customThirdView.getFloatValue("top") : marginTop), + AppUtils.dp2px(mContext, customThirdView.getFloatValue("right") > 0 ? customThirdView.getFloatValue("right") : 10), + AppUtils.dp2px(mContext, customThirdView.getFloatValue("bottom") > 0 ? customThirdView.getFloatValue("bottom") : 10) + ); + linearLayout.setLayoutParams(LayoutParams); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.setGravity(Gravity.CENTER_HORIZONTAL); + + for (int i = 0; i < customThirdViewItem.size(); i++) { + if (customThirdViewItem.get(i) != null && !String.valueOf(customThirdViewItem.get(i)).isEmpty()) { + int finalI = i; + /// 每个item布局 + LinearLayout itemLinearLayout = new LinearLayout(mContext); + /// 按钮和文字布局 + itemLinearLayout.setOrientation(LinearLayout.VERTICAL); + /// 按钮控件 + ImageButton itemButton = new ImageButton(mActivity); + /// 需要转化路径 + try { + itemButton.setBackground( + UtilTool.getBitmapToBitmapDrawable( + mContext, + UtilTool.flutterToPath(String.valueOf(customThirdViewItem.get(i))) + ) + ); + } catch (IOException e) { + // eventSink.success(UtilTool.resultFormatData("500000", null, e.getMessage())); + showResult("500000", "出现错误", e.getMessage()); + } + ViewGroup.LayoutParams buttonLayoutParams = new ViewGroup.LayoutParams( + AppUtils.dp2px(mContext, customThirdView.getFloatValue("itemWidth") > 0 ? customThirdView.getFloatValue("itemWidth") : 60), + AppUtils.dp2px(mContext, customThirdView.getFloatValue("itemHeight") > 0 ? customThirdView.getFloatValue("itemHeight") : 60) + ); + itemButton.setLayoutParams(buttonLayoutParams); + + /// 第三方按钮的点击事件 + itemButton.setOnClickListener(v -> { + // 判断是否隐藏toast + showResult("700005", "点击第三方登录按钮", finalI); + // eventSink.success(UtilTool.resultFormatData("600019", null, finalI)); +// if (!jsonObject.getBooleanValue("isHideToast") && !isChecked) { +// Toaster.show(jsonObject.getString("toastText")); +// return; +// } + if (jsonObject.getBooleanValue("autoQuitPage")) { + mAuthHelper.quitLoginPage(); + } + }); + itemLinearLayout.addView(itemButton); + + Object itemName = customThirdViewName.get(i); + if (itemName != null && !String.valueOf(itemName).isEmpty()) { + // 按钮下文字控件 + TextView textView = new TextView(mContext); + textView.setText(String.valueOf(itemName)); + // 文字颜色 + textView.setTextColor(customThirdView.getString("color") != null && !customThirdView.getString("color").isEmpty() ? Color.parseColor(customThirdView.getString("color")) : Color.BLACK); + textView.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + customThirdView.getFloatValue("size") > 0 ? customThirdView.getFloatValue("size") : 14F + ); + textView.setGravity(Gravity.CENTER); + itemLinearLayout.addView(textView); + } + + /// 新增按钮间距控件 + if (i > 0 && i < customThirdViewItem.size()) { + Space space = new Space(mContext); + space.setLayoutParams(new ViewGroup.LayoutParams( + AppUtils.dp2px(mContext, customThirdView.getFloatValue("space") > 0 ? customThirdView.getFloatValue("space") : 10), + LinearLayout.LayoutParams.MATCH_PARENT) + ); + linearLayout.addView(space); + } + /// 将item放入布局中 + linearLayout.addView(itemLinearLayout); + } + } + + return linearLayout; + } else { + return null; + } + } + + /** + * 更新屏幕 + * @param authPageScreenOrientation + */ + protected void updateScreenSize(int authPageScreenOrientation) { + int screenHeightDp = AppUtils.px2dp(mContext, AppUtils.getPhoneHeightPixels(mContext)); + int screenWidthDp = AppUtils.px2dp(mContext, AppUtils.getPhoneWidthPixels(mContext)); + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + if (authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + authPageScreenOrientation = mActivity.getRequestedOrientation(); + } + if (authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + || authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + || authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE) { + rotation = Surface.ROTATION_90; + } else if (authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + || authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + || authPageScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT) { + rotation = Surface.ROTATION_180; + } + switch (rotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_180: + mScreenWidthDp = screenWidthDp; + mScreenHeightDp = screenHeightDp; + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + mScreenWidthDp = screenHeightDp; + mScreenHeightDp = screenWidthDp; + break; + default: + break; + } + } + + public abstract void configAuthPage(); + + private void configBuild(){} + + /** + * 在横屏APP弹竖屏一键登录页面或者竖屏APP弹横屏授权页时处理特殊逻辑 + * Android8.0只能启动SCREEN_ORIENTATION_BEHIND模式的Activity + */ + public void onResume() {} +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomAssetsConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomAssetsConfig.java new file mode 100644 index 0000000..d5d1ff8 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomAssetsConfig.java @@ -0,0 +1,79 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.view.View; +import android.widget.FrameLayout; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.mobile.auth.gatewayauth.AuthRegisterXmlConfig; +import com.mobile.auth.gatewayauth.ui.AbstractPnsViewDelegate; +import com.sean.rao.ali_auth.R; +import com.sean.rao.ali_auth.common.CacheManage; +import com.sean.rao.ali_auth.common.MediaFileUtil; +import com.sean.rao.ali_auth.common.NativeBackgroundAdapter; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * xml文件方便预览 + * 可以通过addAuthRegisterXmlConfig一次性统一添加授权页的所有自定义view + */ +public class CustomAssetsConfig extends BaseUIConfig { + private CacheManage mCacheManage; + private ExecutorService mThreadExecutor; + private NativeBackgroundAdapter nativeBackgroundAdapter; + public CustomAssetsConfig() { + super(); + String fileType = "imagePath"; + if (jsonObject.getString("pageBackgroundPath") != null && !jsonObject.getString("pageBackgroundPath").isEmpty()) { + if (MediaFileUtil.isImageGifFileType(jsonObject.getString("pageBackgroundPath"))) { + fileType = "gifPath"; + } else if (MediaFileUtil.isVideoFileType(jsonObject.getString("pageBackgroundPath"))) { + fileType = "videoPath"; + } + } + mCacheManage=new CacheManage(mActivity.getApplication()); + mThreadExecutor=new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors(), + 0, + TimeUnit.SECONDS, + new ArrayBlockingQueue(10), + new ThreadPoolExecutor.CallerRunsPolicy() + ); + nativeBackgroundAdapter = new NativeBackgroundAdapter(mCacheManage, mThreadExecutor, fileType); + } + @Override + public void configAuthPage() { + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + updateScreenSize(authPageOrientation); + //sdk默认控件的区域是marginTop50dp + int designHeight = mScreenHeightDp - 50; + int unit = designHeight / 10; + mAuthHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder() + .setLayout(R.layout.authsdk_widget_custom_layout, new AbstractPnsViewDelegate() { + @Override + public void onViewCreated(View view) { + final FrameLayout fly_container = view.findViewById(R.id.fly_container); +// final Button close = view.findViewById(R.id.close); +// fly_container.setOnApplyWindowInsetsListener(); + nativeBackgroundAdapter.solveView(fly_container, "#3F51B5"); + } + }) + .build()); + + //添加自定义切换其他登录方式 + mAuthHelper.addAuthRegistViewConfig("switch_msg", new AuthRegisterViewConfig.Builder() + .setView(initSwitchView(420)) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY) + .build()); + + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomViewConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomViewConfig.java new file mode 100644 index 0000000..04348c0 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomViewConfig.java @@ -0,0 +1,86 @@ +package com.sean.rao.ali_auth.config; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.mobile.auth.gatewayauth.CustomInterface; + +import static com.nirvana.tools.core.AppUtils.dp2px; + +/** + * 号码栏水平位置的自定义view 推荐使用addAuthRegistViewConfig 可以添加到相对精准的位置 + */ +public class CustomViewConfig extends BaseUIConfig { + + public CustomViewConfig() { + super(); + } + + @Override + public void configAuthPage() { + mAuthHelper.addAuthRegistViewConfig("switch_msg", new AuthRegisterViewConfig.Builder() + .setView(initSwitchView(350)) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY) + .setCustomInterface(new CustomInterface() { + @Override + public void onClick(Context context) { +// Toast.makeText(mContext, "切换到短信登录方式", Toast.LENGTH_SHORT).show(); +// Intent pIntent = new Intent(mActivity, MessageActivity.class); +// mActivity.startActivityForResult(pIntent, 1002); +// mAuthHelper.quitLoginPage(); + } + }).build()); + mAuthHelper.addAuthRegistViewConfig("number_logo", new AuthRegisterViewConfig.Builder() + .setView(initNumberLogoView()) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_NUMBER) + .setCustomInterface(new CustomInterface() { + @Override + public void onClick(Context context) { + + } + }).build()); + mAuthHelper.addAuthRegistViewConfig("back_btn", new AuthRegisterViewConfig.Builder() + .setView(initBackBtn()) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_TITLE_BAR) + .setCustomInterface(new CustomInterface() { + @Override + public void onClick(Context context) { + mAuthHelper.quitLoginPage(); + mActivity.finish(); + } + }).build()); + + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } + + private ImageView initNumberLogoView() { + ImageView pImageView = new ImageView(mContext); + // pImageView.setImageResource(R.drawable.phone); + pImageView.setScaleType(ImageView.ScaleType.FIT_XY); + RelativeLayout.LayoutParams pParams = new RelativeLayout.LayoutParams(dp2px(mContext, 30), dp2px(mContext, 30)); + pParams.setMargins(dp2px(mContext, 100), 0, 0, 0); + pImageView.setLayoutParams(pParams); + return pImageView; + } + + private ImageView initBackBtn() { + ImageView pImageView = new ImageView(mContext); + // pImageView.setImageResource(R.drawable.icon_close); + pImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + RelativeLayout.LayoutParams pParams = new RelativeLayout.LayoutParams(dp2px(mContext, 20), dp2px(mContext, 20)); + pParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); + pParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); + pParams.setMargins(dp2px(mContext, 12.0F), 0, 0, 0); + pImageView.setLayoutParams(pParams); + return pImageView; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomXmlConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomXmlConfig.java new file mode 100644 index 0000000..0424ae1 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/CustomXmlConfig.java @@ -0,0 +1,63 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import com.mobile.auth.gatewayauth.AuthRegisterXmlConfig; +import com.mobile.auth.gatewayauth.ui.AbstractPnsViewDelegate; +import com.sean.rao.ali_auth.R; + +/** + * xml文件方便预览 + * 可以通过addAuthRegisterXmlConfig一次性统一添加授权页的所有自定义view + */ +public class CustomXmlConfig extends BaseUIConfig { + + public CustomXmlConfig() { + super(); + } + @Override + public void configAuthPage() { + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + + int getCustomXml = mContext.getResources().getIdentifier("custom_full_port", "layout", mContext.getPackageName()); + // 判断是否有自定义布局文件,没有则加载默认布局文件 + if(getCustomXml == 0){ + getCustomXml = mContext.getResources().getIdentifier("custom_full_port", "layout", mContext.getPackageName()); + } + View customXmlLayout = LayoutInflater.from(mContext).inflate(getCustomXml, new RelativeLayout(mContext), false); + mAuthHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder() + .setLayout(getCustomXml, new AbstractPnsViewDelegate() { + @Override + public void onViewCreated(View view) { + findViewById(R.id.btn_back).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + /// 点击关闭按钮 + // eventSink.success(UtilTool.resultFormatData("700000", null, null)); + showResult("700000", "点击返回按钮", ""); + mAuthHelper.quitLoginPage(); + } + }); + + findViewById(R.id.tv_switch).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + /// 点击切换其他按钮 + showResult("700001", "切换到其他方式", ""); + // eventSink.success(UtilTool.resultFormatData("700001", null, null)); + mAuthHelper.quitLoginPage(); + } + }); + } + }) + .build()); + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogBottomConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogBottomConfig.java new file mode 100644 index 0000000..9c78f0a --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogBottomConfig.java @@ -0,0 +1,51 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.os.Build; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.sean.rao.ali_auth.utils.AppUtils; +import com.sean.rao.ali_auth.utils.UtilTool; + +import java.io.IOException; + +public class DialogBottomConfig extends BaseUIConfig { + + public DialogBottomConfig() { + super(); + } + + @Override + public void configAuthPage() { + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + updateScreenSize(authPageOrientation); + + int dialogHeight = (int) (mScreenHeightDp * 0.5f); + //sdk默认控件的区域是marginTop50dp + int designHeight = dialogHeight - 50; + int unit = designHeight / 10; + mAuthHelper.addAuthRegistViewConfig("switch_msg", new AuthRegisterViewConfig.Builder() + .setView(initSwitchView(unit * 6)) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY) + .build()); + + if (jsonObject.containsKey("pageBackgroundPath") && !jsonObject.getString("pageBackgroundPath").isEmpty()) { + try { + RoundedBitmapDrawable pageBackgroundDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), UtilTool.getPathToBitmap(mContext, jsonObject.getString("pageBackgroundPath"))); + pageBackgroundDrawable.setCornerRadius(AppUtils.dp2px(mContext, jsonObject.getIntValue("pageBackgroundRadius"))); + config.setPageBackgroundDrawable(pageBackgroundDrawable); + } catch (IOException e) { + // eventSink.success(UtilTool.resultFormatData("500000", null, e.getMessage())); + showResult("500000", "背景处理时出现错误", e.getMessage()); + } + } + + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogLandConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogLandConfig.java new file mode 100644 index 0000000..f434113 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogLandConfig.java @@ -0,0 +1,114 @@ +package com.sean.rao.ali_auth.config; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.mobile.auth.gatewayauth.CustomInterface; +import com.sean.rao.ali_auth.utils.AppUtils; + +public class DialogLandConfig extends BaseUIConfig{ + private int mOldScreenOrientation; + + + public DialogLandConfig() { + super(); + } + + @Override + public void configAuthPage() { + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + if (Build.VERSION.SDK_INT == 26) { + mOldScreenOrientation = mActivity.getRequestedOrientation(); + mActivity.setRequestedOrientation(authPageOrientation); + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + updateScreenSize(authPageOrientation); + final int dialogWidth = (int) (mScreenWidthDp * 0.63); + final int dialogHeight = (int) (mScreenHeightDp * 0.6); + + //sdk默认控件的区域是marginTop50dp + int designHeight = dialogHeight - 50; + int unit = designHeight / 10; + int logBtnHeight = (int) (unit * 1.2); + final int logBtnOffsetY = unit * 3; + + final View switchContainer = createLandDialogCustomSwitchView(); + mAuthHelper.addAuthRegistViewConfig("number_logo", new AuthRegisterViewConfig.Builder() + .setView(initNumberView()) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_NUMBER) + .setCustomInterface(new CustomInterface() { + @Override + public void onClick(Context context) { + + } + }).build()); + mAuthHelper.addAuthRegistViewConfig("switch_other", new AuthRegisterViewConfig.Builder() + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_NUMBER) + .setView(switchContainer).build()); +// mAuthHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder() +// .setLayout(R.layout.custom_land_dialog, new AbstractPnsViewDelegate() { +// @Override +// public void onViewCreated(View view) { +// findViewById(R.id.tv_title).setVisibility(View.GONE); +// findViewById(R.id.btn_close).setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// mAuthHelper.quitLoginPage(); +// } +// }); +// int iconTopMargin = AppUtils.dp2px(getContext(), logBtnOffsetY + 50); +// View iconContainer = findViewById(R.id.container_icon); +// RelativeLayout.LayoutParams iconLayout = (RelativeLayout.LayoutParams) iconContainer.getLayoutParams(); +// iconLayout.topMargin = iconTopMargin; +// iconLayout.width = AppUtils.dp2px(getContext(), dialogWidth / 2 - 60); +// } +// }) +// .build()); + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } + + private ImageView createLandDialogPhoneNumberIcon(int leftMargin) { + ImageView imageView = new ImageView(mContext); + int size = AppUtils.dp2px(mContext, 23); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(size, size); + layoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); + layoutParams.leftMargin = leftMargin; + imageView.setLayoutParams(layoutParams); + // imageView.setBackgroundResource(R.drawable.phone); + imageView.setScaleType(ImageView.ScaleType.CENTER); + return imageView; + } + + private View createLandDialogCustomSwitchView() { +// View v = LayoutInflater.from(mContext).inflate(R.layout.custom_switch_other, new RelativeLayout(mContext), false); +// RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, +// RelativeLayout.LayoutParams.WRAP_CONTENT); +// layoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); +// v.setLayoutParams(layoutParams); +// return v; + return null; + } + + @Override + public void onResume() { + super.onResume(); + if (mOldScreenOrientation != mActivity.getRequestedOrientation()) { + mActivity.setRequestedOrientation(mOldScreenOrientation); + } + } + + private ImageView initNumberView() { + ImageView pImageView = new ImageView(mContext); + // pImageView.setImageResource(R.drawable.phone); + pImageView.setScaleType(ImageView.ScaleType.FIT_XY); + RelativeLayout.LayoutParams pParams = new RelativeLayout.LayoutParams(AppUtils.dp2px(mContext, 30), AppUtils.dp2px(mContext, 30)); + pParams.setMargins(AppUtils.dp2px(mContext, 30), 0, 0, 0); + pImageView.setLayoutParams(pParams); + return pImageView; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogPortConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogPortConfig.java new file mode 100644 index 0000000..28cca69 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/DialogPortConfig.java @@ -0,0 +1,106 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Build; +import android.util.Log; +import android.view.View; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.sean.rao.ali_auth.common.CustomAuthUIControlClickListener; +import com.sean.rao.ali_auth.utils.AppUtils; +import com.sean.rao.ali_auth.utils.UtilTool; + +import java.io.IOException; + +public class DialogPortConfig extends BaseUIConfig { + /** + * 应用包名 + */ + private String mPackageName; + + public DialogPortConfig() { + super(); + mPackageName = AppUtils.getPackageName(mActivity); + } + + @Override + public void configAuthPage() { + // 注册 UI 点击事件监听(用户取消、勾选协议等事件) + mAuthHelper.setUIClickListener(new CustomAuthUIControlClickListener()); + + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + updateScreenSize(authPageOrientation); + + // 添加第三方登录按钮(如果配置了的话) + View switchView = initSwitchView(420); + if (switchView != null) { + mAuthHelper.addAuthRegistViewConfig("switch_msg", new AuthRegisterViewConfig.Builder() + .setView(switchView) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY) + .build()); + } + + // 弹窗背景 + String bgPath = jsonObject.getString("pageBackgroundPath"); + if (bgPath != null && !bgPath.isEmpty()) { + // 有背景图片:加载图片并设置圆角 + try { + RoundedBitmapDrawable pageBackgroundDrawable = RoundedBitmapDrawableFactory.create( + mContext.getResources(), + UtilTool.getPathToBitmap(mContext, bgPath)); + pageBackgroundDrawable.setCornerRadius( + AppUtils.dp2px(mContext, jsonObject.getIntValue("pageBackgroundRadius"))); + config.setPageBackgroundDrawable(pageBackgroundDrawable); + } catch (IOException e) { + showResult("500000", "背景处理时出现错误", e.getMessage()); + } + } else { + // 无背景图片:使用纯色背景 + 圆角 + int bgColor = Color.WHITE; + if (jsonObject.containsKey("backgroundColor")) { + Object bgVal = jsonObject.get("backgroundColor"); + if (bgVal instanceof Integer) { + bgColor = (Integer) bgVal; + } else if (bgVal instanceof String) { + try { bgColor = Color.parseColor((String) bgVal); } catch (Exception ignored) {} + } + } + int cornerRadius = AppUtils.dp2px(mContext, jsonObject.getIntValue("pageBackgroundRadius", 12)); + GradientDrawable bgDrawable = new GradientDrawable(); + bgDrawable.setColor(bgColor); + bgDrawable.setCornerRadius(cornerRadius); + config.setPageBackgroundDrawable(bgDrawable); + Log.i("DialogPortConfig", "使用纯色背景: #" + Integer.toHexString(bgColor) + ", 圆角: " + cornerRadius); + } + + // 登录按钮背景:如果没有设置 logBtnBackgroundPath,使用蓝色圆角背景 + String btnPath = jsonObject.getString("logBtnBackgroundPath"); + if (btnPath == null || btnPath.isEmpty()) { + int btnRadius = AppUtils.dp2px(mContext, 22); + // 正常状态 + GradientDrawable normalDrawable = new GradientDrawable(); + normalDrawable.setColor(Color.parseColor("#4A90D9")); + normalDrawable.setCornerRadius(btnRadius); + // 按下状态 + GradientDrawable pressedDrawable = new GradientDrawable(); + pressedDrawable.setColor(Color.parseColor("#3A7BC8")); + pressedDrawable.setCornerRadius(btnRadius); + + StateListDrawable btnStateDrawable = new StateListDrawable(); + btnStateDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDrawable); + btnStateDrawable.addState(new int[]{}, normalDrawable); + config.setLogBtnBackgroundDrawable(btnStateDrawable); + } + + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullLandConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullLandConfig.java new file mode 100644 index 0000000..926ac7c --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullLandConfig.java @@ -0,0 +1,48 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.os.Build; + +public class FullLandConfig extends BaseUIConfig { + + private int mOldScreenOrientation; + + public FullLandConfig() { + super(); + } + + @Override + public void configAuthPage() { +// mAuthHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig.Builder() +// .setLayout(R.layout.custom_port_dialog_action_bar, new AbstractPnsViewDelegate() { +// @Override +// public void onViewCreated(View view) { +// findViewById(R.id.tv_title).setVisibility(View.GONE); +// findViewById(R.id.btn_close).setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// mAuthHelper.quitLoginPage(); +// } +// }); +// } +// }) +// .build()); + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + if (Build.VERSION.SDK_INT == 26) { + mOldScreenOrientation = mActivity.getRequestedOrientation(); + mActivity.setRequestedOrientation(authPageOrientation); + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + updateScreenSize(authPageOrientation); + + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } + + @Override + public void onResume() { + super.onResume(); + if (mOldScreenOrientation != mActivity.getRequestedOrientation()) { + mActivity.setRequestedOrientation(mOldScreenOrientation); + } + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullPortConfig.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullPortConfig.java new file mode 100644 index 0000000..5e8337c --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/config/FullPortConfig.java @@ -0,0 +1,40 @@ +package com.sean.rao.ali_auth.config; + +import android.content.pm.ActivityInfo; +import android.os.Build; + +import com.mobile.auth.gatewayauth.AuthRegisterViewConfig; +import com.sean.rao.ali_auth.common.CustomAuthUIControlClickListener; + +public class FullPortConfig extends BaseUIConfig { + private final String TAG = "全屏竖屏样式"; + + public FullPortConfig() { + super(); + } + + @Override + public void configAuthPage() { + mAuthHelper.setUIClickListener(new CustomAuthUIControlClickListener()); + //添加自定义切换其他登录方式 + mAuthHelper.addAuthRegistViewConfig("switch_msg", new AuthRegisterViewConfig.Builder() + .setView(initSwitchView(420)) + .setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY) +// .setCustomInterface(new CustomInterface() { +// @Override +// public void onClick(Context context) { +// JSONObject jsonObj = new JSONObject(); +// jsonObj.put("code", "7000001"); +// jsonObj.put("data", ""); +// jsonObj.put("msg", "切换到短信登录方式"); +// eventSink.success(jsonObj); +// } +// }) + .build()); + int authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + if (Build.VERSION.SDK_INT == 26) { + authPageOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND; + } + mAuthHelper.setAuthUIConfig(config.setScreenOrientation(authPageOrientation).create()); + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/login/OneKeyLoginPublic.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/login/OneKeyLoginPublic.java new file mode 100644 index 0000000..53ef8fa --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/login/OneKeyLoginPublic.java @@ -0,0 +1,367 @@ +package com.sean.rao.ali_auth.login; + +import android.app.Activity; +import android.graphics.Color; +import android.util.Log; + +import androidx.annotation.IntRange; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.mobile.auth.gatewayauth.AuthUIConfig; +import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper; +import com.mobile.auth.gatewayauth.PreLoginResultListener; +import com.mobile.auth.gatewayauth.ResultCode; +import com.mobile.auth.gatewayauth.TokenResultListener; +import com.mobile.auth.gatewayauth.model.TokenRet; +import com.sean.rao.ali_auth.common.LoginParams; +import com.sean.rao.ali_auth.config.BaseUIConfig; +import com.sean.rao.ali_auth.utils.UtilTool; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import io.flutter.plugin.common.EventChannel; + +/** + * 进app直接登录的场景 + */ +public class OneKeyLoginPublic extends LoginParams { + private static final String TAG = OneKeyLoginPublic.class.getSimpleName(); + + public OneKeyLoginPublic(Activity activity, EventChannel.EventSink _eventSink, Object arguments){ + mActivity = activity; + mContext = activity.getBaseContext(); + eventSink = _eventSink; + jsonObject = formatParmas(arguments); + config = getFormatConfig(jsonObject); + + // 初始化SDK + sdkInit(); + mUIConfig = BaseUIConfig.init(jsonObject.getIntValue("pageType")); + if (jsonObject.getBooleanValue("isDelay")) { + } else { + // 非延时的情况下需要判断是否给予登录 + mAuthHelper.quitLoginPage(); + oneKeyLogin(); + } + } + + /** + * 初始化SDK + */ + private void sdkInit() { + mTokenResultListener=new TokenResultListener() { + @Override + public void onTokenSuccess(String s) { + sdkAvailable = true; + try { + Log.i(TAG, "checkEnvAvailable:" + s); + TokenRet tokenRet = TokenRet.fromJson(s); + if (ResultCode.CODE_ERROR_ENV_CHECK_SUCCESS.equals(tokenRet.getCode())) { + /// 延时登录的情况下加速拉起一键登录页面 + if (jsonObject.getBooleanValue("isDelay")) { + accelerateLoginPage(5000); + } + } + + if (ResultCode.CODE_SUCCESS.equals(tokenRet.getCode())) { + Log.i("TAG", "获取token成功:" + s); + mAuthHelper.setAuthListener(null); + } + showResult(tokenRet.getCode(), null, tokenRet.getToken()); + } catch (Exception e) { + e.fillInStackTrace(); + } + } + + @Override + public void onTokenFailed(String s) { + sdkAvailable = false; + mAuthHelper.hideLoginLoading(); + Log.e(TAG, "获取token失败:" + s); + try { + TokenRet tokenRet = TokenRet.fromJson(s); + List skip = Collections.singletonList(ResultCode.CODE_ERROR_USER_SWITCH); + if (!skip.contains(tokenRet.getCode())) { + showResult(tokenRet.getCode(), tokenRet.getMsg(),null); + } + } catch (Exception e) { + e.fillInStackTrace(); + } + mAuthHelper.setAuthListener(null); + } + }; + mAuthHelper=PhoneNumberAuthHelper.getInstance(mContext, mTokenResultListener); + mAuthHelper.getReporter().setLoggerEnable(jsonObject.getBooleanValue("isDebug")); + mAuthHelper.setAuthSDKInfo(jsonObject.getString("androidSk")); + + // 授权页是否跟随系统深色模式 + mAuthHelper.setAuthPageUseDayLight(jsonObject.getBooleanValue("authPageUseDayLight", false)); + // 横屏水滴屏全屏适配 + mAuthHelper.keepAuthPageLandscapeFullSreen(jsonObject.getBooleanValue("autoQuitPage", false)); + // 用户控制返回键及左上角返回按钮效果, 是否自动退出授权页面,false时由用户自己控制 + if (!jsonObject.getBooleanValue("autoQuitPage", false)) { + mAuthHelper.userControlAuthPageCancel(); + } + // SDK内置所有界面隐藏底部导航栏 + if (jsonObject.getBooleanValue("keepAllPageHideNavigationBar", false)) { + mAuthHelper.keepAllPageHideNavigationBar(); + } + // 授权页物理返回键禁用 + mAuthHelper.closeAuthPageReturnBack(jsonObject.getBooleanValue("closeAuthPageReturnBack", false)); + /// 延时的情况下进行预取号,加快拉取授权页面 + + if (jsonObject.getBooleanValue("isDelay")) { + mAuthHelper.checkEnvAvailable(PhoneNumberAuthHelper.SERVICE_TYPE_LOGIN); + } + } + + /** + * 延时登录操作 + * @param timeout + */ + public void startLogin(int timeout){ + if (sdkAvailable) { + mAuthHelper.quitLoginPage(); + getLoginToken(timeout); + } else { + Log.e(TAG, "SDK环境检查未通过,无法拉起授权页"); + showResult("600002", "SDK环境不可用,请检查网络和SIM卡", null); + } + } + + /** + * 返回默认上网卡运营商 + * @param + * @return CMCC(移动)、CUCC(联通)、CTCC(电信) + */ + public String getCurrentCarrierName(){ + return mAuthHelper.getCurrentCarrierName(); + } + + /** + * 进入app就需要登录的场景使用 + */ + private void oneKeyLogin() { + mUIConfig.configAuthPage(); + mAuthHelper.getLoginToken(mActivity, 5000); + } + + /** + * 在不是一进app就需要登录的场景 建议调用此接口 加速拉起一键登录页面 + * 等到用户点击登录的时候 授权页可以秒拉 + * 预取号的成功与否不影响一键登录功能,所以不需要等待预取号的返回。 + * @param timeout + */ + private void accelerateLoginPage(int timeout) { + mAuthHelper.accelerateLoginPage(timeout, new PreLoginResultListener() { + @Override + public void onTokenSuccess(String s) { + Log.e(TAG, "预取号成功: " + s); + showResult("600016", null, s); + } + @Override + public void onTokenFailed(String s, String s1) { + Log.e(TAG, "预取号失败:" + ", " + s1); + JSONObject jsonDataObj = new JSONObject(); + jsonDataObj.put("name", s); + jsonDataObj.put("name1", s1); + showResult("600012", null, jsonDataObj); + } + }); + } + + /** + * 拉起授权页 + * @param timeout 超时时间 + */ + public void getLoginToken(int timeout) { + try { + Log.i(TAG, "configAuthPage 开始配置授权页..."); + mUIConfig.configAuthPage(); + Log.i(TAG, "configAuthPage 配置完成"); + } catch (Exception e) { + Log.e(TAG, "configAuthPage 异常: " + e.getMessage(), e); + showResult("600002", "授权页配置异常: " + e.getMessage(), null); + return; + } + mTokenResultListener = new TokenResultListener() { + @Override + public void onTokenSuccess(String s) { + TokenRet tokenRet = TokenRet.fromJson(s); + try { + if (ResultCode.CODE_START_AUTHPAGE_SUCCESS.equals(tokenRet.getCode())) { + Log.i(TAG, "唤起授权页成功:" + s); + } + showResult(tokenRet.getCode(), tokenRet.getMsg(),tokenRet.getToken()); + if (ResultCode.CODE_SUCCESS.equals(tokenRet.getCode())) { + Log.i(TAG, "获取token成功:" + s); + mAuthHelper.setAuthListener(null); + } + } catch (Exception e) { + e.fillInStackTrace(); + } + } + + @Override + public void onTokenFailed(String s) { + Log.e(TAG, "获取token失败:" + s); + //如果环境检查失败 使用其他登录方式 + try { + TokenRet tokenRet = TokenRet.fromJson(s); + showResult(tokenRet.getCode(), tokenRet.getMsg(),null); + } catch (Exception e) { + e.fillInStackTrace(); + } + // 失败时也不关闭 + mAuthHelper.setAuthListener(null); + } + }; + mAuthHelper.setAuthListener(mTokenResultListener); + mAuthHelper.getLoginToken(mActivity, timeout); + } + + + /** + * SDK环境检查函数,检查终端是否⽀持号码认证,通过TokenResultListener返回code + * type 1:本机号码校验 2: ⼀键登录 + * 600024 终端⽀持认证 + * 600013 系统维护,功能不可⽤ + */ + public void checkEnvAvailable(@IntRange(from = 1, to = 2) int type){ + mAuthHelper.checkEnvAvailable(type); + } + + + /** + * 获取授权页协议勾选框选中状态 + */ + public boolean queryCheckBoxIsChecked(){ + return mAuthHelper.queryCheckBoxIsChecked(); + } + + + /** + * 设置授权页协议勾选框选中状态 + * + * @param paramBoolean勾选框选中状态 + */ + public boolean setCheckBoxIsChecked(){ + mAuthHelper.setProtocolChecked(true); + return true; + } + + /** + * 退出授权页面 + */ + public void quitPage(){ + mAuthHelper.quitLoginPage(); + } + + /** + * 结束授权页loading dialog + */ + public void hideLoading(){ + mAuthHelper.hideLoginLoading(); + } + + /** + * 处理参数,对参数进行处理包含color、Path + * @param parmas + * @return + */ + private JSONObject formatParmas(Object parmas){ + JSONObject formatData = JSONObject.parseObject(JSONObject.toJSONString(parmas)); + for (Map.Entry entry : formatData.entrySet()) { + String key = entry.getKey(); + String keyLower = key.toLowerCase(); + String strValue = formatData.getString(key); + + // 判断是否是颜色代码 + if (keyLower.contains("color") && strValue != null && strValue.contains("#")) { + try { + formatData.put(key, Color.parseColor(strValue)); + } catch (Exception e) { + Log.w(TAG, "颜色解析失败: " + key + " = " + strValue, e); + } + } + // 判断是否是路径字段(排除按钮状态背景 logBtnBackgroundPath) + else if ( + !key.contains("logBtnBackgroundPath") && + keyLower.contains("path") && + strValue != null && + !strValue.isEmpty() && + !strValue.contains("http") + ) { + formatData.put(key, UtilTool.flutterToPath(strValue)); + } + } + + return formatData; + } + + + /** + * 对配置参数进行格式化并且转换 + * @param jsonObject + * @return + */ + private AuthUIConfig.Builder getFormatConfig(JSONObject jsonObject){ + AuthUIConfig.Builder config = JSON.parseObject(JSONObject.toJSONString(jsonObject), AuthUIConfig.Builder.class); + + // 设置按钮的背景 + // 20230518 修正错误 setLoadingBackgroundPath -> setLogBtnBackgroundPath + if (jsonObject.getString("logBtnBackgroundPath") != null && jsonObject.getString("logBtnBackgroundPath").contains(",")) { + config.setLogBtnBackgroundDrawable(UtilTool.getStateListDrawable(mContext, jsonObject.getString("logBtnBackgroundPath"))); + } else { + config.setLogBtnBackgroundPath(UtilTool.flutterToPath(jsonObject.getString("logBtnBackgroundPath"))); + } + /** + * authPageActIn = var1; + * activityOut = var2; + */ + if(UtilTool.dataStatus(jsonObject, "authPageActIn") && UtilTool.dataStatus(jsonObject, "activityOut")){ + config.setAuthPageActIn(jsonObject.getString("authPageActIn"), jsonObject.getString("activityOut")); + } + /** + * authPageActOut = var1; + * activityIn = var2; + */ + if(UtilTool.dataStatus(jsonObject, "authPageActOut") && UtilTool.dataStatus(jsonObject, "activityIn")){ + config.setAuthPageActOut(jsonObject.getString("authPageActOut"), jsonObject.getString("activityIn")); + } + /** + * protocolOneName = var1; + * protocolOneURL = var2; + */ + if(UtilTool.dataStatus(jsonObject, "protocolOneName") && UtilTool.dataStatus(jsonObject, "protocolOneURL")){ + config.setAppPrivacyOne(jsonObject.getString("protocolOneName"), jsonObject.getString("protocolOneURL")); + } + /** + * protocolTwoName = var1; + * protocolTwoURL = var2; + */ + if(UtilTool.dataStatus(jsonObject, "protocolTwoName") && UtilTool.dataStatus(jsonObject, "protocolTwoURL")){ + config.setAppPrivacyTwo(jsonObject.getString("protocolTwoName"), jsonObject.getString("protocolTwoURL")); + } + /** + * protocolThreeName = var1; + * protocolThreeURL = var2; + */ + if(UtilTool.dataStatus(jsonObject, "protocolThreeName") && UtilTool.dataStatus(jsonObject, "protocolThreeURL")){ + config.setAppPrivacyThree(jsonObject.getString("protocolThreeName"), jsonObject.getString("protocolThreeURL")); + } + /** + * protocolColor = var1; + * protocolOneColor = var2; + * protocolTwoColor = var2; + * protocolThreeColor = var2; + */ + if(UtilTool.dataStatus(jsonObject, "protocolColor") && UtilTool.dataStatus(jsonObject, "protocolCustomColor")){ + config.setAppPrivacyColor(jsonObject.getIntValue("protocolColor"), jsonObject.getIntValue("protocolCustomColor")); + } + return config; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/AppUtils.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/AppUtils.java new file mode 100644 index 0000000..9879ac1 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/AppUtils.java @@ -0,0 +1,45 @@ +package com.sean.rao.ali_auth.utils; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +public class AppUtils extends com.nirvana.tools.core.AppUtils { + public static int dp2px(Context context, float dipValue) { + try { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } catch (Exception e) { + return (int) dipValue; + } + } + + public static int px2dp(Context context, float px) { + try { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (px / scale + 0.5f); + } catch (Exception e) { + return (int) px; + } + } + + public static int getPhoneWidthPixels(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics var2 = new DisplayMetrics(); + if (wm != null) { + wm.getDefaultDisplay().getMetrics(var2); + } + + return var2.widthPixels; + } + + public static int getPhoneHeightPixels(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics var2 = new DisplayMetrics(); + if (wm != null) { + wm.getDefaultDisplay().getMetrics(var2); + } + + return var2.heightPixels; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/ExecutorManager.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/ExecutorManager.java new file mode 100644 index 0000000..e27068f --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/ExecutorManager.java @@ -0,0 +1,22 @@ +package com.sean.rao.ali_auth.utils; + + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class ExecutorManager { + private static ExecutorService threadExecutor; + + public static void run(Runnable var0) { + threadExecutor.execute(var0); + } + + static { + threadExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors(), + 0, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.CallerRunsPolicy()); + } + +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/PermissionUtils.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/PermissionUtils.java new file mode 100644 index 0000000..917319c --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/PermissionUtils.java @@ -0,0 +1,27 @@ +package com.sean.rao.ali_auth.utils; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.os.Build; +import androidx.core.content.PermissionChecker; + +import java.util.ArrayList; +import java.util.List; + +public class PermissionUtils { + @TargetApi(Build.VERSION_CODES.M) + public static void checkAndRequestPermissions(Activity context, int requestCode, + String... permissions) { + List deniedPermissions = new ArrayList<>(permissions.length); + for(String permission:permissions) { + if(PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker.PERMISSION_DENIED) { + deniedPermissions.add(permission); + } + } + if(!deniedPermissions.isEmpty()) { + String[] ps = new String[deniedPermissions.size()]; + ps = deniedPermissions.toArray(ps); + context.requestPermissions(ps, requestCode); + } + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/StatusAll.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/StatusAll.java new file mode 100644 index 0000000..4567e7e --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/StatusAll.java @@ -0,0 +1,105 @@ +package com.sean.rao.ali_auth.utils; + +/** + * @ProjectName: ali_auth + * @Package: com.jokui.rao.auth.ali_auth + * @ClassName: StatusAll + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 2/16/22 5:58 PM + * @UpdateUser: 更新者 + * @UpdateDate: 2/16/22 5:58 PM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +public enum StatusAll { + Status500000("参数获取异常!", "500000"), + Status500001("请先对插件进行监听!", "500001"), + Status500002("校验成功,可进行一键登录!", "500002"), + Status500003("该接口为延时登录接口,请先初始化后再次调用该接口!", "500003"), + Status500004("插件启动监听成功, 当前SDK版本: %s", "500004"), + Status600000("获取token成功!", "600000"), + Status600001("唤起授权页成功!", "600001"), + Status600002("唤起授权⻚失败!建议切换到其他登录⽅式", "600002"), + Status600004("获取运营商配置信息失败!创建⼯单联系⼯程师", "600004"), + Status600005("⼿机终端不安全!切换到其他登录⽅式", "600005"), + Status600007("未检测到sim卡!⽤户检查 SIM 卡后重试", "600007"), + Status600008("蜂窝⽹络未开启!⽤户开启移动⽹络后重试", "600008"), + Status600009("⽆法判断运营商! 创建⼯单联系⼯程师", "600009"), + Status600010("未知异常创建!⼯单联系⼯程师", "600010"), + Status600011("获取token失败!切换到其他登录⽅式", "600011"), + Status600012("预取号失败!", "600012"), + Status600013("运营商维护升级!该功能不可⽤创建⼯单联系⼯程师", "600013"), + Status600014("运营商维护升级!该功能已达最⼤调⽤次创建⼯单联系⼯程师", "600014"), + Status600015("接⼝超时!切换到其他登录⽅式", "600011"), + Status600016("预取号成功!", "600016"), + Status600017("AppID、Appkey解析失败! 秘钥未设置或者设置错误,请先检查秘钥信息,如果⽆法解决问题创建⼯单联系⼯程师", "600017"), + Status600018("请先初始化SDK!", "600018"), + Status600019("用户点击第三方按钮!", "600019"), + Status600021("点击登录时检测到运营商已切换!⽤户退出授权⻚,重新登录", "600021"), + Status600023("加载⾃定义控件异常!检查⾃定义控件添加是否正确", "600023"), + Status600024("终端环境检查⽀持认证", "600024"), + Status600025("终端检测参数错误检查传⼊参数类型与范围是否正确", "600025"), + Status600026("授权⻚已加载时不允许调⽤加速或预取号接⼝检查是否有授权⻚拉起后,去调⽤preLogin或者accelerateAuthPage的接⼝,该⾏为不允许", "600026"), + Status700000("用户取消登录", "700000"), + Status700001("用户切换其他登录方式", "700001"), + Status700002("用户点击登录按钮", "700002"), + Status700003("用户勾选协议选项", "700003"), + Status700004("用户点击协议富文本", "700004"), + Status700006("点击一键登录拉起授权页二次弹窗", "700006"), + Status700007("隐私协议二次弹窗关闭", "700007"), + Status700008("点击隐私协议二次弹窗上同意并继续", "700008"), + Status700009("点击隐私协议二次弹窗上的协议富文本文字", "700009"), + Status700010("中断页面消失时(suspendDisMissVC设置为YES时),点击左上角返回按钮透出的状态码", "700010"), + Status700020("授权页已销毁", "700020"); + + + private String name; + private String value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + // 构造方法 + StatusAll(String name, String value) { + this.name = name; + this.value = value; + } + + // 普通方法 + public static String getName(String value) { + for (StatusAll t : StatusAll.values()) { + if (t.getValue().equals(value)) { + return t.name; + } + } + return null; + } + /** + * 通过value取枚举 + * @param valueKey + * @return + */ + public static StatusAll getStatusByValue(String valueKey){ + for (StatusAll enums : StatusAll.values()) { + if (enums.getValue().equals(valueKey)) { + return enums; + } + } + return null; + } + +} diff --git a/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/UtilTool.java b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/UtilTool.java new file mode 100644 index 0000000..b724c07 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/java/com/sean/rao/ali_auth/utils/UtilTool.java @@ -0,0 +1,161 @@ +package com.sean.rao.ali_auth.utils; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.StateListDrawable; +import android.text.TextUtils; + +import com.alibaba.fastjson2.JSONObject; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +import io.flutter.FlutterInjector; +import io.flutter.embedding.engine.loader.FlutterLoader; +import io.flutter.plugin.common.MethodCall; + + +/** + * @ProjectName: android + * @Package: com.sean.rao.ali_auth.utils + * @ClassName: UtilTool + * @Description: java类作用描述 + * @Author: liys + * @CreateDate: 5/11/22 3:33 PM + * @UpdateUser: 更新者 + * @UpdateDate: 5/11/22 3:33 PM + * @UpdateRemark: 更新说明 + * @Version: 1.0 + */ +public class UtilTool { + /** + * 判断数据类型 + * @param data + * @param key + * @return + */ + public static boolean dataStatus(JSONObject data, String key ){ + if(data.containsKey(key) && data.get(key) != null){ + if((data.get(key) instanceof Float) || (data.get(key) instanceof Double) && (double) data.get(key) > -1){ + return true; + } else if((data.get(key) instanceof Integer) || (data.get(key) instanceof Number) && (int) data.get(key) > -1){ + return true; + } else if((data.get(key) instanceof Boolean) && (boolean) data.get(key)){ + return true; + } else if((data.get(key) instanceof String) && !((String) data.get(key)).equals("")){ + return true; + } else { + return false; + } + } else { + return false; + } + } + + + /** + * flutter 路径转换 + * @param fluPath + * @return + */ + public static String flutterToPath(@Nullable Object fluPath){ + FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader(); + return flutterLoader.getLookupKeyForAsset(String.valueOf(fluPath)); + } + + /** + * 获取key + * @param call + * @param key + * @return + */ + public static Object getValueByKey(MethodCall call, String key) { + if (call != null && call.hasArgument(key)) { + return call.argument(key); + } else { + return null; + } + } + + /** + * 创建有状态的按钮 + * @param mContext + * @param btnPathImage + * @return + */ + public static Drawable getStateListDrawable(Context mContext, String btnPathImage) { + StateListDrawable stateListDrawable = new StateListDrawable(); + if (!btnPathImage.contains(",")) { + return stateListDrawable; + } + try{ + List btnPathList = Arrays.asList(btnPathImage.split(",")); + // 正常状态下的Drawable + BitmapDrawable drawable_p = getBitmapToBitmapDrawable(mContext, flutterToPath(btnPathList.get(0))); + // 按下和获取焦点是的Drawable + BitmapDrawable drawable_n = getBitmapToBitmapDrawable(mContext, flutterToPath(btnPathList.get(1))); + // 被禁用时的Drawable + BitmapDrawable drawable_b = getBitmapToBitmapDrawable(mContext, flutterToPath(btnPathList.get(2))); + + stateListDrawable.addState(new int[]{android.R.attr.state_activated, android.R.attr.state_pressed}, drawable_n); + stateListDrawable.addState(new int[]{android.R.attr.state_activated, -android.R.attr.state_pressed}, drawable_p); + stateListDrawable.addState(new int[]{-android.R.attr.state_activated, -android.R.attr.state_pressed}, drawable_b); + stateListDrawable.addState(new int[]{-android.R.attr.state_activated, android.R.attr.state_pressed}, drawable_n); + } catch (IOException e) { + e.fillInStackTrace(); + } + return stateListDrawable; + } + + /** + * 将bitmap数据转换为drawable + * @param mContext + * @param path + * @return + */ + public static BitmapDrawable getBitmapToBitmapDrawable(Context mContext, String path) throws IOException { + // 正常状态下的Drawable + return new BitmapDrawable(mContext.getResources(), getPathToBitmap(mContext, path)); + } + + /** + * 获取本地图片转换为bitmap + * @param mContext + * @param path + * @return + */ + public static Bitmap getPathToBitmap(Context mContext, String path) throws IOException { + AssetManager assetManager = mContext.getAssets(); + InputStream fileDescriptor = assetManager.open(path); + Bitmap bitmap = BitmapFactory.decodeStream(fileDescriptor); + return bitmap; + } + + /** + * 返回数据封装 + * @param code + * @param msg + * @param jsonDataObj + * @return + */ + public static JSONObject resultFormatData(String code, @Nullable String msg, @Nullable Object jsonDataObj){ + JSONObject jsonObj = new JSONObject(); + jsonObj.put("code", code); + jsonObj.put("msg", msg != null && !msg.isEmpty() ? msg : StatusAll.getName(code)); + jsonObj.put("data", jsonDataObj != null ? jsonDataObj : ""); + jsonObj.put("isChecked", false); + return jsonObj; + } +} diff --git a/airhub_app/packages/ali_auth/android/src/main/kotlin/com/sean/rao/ali_auth/AliAuthPlugin.kt b/airhub_app/packages/ali_auth/android/src/main/kotlin/com/sean/rao/ali_auth/AliAuthPlugin.kt deleted file mode 100644 index 5db34de..0000000 --- a/airhub_app/packages/ali_auth/android/src/main/kotlin/com/sean/rao/ali_auth/AliAuthPlugin.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.sean.rao.ali_auth - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel - -class AliAuthPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { - private lateinit var channel: MethodChannel - - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(binding.binaryMessenger, "ali_auth") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - result.notImplemented() - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } -} diff --git a/airhub_app/packages/ali_auth/android/src/main/res/anim/in_activity.xml b/airhub_app/packages/ali_auth/android/src/main/res/anim/in_activity.xml new file mode 100755 index 0000000..ccc7a88 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/anim/in_activity.xml @@ -0,0 +1,8 @@ + + + + diff --git a/airhub_app/packages/ali_auth/android/src/main/res/anim/out_activity.xml b/airhub_app/packages/ali_auth/android/src/main/res/anim/out_activity.xml new file mode 100755 index 0000000..4748f26 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/anim/out_activity.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/airhub_app/packages/ali_auth/android/src/main/res/color/selector_bottom_item_color.xml b/airhub_app/packages/ali_auth/android/src/main/res/color/selector_bottom_item_color.xml new file mode 100755 index 0000000..9bc0ab4 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/color/selector_bottom_item_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/src/main/res/drawable/icon_close.png b/airhub_app/packages/ali_auth/android/src/main/res/drawable/icon_close.png new file mode 100644 index 0000000..43c79a9 Binary files /dev/null and b/airhub_app/packages/ali_auth/android/src/main/res/drawable/icon_close.png differ diff --git a/airhub_app/packages/ali_auth/android/src/main/res/drawable/phone.png b/airhub_app/packages/ali_auth/android/src/main/res/drawable/phone.png new file mode 100644 index 0000000..d443d39 Binary files /dev/null and b/airhub_app/packages/ali_auth/android/src/main/res/drawable/phone.png differ diff --git a/airhub_app/packages/ali_auth/android/src/main/res/layout/activity_custom_web.xml b/airhub_app/packages/ali_auth/android/src/main/res/layout/activity_custom_web.xml new file mode 100644 index 0000000..80c06ce --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/layout/activity_custom_web.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/src/main/res/layout/authsdk_widget_custom_layout.xml b/airhub_app/packages/ali_auth/android/src/main/res/layout/authsdk_widget_custom_layout.xml new file mode 100644 index 0000000..137316e --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/layout/authsdk_widget_custom_layout.xml @@ -0,0 +1,6 @@ + + + diff --git a/airhub_app/packages/ali_auth/android/src/main/res/layout/custom_full_port.xml b/airhub_app/packages/ali_auth/android/src/main/res/layout/custom_full_port.xml new file mode 100644 index 0000000..ac2ed3d --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/layout/custom_full_port.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/android/src/main/res/values/colors.xml b/airhub_app/packages/ali_auth/android/src/main/res/values/colors.xml new file mode 100644 index 0000000..9210a59 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #3F51B5 + #303F9F + #FF4081 + #FF000000 + #616161 + #ffffffff + #ff026ED2 + diff --git a/airhub_app/packages/ali_auth/android/src/main/res/values/strings.xml b/airhub_app/packages/ali_auth/android/src/main/res/values/strings.xml new file mode 100644 index 0000000..33669a3 --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + 号码认证Demo + + 切换到短信登录页面 + 自定义标题 + 自定义Slogan + 同意服务条款才可以登录 + diff --git a/airhub_app/packages/ali_auth/android/src/main/res/xml/network_security_config.xml b/airhub_app/packages/ali_auth/android/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..9cb8c6c --- /dev/null +++ b/airhub_app/packages/ali_auth/android/src/main/res/xml/network_security_config.xml @@ -0,0 +1,10 @@ + + + + enrichgw.10010.com + onekey.cmpassport.com + 140.205.57.248 + gw.api.tbsandbox.com + m.aliyun.com + + \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/ios/.gitignore b/airhub_app/packages/ali_auth/ios/.gitignore new file mode 100644 index 0000000..0c88507 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/airhub_app/packages/ali_auth/ios/Assets/.gitkeep b/airhub_app/packages/ali_auth/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@2x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@2x.png new file mode 100644 index 0000000..db2ab5b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@3x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@3x.png new file mode 100644 index 0000000..61e1dd6 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_check@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@2x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@2x.png new file mode 100644 index 0000000..96f784b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@3x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@3x.png new file mode 100644 index 0000000..d4f2dad Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_close_gray@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@2x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@2x.png new file mode 100755 index 0000000..c97068b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@3x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@3x.png new file mode 100755 index 0000000..4ee63e0 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_gray@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@2x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@2x.png new file mode 100755 index 0000000..4faed82 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@3x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@3x.png new file mode 100755 index 0000000..e4e17e4 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_nav_back_light@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@2x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@2x.png new file mode 100644 index 0000000..e200be8 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@3x.png b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@3x.png new file mode 100644 index 0000000..bdc8daa Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/ATAuthSDK.bundle/icon_uncheck@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/Assets/taobao.png b/airhub_app/packages/ali_auth/ios/Assets/taobao.png new file mode 100644 index 0000000..16fa78d Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/Assets/taobao.png differ diff --git a/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.h b/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.h new file mode 100644 index 0000000..2f53339 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.h @@ -0,0 +1,43 @@ +// +// AliAuthEnum.好的.h +// Pods +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// +#import + + +@interface AliAuthEnum : NSObject ++ (NSDictionary *)initData; ++ (NSDictionary *)keyPair; +@end +// FOUNDATION_EXPORT NSString * const StatusAll; + +typedef NS_ENUM(NSUInteger, PNSBuildModelStyle) { + //全屏 + PNSBuildModelStylePortrait, + PNSBuildModelStyleLandscape, + //PNSBuildModelStyleAutorotate, + + //弹窗 + PNSBuildModelStyleAlertPortrait, + PNSBuildModelStyleAlertLandscape, + // PNSBuildModelStyleAlertAutorotate, + + //底部弹窗 + PNSBuildModelStyleSheetPortrait, + + //DIY 动画 + PNSDIYAlertPortraitFade, + PNSDIYAlertPortraitDropDown, + // PNSDIYAlertPortraitBounce, +// PNSDIYPortraitFade, +// PNSDIYPortraitScale, + + PNSBuildModelStyleGifBackground, + //other + PNSBuildModelStyleVideoBackground, + PNSBuildModelStylePicBackground, +}; + diff --git a/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.m b/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.m new file mode 100644 index 0000000..38ce851 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/AliAuthEnum.m @@ -0,0 +1,129 @@ +// +// AliAuthEnum.m +// ali_auth +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "AliAuthEnum.h" + +@implementation AliAuthEnum : NSObject +static NSDictionary * StatusAll = nil; ++ (NSDictionary *)initData { + if (StatusAll == nil) { + StatusAll = @{ + @"500000": @"参数获取异常!", + @"500001": @"密钥不能为空, 请先检查密钥是否设置!", + @"500002": @"校验成功,可进行一键登录!", + @"500003": @"该接口为延时登录接口,请先初始化后再次调用该接口!", + @"500004": @"插件启动监听成功, 当前SDK版本: %@", + @"600000": @"获取token成功!", + @"600001": @"唤起授权页成功!", + @"600002": @"唤起授权⻚失败!建议切换到其他登录⽅式", + @"600004": @"获取运营商配置信息失败!创建⼯单联系⼯程师", + @"600005": @"⼿机终端不安全!切换到其他登录⽅式", + @"600007": @"未检测到sim卡!⽤户检查 SIM 卡后,", + @"600008": @"蜂窝⽹络未开启!⽤户开启移动⽹络后重试", + @"600009": @"⽆法判断运营商! 创建⼯单联系⼯程师", + @"600010": @"未知异常创建!⼯单联系⼯程师", + @"600011": @"获取token失败!切换到其他登录⽅式", + @"600012": @"预取号失败!", + @"600013": @"运营商维护升级!该功能不可⽤创建⼯单联系⼯程师", + @"600014": @"运营商维护升级!该功能已达最⼤调⽤次创建⼯单联系⼯程师", + @"600015": @"接⼝超时!切换到其他登录⽅式", + @"600016": @"预取号成功!", + @"600017": @"AppID、Appkey解析失败! 秘钥未设置或者设置错误,请先检查秘钥信息,如果⽆法解决问题创建⼯单联系⼯程师", + @"600018": @"请先初始化SDK!", + @"600019": @"用户点击第三方按钮!", + @"600021": @"点击登录时检测到运营商已切换!⽤户退出授权⻚,重新登录", + @"600023": @"加载⾃定义控件异常!检查⾃定义控件添加是否正确", + @"600024": @"终端环境检查⽀持认证", + @"600025": @"终端检测参数错误检查传⼊参数类型与范围是否正确", + @"600026": @"授权⻚已加载时不允许调⽤加速或预取号接⼝检查是否有授权⻚拉起后,去调⽤preLogin或者accelerateAuthPage的接⼝,该⾏为不允许", + @"700000": @"用户取消登录", + @"700001": @"用户切换其他登录方式", + @"700002": @"用户点击登录按钮", + @"700003": @"用户勾选协议选项", + @"700004": @"用户点击协议富文本", + @"700006": @"点击一键登录拉起授权页二次弹窗", + @"700007": @"隐私协议二次弹窗关闭", + @"700008": @"点击隐私协议二次弹窗上同意并继续", + @"700009": @"点击隐私协议二次弹窗上的协议富文本文字", + @"700010": @"中断页面消失时(suspendDisMissVC设置为YES时),点击左上角返回按钮透出的状态码", + @"700020": @"授权页已销毁!", + }; + } + return StatusAll; +} + ++ (NSDictionary *)keyPair { + return @{ + /// 1、状态栏 + @"isStatusBarHidden": @"prefersStatusBarHidden", + @"lightColor": @"preferredStatusBarStyle", + /// 2、导航栏 + @"navHidden": @"navIsHidden", + @"backgroundColor": @"alertContentViewColor", + @"navReturnImgPath": @"navBackImage", + @"navReturnHidden": @"hideNavBackItem", + /// 3、Logo + @"logoImgPath": @"logoImage", + @"logoHidden": @"logoIsHidden", + /// 4、Slogan + @"sloganHidden": @"sloganIsHidden", + /// 5、掩码栏 + @"numberSize": @"numberFont", + /// 6、登录按钮 + /// 7、切换到其他方式 + @"switchAccHidden": @"changeBtnIsHidden", + @"switchAccText": @"changeBtnTitle", + /// 9、协议栏 + @"checkboxHidden": @"checkBoxIsHidden", + @"checkBoxHeight": @"checkBoxWH", + @"privacyState": @"checkBoxIsChecked", + @"privacyTextSize": @"privacyFont", + @"protocolGravity": @"privacyAlignment", + @"protocolOwnOneColor": @"privacyOneColor", + @"protocolOwnTwoColor": @"privacyTwoColor", + @"protocolOwnThreeColor": @"privacyThreeColor", + @"protocolOwnColor": @"privacyOperatorColor", + @"privacyBefore": @"privacyPreText", + @"privacyEnd": @"privacySufText", + @"vendorPrivacyPrefix": @"privacyOperatorPreText", + @"vendorPrivacySuffix": @"privacyOperatorSufText", + @"privacyConectTexts": @"privacyConectTexts", + /// 协议详情 + @"webNavColor": @"privacyNavColor", + @"webNavTextColor": @"privacyNavTitleColor", + @"webNavTextSize": @"privacyNavTitleFont", + @"webNavReturnImgPath": @"privacyNavBackImage", + /// + @"pageBackgroundPath": @"backgroundImage", + /// 弹窗 + @"dialogCornerRadiusArray": @"alertCornerRadiusArray", + @"alertCloseImagePath": @"alertCloseImage", + /// 二次 + @"privacyAlertIsNeedAutoLogin": @"privacyAlertIsNeedAutoLogin", + @"privacyAlertTitleTextSize": @"privacyAlertTitleFont", + @"privacyAlertContentVerticalMargin": @"privacyAlertLineSpaceDp", + @"privacyAlertBtnText": @"privacyAlertBtnContent", + @"privacyAlertBtnTextSize": @"privacyAlertButtonFont", + @"privacyAlertContentTextSize": @"privacyAlertContentFont", + @"privacyAlertBtnBackgroundImgPath": @"privacyAlertBtnBackgroundImages", + @"privacyAlertCloseImagPath": @"privacyAlertCloseButtonImage", + @"privacyAlertCloseBtnShow": @"privacyAlertCloseButtonIsNeedShow", + @"privacyAlertBefore": @"privacyAlertPreText", + @"privacyAlertEnd": @"privacyAlertSufText", + @"privacyAlertProtocolNameUseUnderLine": @"privacyAlertContentUnderline", + }; +} + +#pragma mark --枚举,消息提示框的位置 +typedef NS_ENUM(NSInteger, MBProgressHUBPosition) { + MBProgressHUBPositionTop, //头部 + MBProgressHUBPositionCenter, //中心 + MBProgressHUBPositionBottom //底部 +}; +@end + diff --git a/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.h b/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.h new file mode 100644 index 0000000..96d01e9 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.h @@ -0,0 +1,5 @@ +#import + + +@interface AliAuthPlugin : NSObject +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.m b/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.m new file mode 100644 index 0000000..2fe0dc6 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.m @@ -0,0 +1,683 @@ +#import "AliAuthPlugin.h" +#import + +#import "AliAuthEnum.h" +#import "MJExtension.h" +#import +//#import "MBProgressHUD.h" +#import "PNSBuildModelUtils.h" +#import "NSDictionary+Utils.h" +#import "UIColor+Hex.h" +#import + +#define TX_SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height +#define TX_SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width + +bool bool_true = true; +bool bool_false = false; + +// 打印长度比较大的字符串 +//#define NSLog(format,...) printf("%s",[[NSString stringWithFormat:(format), ##__VA_ARGS__] UTF8String]) + +/// 添加ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding 取消 +/// authorizationController.delegate = self; authorizationController.presentationContextProvider = self; 警告信息 +@interface AliAuthPlugin() +@property (nonatomic, strong) UINavigationController *navigationController; +@end + +@implementation AliAuthPlugin{ + FlutterEventSink _eventSink; + FlutterResult _result; + FlutterMethodCall * _callData; + TXCustomModel * _model; + Boolean _isChecked; + Boolean _isHideToast; +} ++ (void)registerWithRegistrar:(NSObject*)registrar { + AliAuthPlugin* instance = [[AliAuthPlugin alloc] init]; + + FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"ali_auth" binaryMessenger: [registrar messenger]]; + FlutterEventChannel* chargingChannel = [FlutterEventChannel eventChannelWithName:@"ali_auth/event" binaryMessenger: [registrar messenger]]; + + [chargingChannel setStreamHandler: instance]; + [registrar addMethodCallDelegate:instance channel: channel]; + //为了让手机安装demo弹出使用网络权限弹出框 + [[AliAuthPlugin alloc] httpAuthority]; + +} + +#pragma mark - IOS 主动发送通知让 flutter调用监听 eventChannel start +- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { + if (_eventSink == nil) { + _eventSink = eventSink; + /** 返回初始化状态 */ + NSString *version = [[TXCommonHandler sharedInstance] getVersion]; + + NSDictionary *dict = @{ @"resultCode": @"500004", @"msg": version }; + [self showResultMsg: dict msg:version]; + } + return nil; +} + +- (FlutterError*)onCancelWithArguments:(id)arguments { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + _eventSink = nil; + return nil; +} + +// eventChannel end +#pragma mark - 测试联网阿里授权必须 +-(void)httpAuthority{ + NSURL *url = [NSURL URLWithString:@"https://www.baidu.com/"];//此处修改为自己公司的服务器地址 + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + if (error == nil) { + // NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + NSLog(@"联网成功!"); + } else { + NSLog(@"联网失败!"); + } + }]; + + [dataTask resume]; +} + +#pragma mark - flutter调用 oc eventChannel start +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + // SDK 初始化 + _callData = call; + _result = result; + if ([@"getPlatformVersion" isEqualToString:call.method]) { + result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + } + else if ([@"getCurrentCarrierName" isEqualToString:call.method]) { + // 获取当前上网卡运营商名称,比如中国移动、中国电信、中国联通 + result([TXCommonUtils getCurrentCarrierName]); + } + // 初始化SDK + else if ([@"initSdk" isEqualToString:call.method]) { + _isHideToast = [call.arguments boolValueForKey: @"isHideToast" defaultValue: NO]; + if (_eventSink == nil) { + result(@{ @"code": @"500001", @"msg": @"请先对插件进行监听!" }); + } else { + [self initSdk]; + } + } + // 延时登录获取非延时登录 + else if ([@"login" isEqualToString:call.method]) { + if(_model == nil){ + NSDictionary *dict = @{ @"resultCode": @"500003" }; + [self showResult: dict]; + return; + } + self->_isChecked = [_callData.arguments boolValueForKey: @"privacyState" defaultValue: NO]; + [self loginWithModel: _model complete:^{}]; + } + else if ([@"checkEnvAvailable" isEqualToString:call.method]) { + [self checkVerifyEnable:call result:result]; + } + else if ([@"queryCheckBoxIsChecked" isEqualToString:call.method]) { + BOOL status = [[TXCommonHandler sharedInstance] queryCheckBoxIsChecked]; + result(@(status)); + } + else if ([@"setCheckboxIsChecked" isEqualToString:call.method]) { + [[TXCommonHandler sharedInstance] setCheckboxIsChecked:YES]; + result(@(bool_true)); + } + else if ([@"checkCellularDataEnable" isEqualToString:call.method]) { + [self checkCellularDataEnable:call result:result]; + } + else if ([@"preLogin" isEqualToString:call.method]) { + [self getPreLogin:call result:result]; + } + else if ([@"quitPage" isEqualToString:call.method]) { + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated:YES complete:nil]; + } + else if ([@"hideLoading" isEqualToString:call.method]) { + [[TXCommonHandler sharedInstance] hideLoginLoading]; + } + else if ([@"appleLogin" isEqualToString:call.method]) { + [self handleAuthorizationAppleIDButtonPress:call result:result]; + } + else if ([@"openPage" isEqualToString:call.method]) { + // 1.初始化flutter控制器,并指定路由 “home”,flutter中根据该路由标识显示对应的界面 + FlutterViewController* flutterViewController = [ + [FlutterViewController alloc] initWithProject:nil + initialRoute:[call.arguments stringValueForKey: @"pageRoute" defaultValue: @"/"] + nibName:nil + bundle:nil + ]; + // 2. 跳转 + flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen; + [[self findCurrentViewController] presentViewController:flutterViewController animated: YES completion:nil]; + } + else { + result(FlutterMethodNotImplemented); + } +} + +#pragma mark - 初始化SDK以及相关布局 +- (void)initSdk { + NSDictionary *dic = _callData.arguments; + // _model = [TXCustomModel mj_objectWithKeyValues: dic]; + if ([[dic stringValueForKey: @"iosSk" defaultValue: @""] isEqualToString:@""]) { + NSDictionary *dict = @{ @"resultCode": @"500000" }; + [self showResult: dict]; + } + else { + NSString *secret = [dic stringValueForKey: @"iosSk" defaultValue: @""]; + /** 不管是否延时登录都需要,先初始化model */ + _model = [PNSBuildModelUtils buildModelWithStyle: dic target:self selector:@selector(btnClick:)]; + //1. 初始化sdk,设置secret + [[TXCommonHandler sharedInstance] setAuthSDKInfo:secret complete:^(NSDictionary * _Nonnull resultDic) { + //2. 调用check接口检查及准备接口调用环境 + [[TXCommonHandler sharedInstance] checkEnvAvailableWithAuthType:PNSAuthTypeLoginToken complete:^(NSDictionary * _Nullable checkDic) { + if ([PNSCodeSuccess isEqualToString:[checkDic objectForKey:@"resultCode"]] == YES) { + NSDictionary *dict = @{ @"resultCode": @"600024" }; + [self showResult: dict]; + //3. 调用取号接口,加速授权页的弹起 + [[TXCommonHandler sharedInstance] accelerateLoginPageWithTimeout: 5.0 complete:^(NSDictionary * _Nonnull resultDic) { + //4. 预取号成功后判断是否延时登录,否则立即登录 + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == YES) { + if (![dic boolValueForKey: @"isDelay" defaultValue: NO]) { + [self loginWithModel: self->_model complete:^{}]; + } + NSString *carrierNam = [TXCommonUtils getCurrentCarrierName]; + NSDictionary *acceleDict = @{ @"resultCode": @"600016", @"data": carrierNam }; + [self showResult: acceleDict]; + return; + } + [self showResult: resultDic]; + }]; + } else { + NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:checkDic]; + [result setValue:@(bool_false) forKey: @"token"]; + [self showResult: result]; + } + }]; + }]; + } +} + +/** SDK 判断网络环境是否支持 */ +- (void)checkVerifyEnable:(FlutterMethodCall*)call result:(FlutterResult)result { + __weak typeof(self) weakSelf = self; + + [[TXCommonHandler sharedInstance] checkEnvAvailableWithAuthType:PNSAuthTypeLoginToken complete:^(NSDictionary * _Nullable resultDic) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setValue: [resultDic objectForKey:@"resultCode"] forKey: @"code"]; + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == NO) { + [weakSelf showResult:resultDic]; + [dict setValue: @(bool_false) forKey: @"data"]; + } else { + [[TXCommonHandler sharedInstance] accelerateLoginPageWithTimeout:5.0 complete:^(NSDictionary * _Nonnull resultDic) { + /// NSLog(@"为后面授权页拉起加个速,加速结果:%@", resultDic); + }]; + // 中国移动支持2G/3G/4G、中国联通支持3G/4G、中国电信支持4G。 2G网络下认证失败率较高 + // WiFi,4G,3G,2G,NoInternet等 +// NSString *networktype = [[TXCommonUtils init] getNetworktype]; +// // 中国移动,中国联通,中国电信等 +// NSString *carrierName = [[TXCommonUtils init] getCurrentCarrierName]; +// if( [carrierName isEqual:(@"中国移动")] && [networktype isEqual:(@"2G")] && [networktype isEqual:(@"3G")] && [networktype isEqual:(@"4G")] ){ +// result(@(bool_true)); +// } else if( [carrierName isEqual:(@"中国联通")] && [networktype isEqual:(@"3G")] && [networktype isEqual:(@"4G")]){ +// result(@(bool_true)); +// } else if( [carrierName isEqual:(@"中国电信")] && [networktype isEqual:(@"4G")]){ +// result(@(bool_true)); +// } +// result(@(bool_false)); + [dict setValue: @"终端环境检查⽀持认证" forKey: @"msg"]; + [dict setValue: @"600024" forKey: @"code"]; + [dict setValue: @(bool_true) forKey: @"data"]; + } + [self resultData: dict]; + }]; +} + +/** 检测是否开启蜂窝网络 */ +- (void)checkCellularDataEnable:(FlutterMethodCall*)call result:(FlutterResult)result { + bool status = [TXCommonUtils checkDeviceCellularDataEnable]; + NSDictionary *dict = @{ + @"code": @"1", + @"msg" : @"蜂窝网络检测", + @"data" : @(status) + }; + result(dict); +} + + +#pragma mark - action 选中第三方按钮时回调 +- (void)btnClick: (UIGestureRecognizer *) sender { + UIButton *view = (UIButton *)sender; + NSInteger index = view.tag; + NSDictionary *dict = @{ + @"code": @"700005", + @"msg" : @"点击第三方登录按钮", + @"data" : [NSNumber numberWithInteger: index] + }; + [self resultData: dict]; + if (!self->_isChecked && !self->_isHideToast) { + NSDictionary *dic = self -> _callData.arguments; + // [self showToast: [dic stringValueForKey: @"toastText" defaultValue: @"请先阅读用户协议"]]; + } else { + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated: YES complete:^(void) {}]; + } +} + +// 一键登录预取号 +- (void)getPreLogin:(FlutterMethodCall*)call result:(FlutterResult)result{ + [self accelerateLogin:_model call:call result:result complete:^{}]; +} + +/** + * 函数名: accelerateLoginPageWithTimeout + * @brief 加速一键登录授权页弹起,防止调用 getLoginTokenWithTimeout:controller:model:complete: 等待弹起授权页时间过长 + * @param timeout:接口超时时间,单位s,默认3.0s,值为0.0时采用默认超时时间 + * @param complete 结果异步回调,成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode +*/ +#pragma mark - action 一键登录预取号 +- (void)accelerateLogin:(TXCustomModel *)model call:(FlutterMethodCall*)call result:(FlutterResult)result complete:(void (^)(void))completion { + float timeout = 5.0; // self.tf_timeout.text.floatValue; + __weak typeof(self) weakSelf = self; + + //1. 调用check接口检查及准备接口调用环境 + [[TXCommonHandler sharedInstance] checkEnvAvailableWithAuthType:PNSAuthTypeLoginToken complete:^(NSDictionary * _Nullable resultDic) { + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == NO) { + [weakSelf showResult:resultDic]; + return; + } + NSDictionary *dict = @{ @"resultCode": @"600024" }; + [self showResult: dict]; + + //2. 调用取号接口,加速授权页的弹起 + [[TXCommonHandler sharedInstance] accelerateLoginPageWithTimeout:timeout complete:^(NSDictionary * _Nonnull resultDic) { + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == NO) { + [weakSelf showResult:resultDic]; + return; + } + NSDictionary *acceleDict = @{ @"resultCode": @"600016" }; + [self showResult: acceleDict]; + }]; + }]; +} + +// 跳转Flutter混合原生view界面 +-(void)handleSingleTap:(UITapGestureRecognizer *)sender{ //获得参数 + NSLog(@"%@", @"我被点击了"); +} + +#pragma mark - action 一键登录公共方法 +- (void)loginWithModel:(TXCustomModel *)model complete:(void (^)(void))completion { + float timeout = 5.0; //self.tf_timeout.text.floatValue; + __weak typeof(self) weakSelf = self; + +// UIWindow *window = [[UIApplication sharedApplication].delegate window]; +// UIViewController * _vc = [[ViewController alloc] init]; +// window.rootViewController = _vc; + + UIViewController *_vc = [self findCurrentViewController]; + +// UIButton *pushFlutterNativePageButton = [UIButton buttonWithType:UIButtonTypeSystem]; +// pushFlutterNativePageButton.frame = CGRectMake(100, 300, 300, 100); +// [pushFlutterNativePageButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; +// [pushFlutterNativePageButton setTitle:@"跳转到Flutter混合原生view界面" forState:UIControlStateNormal]; +// [pushFlutterNativePageButton addTarget:self action:@selector(pushFlutterNativePage) forControlEvents:UIControlEventTouchUpInside]; +// [_vc.view addSubview:pushFlutterNativePageButton]; + + // 每次登录时都设置没有登录状态 + self->_isChecked = false; + + //1. 调用check接口检查及准备接口调用环境 + [[TXCommonHandler sharedInstance] checkEnvAvailableWithAuthType:PNSAuthTypeLoginToken complete:^(NSDictionary * _Nullable resultDic) { + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == NO) { + [weakSelf showResult:resultDic]; + return; + } + //2. 调用取号接口,加速授权页的弹起 + [[TXCommonHandler sharedInstance] accelerateLoginPageWithTimeout:timeout complete:^(NSDictionary * _Nonnull resultDic) { + if ([PNSCodeSuccess isEqualToString:[resultDic objectForKey:@"resultCode"]] == NO) { + [weakSelf showResult:resultDic]; + return; + } + + //3. 调用获取登录Token接口,可以立马弹起授权页 + // 关闭loading + // [MBProgressHUD hideHUDForView:_vc.view animated:YES]; + [[TXCommonHandler sharedInstance] getLoginTokenWithTimeout:timeout controller:_vc model:model complete:^(NSDictionary * _Nonnull resultDic) { + NSString *code = [resultDic objectForKey:@"resultCode"]; +// UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickAllScreen:)]; +// +// UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _vc.view.bounds.size.width, _vc.view.bounds.size.height)]; + //将手势添加到需要相应的view中去 + [[weakSelf findCurrentViewController].view hitTest:CGPointMake(_vc.view.bounds.size.width, _vc.view.bounds.size.height) withEvent:nil]; +// [[weakSelf findCurrentViewController].view addSubview:headerView]; + + // 当未勾选隐私协议时,弹出 Toast 提示 + if ([PNSCodeLoginControllerClickLoginBtn isEqualToString:code] && !self->_isChecked) { + } else if ([PNSCodeSuccess isEqualToString:code]) { + bool autoQuitPage = [self->_callData.arguments boolValueForKey: @"autoQuitPage" defaultValue: YES]; + // 登录成功后是否自动关闭页面 + if (autoQuitPage) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated:YES complete:nil]; + }); + } + } else if ([PNSCodeLoginControllerClickChangeBtn isEqualToString:code]) { + // 通过switchCheck 参数动态控制 是否需要切换其他方式时需要勾选 + NSDictionary *dic = self -> _callData.arguments; + if (!self->_isChecked && !self-> _isHideToast && [dic boolValueForKey: @"switchCheck" defaultValue: YES]) { + } else { + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated:YES complete:nil]; + } + } else if ([PNSCodeLoginControllerClickCheckBoxBtn isEqualToString:code]) { // 点击同意协议 + self->_isChecked = [[resultDic objectForKey:@"isChecked"] boolValue]; + } else if ([PNSCodeLoginControllerClickCancel isEqualToString:code]) { // 取消 + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated:YES complete:nil]; + } else if ([PNSCodeCarrierChanged isEqualToString:code]) { // 切换运营商 + [[TXCommonHandler sharedInstance] cancelLoginVCAnimated:YES complete:nil]; + } + [weakSelf showResult:resultDic]; + }]; + }]; + }]; +} + +#pragma mark - toast +- (void)showToast:(NSString*) message { +// NSDictionary *dic = _callData.arguments; +// UIView *view = [self findCurrentViewController].view; +// MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo: view animated:YES]; +// // Set the text mode to show only text. +// hud.mode = MBProgressHUDModeText; +// hud.label.text = NSLocalizedString(message, @"温馨提示"); +// // 内边剧 +// hud.margin = [dic floatValueForKey: @"toastPadding" defaultValue: 10]; +// // 文本颜色 +// hud.contentColor = [UIColor colorWithHex: [dic stringValueForKey: @"toastColor" defaultValue: @"#FFFFFF"] defaultValue: @"#FFFFFF"]; +// // 弹窗背景 +// hud.bezelView.color = [UIColor colorWithHex: [dic stringValueForKey: @"toastBackground" defaultValue: @"#000000"] defaultValue: @"#000000"]; +// // 弹窗背景样式 +// hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; +// +// CGFloat offSetY = view.bounds.size.height / 2; +// NSString* toastPosition = [dic stringValueForKey: @"toastPositionMode" defaultValue: @"bottom"]; +// if ([toastPosition isEqual: @"top"]) { +// CGFloat top = [dic floatValueForKey: @"toastMarginTop" defaultValue: 30.f]; +// offSetY = - offSetY + view.window.safeAreaInsets.top + top; +// } else if ([toastPosition isEqual: @"bottom"]) { +// CGFloat bottom = [dic floatValueForKey: @"toastMarginBottom" defaultValue: 30.f]; +// offSetY = offSetY - view.window.safeAreaInsets.bottom - bottom; +// } else if ([toastPosition isEqual: @"center"]) { +// offSetY = 0; +// } +// // 设置上下的位置 +// hud.offset = CGPointMake(0.f, offSetY); +// [hud hideAnimated:YES afterDelay: [dic floatValueForKey: @"toastDelay" defaultValue: 3]]; +} + +-(void) resultData:(NSDictionary *)dict{ + if (_eventSink != nil) { + NSMutableDictionary *mutableDict = [dict mutableCopy]; + [mutableDict setObject: self->_isChecked ? @(bool_true):@(bool_false) forKey: @"isChecked"]; + _eventSink(mutableDict); + } +} + +#pragma mark - 格式化数据utils返回数据 +- (void)showResult:(id __nullable)showResult { + // 当存在autoHideLoginLoading时需要执行关闭 +// if (![self->_callData.arguments boolValueForKey: @"autoHideLoginLoading" defaultValue: YES]) { +// dispatch_async(dispatch_get_main_queue(), ^{ +// [MBProgressHUD hideHUDForView: [self findCurrentViewController].view animated:YES]; +// }); +// } + + NSDictionary *dict = @{ + @"code": [NSString stringWithFormat: @"%@", [showResult objectForKey:@"resultCode"]], + @"msg" : [AliAuthEnum initData][[showResult objectForKey:@"resultCode"]]?:@"", + @"data" : [showResult objectForKey: @"token"]?:@"", + @"isChecked": self->_isChecked ? @(bool_true):@(bool_false) + }; + + [self resultData: dict]; + [self showResultLog: showResult]; +} + +#pragma mark - 格式化数据utils返回数据 +- (void)showResultMsg:(id __nullable)showResult msg: (NSString*)msg { + NSString *resultMsg = [NSString stringWithFormat: [AliAuthEnum initData][[showResult objectForKey:@"resultCode"]], msg]?:@""; + NSDictionary *dict = @{ + @"code": [NSString stringWithFormat: @"%@", [showResult objectForKey:@"resultCode"]], + @"msg" : resultMsg, + @"data" : [showResult objectForKey: @"token"]?:@"" + }; + + [self resultData: dict]; + [self showResultLog: showResult]; +} + +#pragma mark - 格式化数据utils统一输出日志 +- (void)showResultLog:(id __nullable)showResult { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString *desc = nil; + if ([showResult isKindOfClass:NSString.class]) { + desc = (NSString *)showResult; + } else { + desc = [showResult description]; + // if (desc != nil) { + // desc = [NSString stringWithCString:[desc cStringUsingEncoding:NSUTF8StringEncoding] encoding:NSNonLossyASCIIStringEncoding]; + // } + } + // NSLog( @"打印日志---->>%@", desc ); + }); +} + +#pragma mark - Apple授权登录 +// 处理授权 +- (void)handleAuthorizationAppleIDButtonPress:(FlutterMethodCall*)call result:(FlutterResult)result{ + NSLog(@"点击授权---开始授权"); + if (@available(iOS 13.0, *)) { + // 基于用户的Apple ID授权用户,生成用户授权请求的一种机制 + ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init]; + // 创建新的AppleID 授权请求 + ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest]; + // 在用户授权期间请求的联系信息 + appleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail]; + // 由ASAuthorizationAppleIDProvider创建的授权请求 管理授权请求的控制器 + ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest]]; + // 设置授权控制器通知授权请求的成功与失败的代理 + authorizationController.delegate = self; + // 设置提供 展示上下文的代理,在这个上下文中 系统可以展示授权界面给用户 + authorizationController.presentationContextProvider = self; + // 在控制器初始化期间启动授权流 + [authorizationController performRequests]; + } + else{ + // 处理不支持系统版本 + NSLog(@"该系统版本不可用Apple登录"); + NSDictionary *resultData = @{ + @"code": @500, + @"msg" : @"该系统版本不可用Apple登录", + @"user" : @"", + @"familyName" : @"", + @"givenName" : @"", + @"email" : @"", + @"identityTokenStr": @"", + @"authorizationCodeStr": @"" + }; + + [self resultData: resultData]; + } +} + + +#pragma mark - delegate +//@optional 授权成功地回调 +- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)){ +// NSLog(@"授权完成:::%@", authorization.credential); + NSLog(@"授权完成---开始返回数据"); + if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { + // 用户登录使用ASAuthorizationAppleIDCredential + ASAuthorizationAppleIDCredential *appleIDCredential = (ASAuthorizationAppleIDCredential *)authorization.credential; + NSString *user = appleIDCredential.user; + // 使用过授权的,可能获取不到以下三个参数 + NSString *familyName = appleIDCredential.fullName.familyName; + NSString *givenName = appleIDCredential.fullName.givenName; + NSString *email = appleIDCredential.email; + + NSData *identityToken = appleIDCredential.identityToken; + NSData *authorizationCode = appleIDCredential.authorizationCode; + + // 服务器验证需要使用的参数 + NSString *identityTokenStr = [[NSString alloc] initWithData:identityToken encoding:NSUTF8StringEncoding]; + NSString *authorizationCodeStr = [[NSString alloc] initWithData:authorizationCode encoding:NSUTF8StringEncoding]; + // NSLog(@"后台参数--%@\n\n%@", identityTokenStr, authorizationCodeStr); +// NSLog(@"后台参数identityTokenStr---%@", identityTokenStr); +// NSLog(@"后台参数authorizationCodeStr---%@", authorizationCodeStr); + + // Create an account in your system. + // For the purpose of this demo app, store the userIdentifier in the keychain. + // 需要使用钥匙串的方式保存用户的唯一信息 +// [YostarKeychain save:KEYCHAIN_IDENTIFIER(@"userIdentifier") data:user]; +// NSLog(@"user--%@", user); +// NSLog(@"familyName--%@", familyName); +// NSLog(@"givenName--%@", givenName); +// NSLog(@"email--%@", email); + + NSDictionary *resultData = @{ + @"code": @200, + @"msg" : @"获取成功", + @"user" : user, + @"familyName" : familyName != nil ? familyName : @"", + @"givenName" : givenName != nil ? givenName : @"", + @"email" : email != nil ? email : @"", + @"identityTokenStr": identityTokenStr, + @"authorizationCodeStr": authorizationCodeStr + }; + + [self resultData: resultData]; + }else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]){ + // 这个获取的是iCloud记录的账号密码,需要输入框支持iOS 12 记录账号密码的新特性,如果不支持,可以忽略 + // Sign in using an existing iCloud Keychain credential. + // 用户登录使用现有的密码凭证 + ASPasswordCredential *passwordCredential = (ASPasswordCredential*)authorization.credential; + // 密码凭证对象的用户标识 用户的唯一标识 + NSString *user = passwordCredential.user; + // 密码凭证对象的密码 + NSString *password = passwordCredential.password; + NSLog(@"user--%@", user); + NSLog(@"password--%@", password); + + NSDictionary *resultData = @{ + @"code": @200, + @"msg" : @"获取成功", + @"user" : user, + @"familyName" : @"", + @"givenName" : @"", + @"email" : @"", + @"identityTokenStr": @"", + @"authorizationCodeStr": @"" + }; + + [self resultData: resultData]; + }else{ + NSLog(@"授权信息均不符"); + NSDictionary *resultData = @{ + @"code": @500, + @"msg" : @"授权信息均不符", + @"user" : @"", + @"familyName" : @"", + @"givenName" : @"", + @"email" : @"", + @"identityTokenStr": @"", + @"authorizationCodeStr": @"" + }; + + [self resultData: resultData]; + } +} + +// 授权失败的回调 +- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)){ + // Handle error. + NSLog(@"苹果登录授权失败:%@", error); + NSString *errorMsg = nil; + switch (error.code) { + case ASAuthorizationErrorCanceled: + errorMsg = @"用户取消了授权请求"; + break; + case ASAuthorizationErrorFailed: + errorMsg = @"授权请求失败"; + break; + case ASAuthorizationErrorInvalidResponse: + errorMsg = @"授权请求响应无效"; + break; + case ASAuthorizationErrorNotHandled: + errorMsg = @"未能处理授权请求"; + break; + case ASAuthorizationErrorUnknown: + errorMsg = @"授权请求失败未知原因"; + break; + + default: + break; + } + + NSLog(@"%@", errorMsg); + NSDictionary *resultData = @{ + @"code": @500, + @"msg" : errorMsg, + @"user" : @"", + @"familyName" : @"", + @"givenName" : @"", + @"email" : @"", + @"identityTokenStr": @"", + @"authorizationCodeStr": @"" + }; + + [self resultData: resultData]; +} + +#pragma mark - 告诉代理应该在哪个window 展示内容给用户 +- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)){ + NSLog(@"88888888888"); + // 返回window + return [UIApplication sharedApplication].windows.lastObject; +} + +#pragma mark - 获取到跟视图 +- (UIViewController *)getRootViewController { + UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; + return window.rootViewController; +} + +#pragma mark ======在view上添加UIViewController======== +- (UIViewController *)findCurrentViewController{ + UIWindow *window = [[UIApplication sharedApplication].delegate window]; + +// UIViewController * vc = [[ViewController alloc] init]; +// window.rootViewController = vc; +// UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickAllScreen:)]; +// [window addGestureRecognizer:singleTap]; + UIViewController *topViewController = [window rootViewController]; + while (true) { + if (topViewController.presentedViewController) { + topViewController = topViewController.presentedViewController; + } else if ([topViewController isKindOfClass:[UINavigationController class]] && [(UINavigationController*)topViewController topViewController]) { + topViewController = [(UINavigationController *)topViewController topViewController]; + } else if ([topViewController isKindOfClass:[UITabBarController class]]) { + UITabBarController *tab = (UITabBarController *)topViewController; + topViewController = tab.selectedViewController; + } else { + break; + } + } + return topViewController; +} + +- (void) clickAllScreen:(UITapGestureRecognizer *) recognizer { + NSLog(@"点击事件屏蔽"); +} + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.swift b/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.swift deleted file mode 100644 index fb965bd..0000000 --- a/airhub_app/packages/ali_auth/ios/Classes/AliAuthPlugin.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Flutter - -public class AliAuthPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "ali_auth", binaryMessenger: registrar.messenger()) - let instance = AliAuthPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result(FlutterMethodNotImplemented) - } -} diff --git a/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.h b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.h new file mode 100644 index 0000000..321b448 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.h @@ -0,0 +1,17 @@ +// +// PNSBaseNavigationController.h +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PNSBaseNavigationController : UINavigationController + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.m b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.m new file mode 100644 index 0000000..b9e283f --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseNavigationController.m @@ -0,0 +1,29 @@ +// +// PNSBaseNavigationController.m +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "PNSBaseNavigationController.h" + +@interface PNSBaseNavigationController () + +@end + +@implementation PNSBaseNavigationController + +- (BOOL)shouldAutorotate { + return [[self.viewControllers lastObject] shouldAutorotate]; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return [[self.viewControllers lastObject] supportedInterfaceOrientations]; +} + +//- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { +// return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation]; +//} + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.h b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.h new file mode 100644 index 0000000..fbc7c66 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.h @@ -0,0 +1,37 @@ +// +// PNSBaseViewController.h +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PNSBaseViewController : UIViewController + +/** + * 负责初始化和设置controller里面的view,也就是self.view的subView。目的在于分类代码,所以与view初始化的相关代码都写在这里。 + * + * @warning initSubviews只负责subviews的init,不负责布局。布局相关的代码应该写在 viewDidLayoutSubviews + */ +- (void)initSubviews NS_REQUIRES_SUPER; + +/** + * 负责布局controller里面的view,也就是self.view的subView。目的在于分类代码,所以与view初始化的相关代码都写在这里。 + * + * @warning setLayoutSubviews只负责布局subviews的约束 + */ +- (void)setLayoutSubviews; + +/// 是否需要将状态栏改为浅色文字,默认为浅文字色 +- (BOOL)shouldSetStatusBarStyleLight; + +/// 是否影藏电池栏 (默认不影藏) +- (BOOL)shouldHiddenStatusBar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.m b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.m new file mode 100644 index 0000000..d7a1231 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Base/PNSBaseViewController.m @@ -0,0 +1,77 @@ +// +// PNSBaseViewController.m +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "PNSBaseViewController.h" + +@interface PNSBaseViewController () + +@end + +@implementation PNSBaseViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + [self initSubviews]; + [self setLayoutSubviews]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; +} + +#pragma mark - 状态栏 + +- (BOOL)prefersStatusBarHidden { + return NO; +} + +- (UIStatusBarStyle)preferredStatusBarStyle { + return UIStatusBarStyleDefault; +} + +- (BOOL)shouldSetStatusBarStyleLight { + return YES; +} + +- (BOOL)shouldHiddenStatusBar { + return NO; +} + +#pragma mark - UI +- (void)initSubviews { + // 子类重写 +} + +- (void)setLayoutSubviews { + //子类重新 +} + +#pragma mark - 屏幕方向(让设备支持旋转, 但是只支持竖屏, 这样防止从横屏过来导致APP没法旋转成竖屏) +// 是否支持设备自动旋转 +- (BOOL)shouldAutorotate { + return YES; +} + +// 支持竖屏显示 +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskAllButUpsideDown; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + NSLog(@"[%@,%@]===已被释放",NSStringFromClass([self class]), self.navigationItem.title ?: self.title); +} + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.h b/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.h new file mode 100644 index 0000000..0d5a895 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.h @@ -0,0 +1,21 @@ +// +// NSDictionaryUtils.h +// UZEngine +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import +#import + +@interface NSDictionary (Utils) +- (NSInteger)integerValueForKey:(NSString *)key defaultValue:(NSInteger)defaultValue; +- (int)intValueForKey:(NSString *)key defaultValue:(int)defaultValue; +- (long long)longlongValueForKey:(NSString *)key defaultValue:(long long)defaultValue; +- (float)floatValueForKey:(NSString *)key defaultValue:(float)defaultValue; +- (BOOL)boolValueForKey:(NSString *)key defaultValue:(BOOL)defaultValue; +- (NSString *)stringValueForKey:(NSString *)key defaultValue:(NSString *)defaultValue; +- (NSArray *)arrayValueForKey:(NSString *)key defaultValue:(NSArray *)defaultValue; +- (NSDictionary *)dictValueForKey:(NSString *)key defaultValue:(NSDictionary *)defaultValue; +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.m b/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.m new file mode 100644 index 0000000..d5414be --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/NSDictionary+Utils.m @@ -0,0 +1,124 @@ +// +// NSDictionary+Utils.m +// BCPay +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "NSDictionary+Utils.h" + +@implementation NSDictionary (Utils) + +/** + * @brief 如果akey找不到,返回@"" (防止出现nil,使程序崩溃) + * + * @param aKey 字典key值 + * + * @return 字典value + */ + +/** + * 获取字典指定的key的数值字符 + * + * @param key 需要取的键值 + * + * @return value值如果为nil或者null会返回0字符串 + */ +- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue { + if ([self valueForKeyIsNumber:key]) { + return [[self valueForKey:key] integerValue]; + } + return defaultValue; +} +- (int)intValueForKey:(NSString *)key defaultValue:(int)defaultValue{ + if ([self valueForKeyIsNumber:key]) { + return [[self valueForKey:key] intValue]; + } + return defaultValue; +} +- (long long)longlongValueForKey:(NSString*)key defaultValue:(long long)defaultValue { + if ([self valueForKeyIsNumber:key]) { + return [[self valueForKey:key] longLongValue]; + } + return defaultValue; +} +- (float)floatValueForKey:(NSString*)key defaultValue:(float)defaultValue { + if ([self valueForKeyIsNumber:key]) { + return [[self valueForKey:key] floatValue]; + } + return defaultValue; +} + +- (BOOL)boolValueForKey:(NSString*)key defaultValue:(BOOL)defaultValue { + if ([self valueForKeyIsNumber:key]) { + return [[self valueForKey:key] boolValue]; + } + return defaultValue; +} + +- (NSString *)stringValueForKey:(NSString*)key defaultValue:(NSString*)defaultValue { + if ([self valueForKeyIsString:key]) { + return [self valueForKey:key]; + } + return defaultValue; +} + +- (NSArray *)arrayValueForKey:(NSString *)key defaultValue:(NSArray *)defaultValue { + if ([self valueForKeyIsArray:key]) { + return [self valueForKey:key]; + } + return defaultValue; +} + +- (NSDictionary *)dictValueForKey:(NSString *)key defaultValue:(NSDictionary *)defaultValue { + if ([self valueForKeyIsNSDictionary:key]) { + return [self valueForKey:key]; + } + return defaultValue; +} + +///** +// * @brief 替换 为空 +// * +// * @param aKey 字典key值 +// * +// * @return 字典value +// */ +// +//- (NSString *)replaceNBSPforKey:(id)aKey { +// NSString *value = [self objectForKey:aKey]; +// +// if (!value) { +// value = @""; +// } +// NSString* str = [value stringByReplacingOccurrencesOfString:@" " withString:@" "] ; +// +// return [NSString stringWithFormat:@"%@",str]; +//} + +- (BOOL)valueForKeyIsArray:(NSString *)key { + return [self valueForKeyIsType:key type:[NSArray class]]; +} + +- (BOOL)valueForKeyIsString:(NSString *)key { + return [self valueForKeyIsType:key type:[NSString class]]; +} + +- (BOOL)valueForKeyIsNumber:(NSString *)key { + return [self valueForKeyIsType:key type:[NSNumber class]]; +} + +- (BOOL)valueForKeyIsNSDictionary:(NSString *)key { + return [self valueForKeyIsType:key type:[NSDictionary class]]; +} + +- (BOOL)valueForKeyIsType:(NSString *)key type:(Class)kClass { + id value = [self objectForKey:key]; + if (value == nil || [value isKindOfClass:[NSNull class]] || ![value isKindOfClass:kClass]) { + return NO; + } + return YES; +} + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.h b/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.h new file mode 100644 index 0000000..5284969 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.h @@ -0,0 +1,22 @@ +// +// PrivacyWebViewController.h +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "PNSBaseViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PrivacyWebViewController : PNSBaseViewController + +- (instancetype)initWithUrl:(NSString *)url andUrlName:(NSString *)urlName; + +/// 是否隐藏系统的导航栏 +@property (nonatomic, assign) BOOL isHiddenNavgationBar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.m b/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.m new file mode 100644 index 0000000..1793687 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Privacy/PrivacyWebViewController.m @@ -0,0 +1,85 @@ +// +// PrivacyWebViewController.m +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import "PrivacyWebViewController.h" +#import + +@interface PrivacyWebViewController () + +@property (nonatomic, strong) WKWebView *webView; + +@property (nonatomic, strong) NSString *url; + +@property (nonatomic, copy) NSString *urlName; + +@end + +@implementation PrivacyWebViewController + +- (instancetype)initWithUrl:(NSString *)url andUrlName:(nonnull NSString *)urlName{ + self = [super init]; + if (self) { + _url = url ?: nil; + _urlName = urlName ?: nil; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.navigationItem.title = self.urlName ?: @"服务协议"; + [self.view addSubview:self.webView]; + + if (self.url) { + [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url]]]; + } +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + if (!self.isHiddenNavgationBar) { + //显示系统导航栏 + [self.navigationController setNavigationBarHidden:NO animated:YES]; + } +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + if (self.isHiddenNavgationBar) { + //隐藏系统导航栏 + [self.navigationController setNavigationBarHidden:YES animated:YES]; + } +} + + +#pragma mark - UI + +- (WKWebView *)webView { + if (!_webView) { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + WKPreferences *preference = [[WKPreferences alloc]init]; + preference.minimumFontSize = 0; + preference.javaScriptEnabled = YES; + preference.javaScriptCanOpenWindowsAutomatically = YES; + config.preferences = preference; + float y = self.navigationController.navigationBar.frame.size.height + [UIApplication sharedApplication].statusBarFrame.size.height; + _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, y, self.view.frame.size.width, self.view.frame.size.height) configuration:config]; + // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回 + _webView.allowsBackForwardNavigationGestures = YES; + _webView.navigationDelegate = self; + _webView.UIDelegate = self; + } + return _webView; +} + +- (void)initSubviews { + [super initSubviews]; + [self.view addSubview:self.webView]; +} + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.h b/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.h new file mode 100644 index 0000000..f126548 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.h @@ -0,0 +1,5 @@ +#import + +@interface CustomButton : UIButton + +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.m b/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.m new file mode 100644 index 0000000..d34e834 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/CustomButton.m @@ -0,0 +1,15 @@ +#import "CustomButton.h" + +@implementation CustomButton + + +// title的位置 +- (CGRect)titleRectForContentRect:(CGRect)contentRect { + /// CGFloat x, CGFloat y, CGFloat width, CGFloat height + return CGRectMake(0, self.bounds.size.height + 5, self.bounds.size.width, 16); +} +//背景图的位置 +- (CGRect)backgroundRectForBounds:(CGRect)bounds{ + return CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); +} +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.h b/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.h new file mode 100644 index 0000000..cd7d631 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.h @@ -0,0 +1,22 @@ +// +// PNSBuildModelUtils.h +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PNSBuildModelUtils : NSObject + ++ (TXCustomModel *)buildModelWithStyle:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.m b/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.m new file mode 100644 index 0000000..c6fbb39 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/PNSBuildModelUtils.m @@ -0,0 +1,2332 @@ +// +// PNSBuildModelUtils.m +// ATAuthSceneDemo +// +// Created by Yau的MacBook on 2022/5/19. +// Copyright © 2022 Yau的MacBook. All rights reserved. +// +#import "NSDictionary+Utils.h" + +#import "CustomButton.h" +#import "MJExtension.h" +#import "AliAuthEnum.h" +#import "PNSBuildModelUtils.h" +#import "PNSBackgroundView.h" +#import "AliAuthPlugin.h" +#import + +#define TX_SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height +#define TX_SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width +#define IS_HORIZONTAL (TX_SCREEN_WIDTH > TX_SCREEN_WIDTH) + +#define TX_Alert_NAV_BAR_HEIGHT 55.0 +#define TX_Alert_HORIZONTAL_NAV_BAR_HEIGHT 41.0 + +//竖屏弹窗 +#define TX_Alert_Default_Left_Padding 42 +#define TX_Alert_Default_Top_Padding 115 + +/**横屏弹窗*/ +#define TX_Alert_Horizontal_Default_Left_Padding 80.0 + +@implementation PNSBuildModelUtils + ++ (TXCustomModel *)buildModelWithStyle:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = nil; + /// 页面类型 + PNSBuildModelStyle style = [dict intValueForKey: @"pageType" defaultValue: 0]; + model = [self buildModelOption:dict target:target style:style selector:selector]; + return model; +} + +#pragma mark - 全屏-竖屏 ++ (TXCustomModel *)buildFullScreenPortraitModel:(NSDictionary *)viewConfig + target:(id)target + selector:(SEL)selector{ + TXCustomModel *model = [[TXCustomModel alloc] init]; + + // JSON -> TXCustomModel + // TXCustomModel *modelConfig = [TXCustomModel mj_objectWithKeyValues:viewConfig]; + + // 1状态栏START + model.prefersStatusBarHidden = [viewConfig boolValueForKey: @"isStatusBarHidden" defaultValue: NO]; + bool isLightColor = [viewConfig boolValueForKey: @"lightColor" defaultValue: NO]; + model.preferredStatusBarStyle = isLightColor ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; + // 1状态栏ENd + + /// 导航设置 + model.navIsHidden = [viewConfig boolValueForKey: @"navHidden" defaultValue: NO]; + model.navColor = [self getColor: [viewConfig stringValueForKey: @"navColor" defaultValue: @"#3971fe"]]; + + model.navTitle = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"navText" defaultValue: @"一键登录"] + attributes: @{ + NSForegroundColorAttributeName: UIColor.whiteColor, + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"navTextSize" defaultValue: 20.0]] + } + ]; + + /// 返回按钮 START + bool isHiddenNavBack = [viewConfig boolValueForKey: @"navReturnHidden" defaultValue: NO]; + model.hideNavBackItem = isHiddenNavBack; + /// 动态读取assets文件夹下的资源 + UIImage * navBackImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"navReturnImgPath" defaultValue: nil]]; + model.navBackImage = navBackImage?:[UIImage imageNamed:@"icon_close_light"]; + + if (!isHiddenNavBack) { + /// 自定义返回按钮 + model.navBackButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + UIImageView *imageView = [[UIImageView alloc]init]; + imageView.image = navBackImage; + imageView.frame = CGRectMake( + CGRectGetMinX(frame), + CGRectGetMaxY(frame), + CGRectGetWidth(frame), + CGRectGetHeight(frame) + ); + frame.origin.y = [viewConfig floatValueForKey: @"navReturnOffsetY" defaultValue: 5]; + frame.origin.x = [viewConfig floatValueForKey: @"navReturnOffsetX" defaultValue: 15]; + + frame.size.width = [viewConfig floatValueForKey: @"navReturnImgWidth" defaultValue: 40]; + frame.size.height = [viewConfig floatValueForKey: @"navReturnImgHeight" defaultValue: 40]; + return frame; + }; + } + /// 返回按钮 END + + /// 右侧按钮布局设置 + // UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeSystem]; + // [rightBtn setTitle:@"更多" forState:UIControlStateNormal]; + // model.navMoreView = rightBtn; + + /// 协议页面导航设置 + model.privacyNavColor = [self getColor: [viewConfig stringValueForKey: @"webNavColor" defaultValue: @"#000"]]; + UIImage * privacyNavBackImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"webNavReturnImgPath" defaultValue: nil]]; + if(privacyNavBackImage != nil){ + model.privacyNavBackImage = privacyNavBackImage; + } + model.privacyNavTitleFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"webNavTextSize" defaultValue: 18]]; + model.privacyNavTitleColor = [self getColor: [viewConfig stringValueForKey: @"webNavTextColor" defaultValue: @"#000"]]; + + /// logo 设置 + model.logoIsHidden = [viewConfig boolValueForKey: @"logoHidden" defaultValue: NO]; + UIImage * image = [self changeUriPathToImage: [viewConfig stringValueForKey: @"logoImgPath" defaultValue: nil]]; + if(image != nil){ + /// logo 默认水平居中 + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]; + frame.size.height = [viewConfig floatValueForKey: @"logoHeight" defaultValue: 80]; + frame.origin.y = [viewConfig floatValueForKey: @"logoOffsetY" defaultValue: screenSize.height > screenSize.width ? 30 : 15]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]) * 0.5; + return frame; + }; + model.logoImage = image; + } + + /// slogan 设置 START + model.sloganIsHidden = [viewConfig boolValueForKey: @"sloganHidden" defaultValue: NO]; + model.sloganText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"sloganText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"sloganTextColor" defaultValue: @"#555555"]], + NSFontAttributeName: [ + UIFont systemFontOfSize: [viewConfig floatValueForKey: @"sloganTextSize" defaultValue: 19] + ] + } + ]; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"sloganOffsetY" defaultValue: 20 + 80 + 20]; + return frame; + } else { + return CGRectZero; + } + }; + /// slogan 设置 END + + /// number 设置 START + model.numberColor = [self getColor: [viewConfig stringValueForKey: @"numberColor" defaultValue: @"#555"]]; + model.numberFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"numberSize" defaultValue: 17]]; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"numFieldOffsetY" defaultValue: 130 + 20 + 15]; + } else { + frame.origin.y = 15 + 80 + 15; + } + return frame; + }; + /// number 设置 END + + /// 登录按钮的设置 START + model.loginBtnText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"logBtnText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"logBtnTextColor" defaultValue: @"#ff00ff"]], + NSFontAttributeName: [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"logBtnTextSize" defaultValue: 23]] + } + ]; + /// 登录按钮背景设置 + NSArray *logBtnCustomBackgroundImagePath = [[viewConfig stringValueForKey: @"logBtnBackgroundPath" defaultValue: @","] componentsSeparatedByString:@","]; + if (logBtnCustomBackgroundImagePath.count == 3) { + // login_btn_normal + UIImage* login_btn_normal = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[0]]; + // login_btn_unable + UIImage* login_btn_unable = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[1]]; + // login_btn_press + UIImage* login_btn_press = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[2]]; + UIImage *defaultClick = [UIImage imageNamed:@"button_click"]; + UIImage *defaultUnClick = [UIImage imageNamed:@"button_unclick"]; + // fix '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]' + if ((login_btn_normal != nil && login_btn_unable != nil && login_btn_press != nil) || (defaultClick != nil && defaultUnClick != nil)) { + // 登录按钮设置 + model.loginBtnBgImgs = @[ + login_btn_normal?:defaultClick, + login_btn_unable?:defaultUnClick, + login_btn_press?:defaultClick + ]; + } + } + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]; + frame.size.height = [viewConfig floatValueForKey: @"logBtnHeight" defaultValue: 40]; + frame.origin.y = [viewConfig floatValueForKey: @"logBtnOffsetY" defaultValue: 170 + 30 + 20]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]) * 0.5; + } else { + frame.origin.y = 110 + 30 + 20; + } + return frame; + }; + /// 登录按钮的设置 END + + //model.autoHideLoginLoading = NO; + + /// 协议设置 START + NSString *protocolOneURL = [viewConfig stringValueForKey: @"protocolOneURL" defaultValue: nil]; + if (protocolOneURL != nil) { + model.privacyOne = @[ + [viewConfig stringValueForKey: @"protocolOneName" defaultValue: @"协议1"], + protocolOneURL + ]; + } + NSString *protocolTwoURL = [viewConfig stringValueForKey: @"protocolTwoURL" defaultValue: nil]; + if (protocolTwoURL != nil) { + model.privacyTwo = @[ + [viewConfig stringValueForKey: @"protocolTwoName" defaultValue: @"协议2"], + protocolTwoURL + ]; + } + NSString *protocolThreeURL = [viewConfig stringValueForKey: @"protocolThreeURL" defaultValue: nil]; + if (protocolThreeURL != nil) { + model.privacyThree = @[ + [viewConfig stringValueForKey: @"protocolThreeName" defaultValue: @"协议3"], + protocolThreeURL + ]; + } + + model.privacyAlertContentUnderline = [viewConfig boolValueForKey: @"privacyAlertProtocolNameUseUnderLine" defaultValue: NO]; + // 协议1内容颜色 + model.privacyOneColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnOneColor" defaultValue: @"#000000"]]; + // 协议2内容颜色 + model.privacyTwoColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnTwoColor" defaultValue: @"#000000"]]; + // 协议3内容颜色 + model.privacyThreeColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnThreeColor" defaultValue: @"#000000"]]; + // 运营商协议内容颜色 + model.privacyOperatorColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnColor" defaultValue: @"#000000"]]; + + // 二次协议1内容颜色 + model.privacyAlertOneColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnOneColor" defaultValue: @"#000000"]]; + // 二次协议2内容颜色 + model.privacyAlertTwoColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnTwoColor" defaultValue: @"#000000"]]; + // 二次协议3内容颜色 + model.privacyAlertThreeColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnThreeColor" defaultValue: @"#000000"]]; + // 二次运营商协议内容颜色 + model.privacyAlertOperatorColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOperatorColor" defaultValue: @"#000000"]]; + + /** 导航背景色*/ + model.privacyNavColor = [self getColor: [viewConfig stringValueForKey: @"webNavColor" defaultValue: @"#FFFFFF"]]; + /** 导航文字色 */ + model.privacyNavTitleColor = [self getColor: [viewConfig stringValueForKey: @"webNavTextColor" defaultValue: @"#000000"]]; + /** 字体大小 */ + model.privacyNavTitleFont = [UIFont fontWithName:@"PingFangSC-Regular" size: [viewConfig floatValueForKey: @"webNavTextSize" defaultValue: 12.0]]; + /** 返回按钮 */ + UIImage * webNavReturnImgPath = [self changeUriPathToImage: [viewConfig stringValueForKey: @"webNavReturnImgPath" defaultValue: nil]]; + if (webNavReturnImgPath != nil) { + model.privacyNavBackImage = webNavReturnImgPath; + } + + model.privacyAlignment = [viewConfig intValueForKey: @"protocolLayoutGravity" defaultValue: 1];; + model.privacyFont = [UIFont fontWithName:@"PingFangSC-Regular" size: [viewConfig floatValueForKey: @"privacyTextSize" defaultValue: 12.0]]; + model.privacyPreText = [viewConfig stringValueForKey: @"privacyBefore" defaultValue: @"点击一键登录并登录表示您已阅读并同意"]; + model.privacySufText = [viewConfig stringValueForKey: @"privacyEnd" defaultValue: @"思预云用户协议,隐私"]; + model.privacyOperatorPreText = [viewConfig stringValueForKey: @"vendorPrivacyPrefix" defaultValue: @"《"]; + model.privacyOperatorSufText = [viewConfig stringValueForKey: @"vendorPrivacySuffix" defaultValue: @"》"]; + + /// 协议水平垂直设置 + model.privacyFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.y = [viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1]; + } + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.x = [viewConfig floatValueForKey: @"privacyOffsetX" defaultValue: -1]; + } + return frame; + }; + // 0.2.3 - 1.12.4新增 + model.privacyVCIsCustomized = [viewConfig boolValueForKey: @"privacyVCIsCustomized" defaultValue: NO]; + // 是否使用授权页协议动画 + bool isPrivacyAnimation = [viewConfig boolValueForKey: @"isPrivacyAnimation" defaultValue: NO]; + if (isPrivacyAnimation) { + CAKeyframeAnimation *privacyAnimation = [CAKeyframeAnimation animation]; + privacyAnimation.keyPath = @"transform.translation.x"; + privacyAnimation.values = @[@(0), @(-10), @(0)]; + privacyAnimation.repeatCount = 2; + privacyAnimation.speed = 1; + model.privacyAnimation = privacyAnimation; + } + /// 协议设置 END + + /// 切换到其他标题 START + BOOL changeBrnStatus = [viewConfig boolValueForKey: @"switchAccHidden" defaultValue: NO]; + model.changeBtnIsHidden = changeBrnStatus; + if (!changeBrnStatus) { + model.changeBtnTitle = [ + [NSAttributedString alloc] initWithString: [viewConfig stringValueForKey: @"switchAccText" defaultValue: @"切换到其他方式"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"switchAccTextColor" defaultValue: @"#555555"]], + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"switchAccTextSize" defaultValue: 18]] + } + ]; + model.changeBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + return CGRectMake( + 10, + [viewConfig floatValueForKey: @"switchOffsetY" defaultValue: frame.origin.y], + superViewSize.width - 20, + 30 + ); + } else { + return CGRectZero; //横屏时模拟隐藏该控件 + } + }; + } + /// 切换到其他标题 END + + model.prefersStatusBarHidden = YES; + model.preferredStatusBarStyle = UIStatusBarStyleLightContent; + + /// 页面弹出方向 START + model.presentDirection = [viewConfig intValueForKey: @"presentDirection" defaultValue: 0]; + + /// 无动画过渡:让授权页瞬间出现/消失,减少页面跳转感 + BOOL instantTransition = [viewConfig boolValueForKey: @"instantTransition" defaultValue: NO]; + if (instantTransition) { + CABasicAnimation *entryAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; + entryAnim.fromValue = @1; + entryAnim.toValue = @1; + entryAnim.duration = 0.01; + model.entryAnimation = entryAnim; + + CABasicAnimation *exitAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; + exitAnim.fromValue = @1; + exitAnim.toValue = @0; + exitAnim.duration = 0.01; + model.exitAnimation = exitAnim; + } + /// 页面弹出方向 END + + /// 勾选统一按钮 START + BOOL checkStatus = [viewConfig boolValueForKey: @"checkboxHidden" defaultValue: NO]; + model.checkBoxIsHidden = checkStatus; + if (!checkStatus) { + UIImage* unchecked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"uncheckedImgPath" defaultValue: nil]]; + UIImage* checked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"checkedImgPath" defaultValue: nil]]; + if (unchecked != nil && checked != nil) { + model.checkBoxImages = @[ + unchecked, + checked + ]; + } + } + model.checkBoxIsChecked = [viewConfig boolValueForKey: @"privacyState" defaultValue: NO]; + model.checkBoxWH = [viewConfig floatValueForKey: @"checkBoxHeight" defaultValue: 17.0]; + /// 勾选统一按钮 END + + /// 自定义第三方按钮布局 START + NSMutableArray * customArrayView = [NSMutableArray array]; + NSDictionary *customThirdView = [viewConfig dictValueForKey: @"customThirdView" defaultValue: nil]; + if (customThirdView != nil) { + NSArray * customArray = [customThirdView arrayValueForKey: @"viewItemPath" defaultValue: nil]; + NSArray * customNameArray = [customThirdView arrayValueForKey: @"viewItemName" defaultValue: nil]; + if(customArray != nil && customArray.count > 0){ + int width = [customThirdView intValueForKey: @"itemWidth" defaultValue: 70]; + int height = [customThirdView intValueForKey: @"itemHeight" defaultValue: 70]; + int offsetY = [customThirdView intValueForKey: @"top" defaultValue: 20]; + int space = [customThirdView intValueForKey: @"space" defaultValue: 30]; + int textSize = [customThirdView intValueForKey: @"size" defaultValue: 30]; + NSString *color = [viewConfig stringValueForKey: @"color" defaultValue: @"#3C4E5F"]; + + for (int i = 0 ; i < customArray.count; i++) { + CustomButton *button = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, width, height)]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont systemFontOfSize: textSize]; + [button setTag: i]; + [button setTitle: customNameArray[i] forState:UIControlStateNormal]; + [button setTitleColor: [self getColor: color] forState:UIControlStateNormal]; + [button setBackgroundImage:[self changeUriPathToImage: customArray[i]] forState:UIControlStateNormal]; + [button addTarget:target action: selector forControlEvents:UIControlEventTouchUpInside]; + [customArrayView addObject: button]; + } + + model.customViewLayoutBlock = ^( + CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, + CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, + CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, + CGRect privacyFrame + ) { + NSUInteger count = customArrayView.count; + NSInteger contentWidth = screenSize.width; + for (int i = 0 ; i < count; i++) { + UIButton *itemView = (UIButton *)customArrayView[i]; + NSInteger X = (contentWidth - (width * count + space * (count - 1))) / 2 + (space + width) * i; + itemView.frame = CGRectMake( X, offsetY, itemView.frame.size.width, itemView.frame.size.height ); + } + }; + } + } + + /// 自定义标题文字(通过 customViewBlock 在授权页顶部添加自定义标签) + NSString *customTitleText = [viewConfig stringValueForKey: @"customTitleText" defaultValue: nil]; + NSString *customTitleColor = [viewConfig stringValueForKey: @"customTitleColor" defaultValue: @"#6366F1"]; + CGFloat customTitleSize = [viewConfig floatValueForKey: @"customTitleSize" defaultValue: 28]; + CGFloat customTitleOffsetY = [viewConfig floatValueForKey: @"customTitleOffsetY" defaultValue: 60]; + + BOOL hasCustomTitle = (customTitleText != nil && customTitleText.length > 0); + BOOL hasThirdButtons = (customArrayView.count > 0); + + if (hasCustomTitle || hasThirdButtons) { + model.customViewBlock = ^(UIView * _Nonnull superCustomView) { + /// 添加自定义标题 + if (hasCustomTitle) { + UILabel *titleLabel = [[UILabel alloc] init]; + titleLabel.text = customTitleText; + titleLabel.textColor = [PNSBuildModelUtils getColor: customTitleColor]; + titleLabel.font = [UIFont fontWithName:@"PressStart2P-Regular" size:customTitleSize] + ?: [UIFont boldSystemFontOfSize:customTitleSize]; + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.tag = 9999; + + /// 添加像素风文字阴影 + titleLabel.layer.shadowColor = [UIColor colorWithRed:0.65 green:0.55 blue:0.98 alpha:0.19].CGColor; + titleLabel.layer.shadowOffset = CGSizeMake(1, 1); + titleLabel.layer.shadowOpacity = 1.0; + titleLabel.layer.shadowRadius = 0; + + [titleLabel sizeToFit]; + CGFloat labelWidth = titleLabel.frame.size.width; + CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; + titleLabel.frame = CGRectMake( + (screenWidth - labelWidth) / 2.0, + customTitleOffsetY, + labelWidth, + titleLabel.frame.size.height + ); + [superCustomView addSubview: titleLabel]; + } + /// 添加第三方图标 + for (int i = 0 ; i < customArrayView.count; i++) { + [superCustomView addSubview: customArrayView[i]]; + } + }; + } + /// 自定义第三方按钮布局 END + + /// 背景设置 START + NSString * backgroundColor = [viewConfig stringValueForKey: @"backgroundColor" defaultValue: nil]; + if (![backgroundColor isEqual: nil]) { + model.backgroundColor = [self getColor: backgroundColor]; + } + NSString * backgroundImagePath = [viewConfig stringValueForKey: @"pageBackgroundPath" defaultValue: nil]; + if (![backgroundImagePath isEqual: nil]) { + model.backgroundImage = [self changeUriPathToImage: backgroundImagePath]; + } + /// 背景设置 END + + return model; +} +#pragma mark - 全屏-横屏 ++ (TXCustomModel *)buildFullScreenLandscapeModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = [[TXCustomModel alloc] init]; + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskLandscape; + model.navColor = [UIColor orangeColor]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : [UIColor whiteColor], + NSFontAttributeName : [UIFont systemFontOfSize:20.0] + }; + model.navTitle = [[NSAttributedString alloc] initWithString:@"一键登录" attributes:attributes]; + model.navBackImage = [UIImage imageNamed:@"icon_nav_back_light"]; + model.logoImage = [UIImage imageNamed:@"taobao"]; + model.sloganIsHidden = YES; + model.changeBtnIsHidden = YES; + model.privacyOne = @[@"协议1", @"https://www.taobao.com"]; + + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = 80; + frame.size.height = 80; + frame.origin.y = 15; + frame.origin.x = (superViewSize.width - 80) * 0.5; + return frame; + }; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.origin.y = 15 + 80 + 15; + return frame; + }; + + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.origin.y = 110 + 30 + 20; + return frame; + }; + +// UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button1 setTitle:button1Title forState:UIControlStateNormal]; +// [button1 addTarget:target1 action:selector1 forControlEvents:UIControlEventTouchUpInside]; +// +// UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button2 setTitle:button2Title forState:UIControlStateNormal]; +// [button2 addTarget:target2 action:selector2 forControlEvents:UIControlEventTouchUpInside]; + +// model.customViewBlock = ^(UIView * _Nonnull superCustomView) { +// [superCustomView addSubview:button1]; +// [superCustomView addSubview:button2]; +// }; +// model.customViewLayoutBlock = ^(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame) { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// +// button2.frame = CGRectMake(CGRectGetMaxX(button1.frame), +// CGRectGetMinY(button1.frame), +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// }; + return model; +} + +#pragma mark - 弹窗-竖屏 ++ (TXCustomModel *)buildAlertPortraitMode:(NSDictionary *)viewConfig + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = [[TXCustomModel alloc] init]; + + // 1状态栏START + model.prefersStatusBarHidden = [viewConfig boolValueForKey: @"isStatusBarHidden" defaultValue: NO]; + bool isLightColor = [viewConfig boolValueForKey: @"lightColor" defaultValue: NO]; + model.preferredStatusBarStyle = isLightColor ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; + // 1状态栏ENd + + model.alertBarIsHidden = [viewConfig boolValueForKey: @"navHidden" defaultValue: NO]; + model.alertTitleBarColor = [self getColor: [viewConfig stringValueForKey: @"alertTitleBarColor" defaultValue: @"#3971fe"]]; + model.alertTitle = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"navText" defaultValue: @"一键登录"] + attributes: @{ + NSForegroundColorAttributeName: UIColor.whiteColor, + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"navTextSize" defaultValue: 20.0]] + } + ];; + model.alertCloseItemIsHidden = [viewConfig boolValueForKey: @"alertCloseItemIsHidden" defaultValue: NO]; + + UIImage * alertCloseImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"alertCloseImage" defaultValue: nil]]; + model.alertCloseImage = alertCloseImage?:[UIImage imageNamed:@"icon_close_light"]; + + model.alertCloseItemFrameBlock = ^CGRect(CGSize screenSize,CGSize superViewSize,CGRect frame) { + if ([self isHorizontal:screenSize]) { + //横屏时模拟隐藏该控件 + return CGRectZero; + } else { + frame.origin.x = [viewConfig intValueForKey: @"alertCloseImageX" defaultValue: 5]; + frame.origin.y = [viewConfig intValueForKey: @"alertCloseImageY" defaultValue: 5]; + frame.size.width = [viewConfig intValueForKey: @"alertCloseImageW" defaultValue: 30]; + frame.size.height = [viewConfig intValueForKey: @"alertCloseImageH" defaultValue: 30]; + return frame; + } + }; + + model.alertBlurViewColor = [self getColor: [viewConfig stringValueForKey: @"alertBlurViewColor" defaultValue: @"#000000"]]; + model.alertBlurViewAlpha = [viewConfig floatValueForKey: @"dialogAlpha" defaultValue: 0.5]; + NSArray *alertCornerRadiusArray = [viewConfig arrayValueForKey: @"dialogCornerRadiusArray" defaultValue: @[@10, @10, @10, @10]]; + model.alertCornerRadiusArray = alertCornerRadiusArray; + + /// 协议页面导航设置 + model.privacyNavColor = [self getColor: [viewConfig stringValueForKey: @"webNavColor" defaultValue: @"#000000"]]; + UIImage * privacyNavBackImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"webNavReturnImgPath" defaultValue: nil]]; + if(privacyNavBackImage != nil){ + model.privacyNavBackImage = privacyNavBackImage; + } + model.privacyNavTitleFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"webNavTextSize" defaultValue: 18]]; + model.privacyNavTitleColor = [self getColor: [viewConfig stringValueForKey: @"webNavTextColor" defaultValue: @"#000000"]]; + + /// logo 设置 START + model.logoIsHidden = [viewConfig boolValueForKey: @"logoHidden" defaultValue: NO]; + UIImage * image = [self changeUriPathToImage: [viewConfig stringValueForKey: @"logoImgPath" defaultValue: nil]]; + if(image != nil){ + /// logo 默认水平居中 + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]; + frame.size.height = [viewConfig floatValueForKey: @"logoHeight" defaultValue: 80]; + frame.origin.y = [viewConfig floatValueForKey: @"logoOffsetY" defaultValue: screenSize.height > screenSize.width ? 30 : 15]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]) * 0.5; + return frame; + }; + model.logoImage = image; + } + /// logo 设置 END + + /// slogan 设置 START + model.sloganIsHidden = [viewConfig boolValueForKey: @"sloganHidden" defaultValue: NO]; + model.sloganText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"sloganText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"sloganTextColor" defaultValue: @"#555555"]], + NSFontAttributeName: [ + UIFont systemFontOfSize: [viewConfig floatValueForKey: @"sloganTextSize" defaultValue: 19] + ] + } + ]; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"sloganOffsetY" defaultValue: 20 + 80 + 20]; + return frame; + } else { + return CGRectZero; + } + }; + /// slogan 设置 END + + /// number 设置 START + model.numberColor = [self getColor: [viewConfig stringValueForKey: @"numberColor" defaultValue: @"#555"]]; + model.numberFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"numberSize" defaultValue: 17]]; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"numFieldOffsetY" defaultValue: 130 + 20 + 15]; + } else { + frame.origin.y = 15 + 80 + 15; + } + return frame; + }; + /// number 设置 END + + /// 登录按钮 START + model.loginBtnText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"logBtnText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"logBtnTextColor" defaultValue: @"#ff00ff"]], + NSFontAttributeName: [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"logBtnTextSize" defaultValue: 23]] + } + ]; + NSArray *logBtnCustomBackgroundImagePath = [[viewConfig stringValueForKey: @"logBtnBackgroundPath" defaultValue: @","] componentsSeparatedByString:@","]; + if (logBtnCustomBackgroundImagePath.count == 3) { + // login_btn_normal + UIImage * login_btn_normal = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[0]]; + // login_btn_unable + UIImage * login_btn_unable = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[1]]; + // login_btn_press + UIImage * login_btn_press = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[2]]; + // default + UIImage *defaultClick = [UIImage imageNamed:@"button_click"]; + UIImage *defaultUnClick = [UIImage imageNamed:@"button_unclick"]; + // fix '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]' + if ((login_btn_normal != nil && login_btn_unable != nil && login_btn_press != nil) || (defaultClick != nil && defaultUnClick != nil)) { + // 登录按钮设置 + model.loginBtnBgImgs = @[ + login_btn_normal?:defaultClick, + login_btn_unable?:defaultUnClick, + login_btn_press?:defaultClick + ]; + } + } + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]; + frame.size.height = [viewConfig floatValueForKey: @"logBtnHeight" defaultValue: 40]; + frame.origin.y = [viewConfig floatValueForKey: @"logBtnOffsetY" defaultValue: 170 + 30 + 20]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]) * 0.5; + } else { + frame.origin.y = 110 + 30 + 20; + } + return frame; + }; + /// 登录按钮 END + model.privacyOne = @[ + [viewConfig stringValueForKey: @"protocolOneName" defaultValue: @""], + [viewConfig stringValueForKey: @"protocolOneURL" defaultValue: @""] + ]; + model.privacyTwo = @[ + [viewConfig stringValueForKey: @"protocolTwoName" defaultValue: @""], + [viewConfig stringValueForKey: @"protocolTwoURL" defaultValue: @""] + ]; + model.privacyThree = @[ + [viewConfig stringValueForKey: @"protocolThreeName" defaultValue: @""], + [viewConfig stringValueForKey: @"protocolThreeURL" defaultValue: @""] + ]; + NSArray *privacyColors = [[viewConfig stringValueForKey: @"appPrivacyColor" defaultValue: nil] componentsSeparatedByString:@","]; + if(privacyColors != nil && privacyColors.count > 1){ + model.privacyColors = @[ + [self getColor: privacyColors[0]], + [self getColor: privacyColors[1]] + ]; + } + + model.privacyAlertContentUnderline = [viewConfig boolValueForKey: @"privacyAlertProtocolNameUseUnderLine" defaultValue: NO]; + // 协议1内容颜色 + model.privacyOneColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnOneColor" defaultValue: @"#000000"]]; + // 协议2内容颜色 + model.privacyTwoColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnTwoColor" defaultValue: @"#000000"]]; + // 协议3内容颜色 + model.privacyThreeColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnThreeColor" defaultValue: @"#000000"]]; + // 运营商协议内容颜色 + model.privacyOperatorColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnColor" defaultValue: @"#000000"]]; + + // 二次协议1内容颜色 + model.privacyAlertOneColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnOneColor" defaultValue: @"#000000"]]; + // 二次协议2内容颜色 + model.privacyAlertTwoColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnTwoColor" defaultValue: @"#000000"]]; + // 二次协议3内容颜色 + model.privacyAlertThreeColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnThreeColor" defaultValue: @"#000000"]]; + // 二次运营商协议内容颜色 + model.privacyAlertOperatorColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOperatorColor" defaultValue: @"#000000"]]; + + model.privacyAlignment = [viewConfig intValueForKey: @"protocolLayoutGravity" defaultValue: 1]; + model.privacyFont = [UIFont fontWithName:@"PingFangSC-Regular" size: [viewConfig floatValueForKey: @"privacyTextSize" defaultValue: 12.0]]; + model.privacyPreText = [viewConfig stringValueForKey: @"privacyBefore" defaultValue: @"点击一键登录并登录表示您已阅读并同意"]; + model.privacySufText = [viewConfig stringValueForKey: @"privacyEnd" defaultValue: @"思预云用户协议,隐私"]; + model.privacyOperatorPreText = [viewConfig stringValueForKey: @"vendorPrivacyPrefix" defaultValue: @"《"]; + model.privacyOperatorSufText = [viewConfig stringValueForKey: @"vendorPrivacySuffix" defaultValue: @"》"]; + /// 协议水平垂直设置 + model.privacyFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.y = [viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1]; + } + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.x = [viewConfig floatValueForKey: @"privacyOffsetX" defaultValue: -1]; + } + return frame; + }; + + // 勾选统一按钮 + BOOL checkStatus = [viewConfig boolValueForKey: @"checkboxHidden" defaultValue: NO]; + model.checkBoxIsHidden = checkStatus; + if (!checkStatus) { + UIImage* unchecked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"uncheckedImgPath" defaultValue: nil]]; + UIImage* checked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"checkedImgPath" defaultValue: nil]]; + if (unchecked != nil && checked != nil) { + model.checkBoxImages = @[ + unchecked, + checked + ]; + } + } + model.checkBoxIsChecked = [viewConfig boolValueForKey: @"privacyState" defaultValue: NO]; + model.checkBoxWH = [viewConfig floatValueForKey: @"checkBoxHeight" defaultValue: 17.0]; + + // 切换到其他标题 + model.changeBtnIsHidden = [viewConfig boolValueForKey: @"switchAccHidden" defaultValue: NO]; + model.changeBtnTitle = [ + [NSAttributedString alloc] initWithString: [viewConfig stringValueForKey: @"switchAccText" defaultValue: @"切换到其他方式"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"switchAccTextColor" defaultValue: @"#ccc"]], + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"switchAccTextSize" defaultValue: 18]] + } + ]; + model.changeBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + return CGRectMake(10, [viewConfig floatValueForKey: @"switchOffsetY" defaultValue: 18], superViewSize.width - 20, 30); + } else { + return CGRectZero; //横屏时模拟隐藏该控件 + } + }; + + /// 点击授权页背景是否关闭授权页,只有在弹窗模式下生效,默认NO + model.tapAuthPageMaskClosePage=[viewConfig boolValueForKey: @"tapAuthPageMaskClosePage" defaultValue: NO]; + /// 页面弹出方向 START + model.presentDirection = [viewConfig intValueForKey: @"presentDirection" defaultValue: 0]; + /// 页面弹出方向 END + + CGFloat ratio = MAX(TX_SCREEN_WIDTH, TX_SCREEN_HEIGHT) / 667.0; + //实现该block,并且返回的frame的x或y大于0,则认为是弹窗谈起授权页 + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize contentSize, CGRect frame) { + CGFloat alertX = 0; + CGFloat alertY = 0; + CGFloat alertWidth = 0; + CGFloat alertHeight = 0; + if ([self isHorizontal:screenSize]) { + alertX = ratio * TX_Alert_Horizontal_Default_Left_Padding; + alertWidth = [viewConfig intValueForKey: @"dialogWidth" defaultValue: screenSize.width - alertX * 2]; + alertY = (screenSize.height - alertWidth * 0.5) * 0.5; + alertHeight = [viewConfig intValueForKey: @"dialogHeight" defaultValue: screenSize.height - 2 * alertY]; + } else { + alertWidth = [viewConfig intValueForKey: @"dialogWidth" defaultValue: screenSize.width / 2]; + alertHeight = [viewConfig intValueForKey: @"dialogHeight" defaultValue: screenSize.height / 2]; + alertX = [viewConfig intValueForKey: @"dialogOffsetX" defaultValue: (TX_SCREEN_WIDTH - alertWidth) / 2]; + alertY = [viewConfig intValueForKey: @"dialogOffsetY" defaultValue: (TX_SCREEN_HEIGHT - alertHeight) / 2]; + } + return CGRectMake(alertX, alertY, alertWidth, alertHeight); + }; + + //授权页默认控件布局调整 + + /// 自定义第三方按钮布局 START + NSDictionary *customThirdView = [viewConfig dictValueForKey: @"customThirdView" defaultValue: nil]; + if (customThirdView != nil) { + NSMutableArray * customArrayView = [NSMutableArray array]; /// 空数组,有意义 + NSArray * customArray = [customThirdView arrayValueForKey: @"viewItemPath" defaultValue: nil]; //空数组,有意义 + NSArray * customNameArray = [customThirdView arrayValueForKey: @"viewItemName" defaultValue: nil]; //空数组,有意义 + if(customArray != nil && customArray.count > 0){ + /// 第三方图标按钮的相关参数 + int width = [customThirdView intValueForKey: @"itemWidth" defaultValue: 50]; + int height = [customThirdView intValueForKey: @"itemHeight" defaultValue: 50]; + int offsetY = [customThirdView intValueForKey: @"top" defaultValue: 20]; + int textSize = [customThirdView intValueForKey: @"size" defaultValue: 20]; + int space = [customThirdView intValueForKey: @"space" defaultValue: 30]; + NSString* color = [customThirdView stringValueForKey: @"color" defaultValue: @"#3c4E5F"]; + for (int i = 0 ; i < customArray.count; i++) { + /// 动态生成imageView 并且加入到 imageView数组中以备使用 + CustomButton *button = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, width, height)]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont systemFontOfSize: textSize]; + [button setTag: i]; + [button setTitle: customNameArray[i] forState:UIControlStateNormal]; + [button setTitleColor: [self getColor: color] forState:UIControlStateNormal]; + [button setBackgroundImage:[self changeUriPathToImage: customArray[i]] forState:UIControlStateNormal]; + [button addTarget:target action: selector forControlEvents:UIControlEventTouchUpInside]; + + [customArrayView addObject: button]; + } + + /// 添加第三方图标 + model.customViewBlock = ^(UIView * _Nonnull superCustomView) { + for (int i = 0 ; i < customArrayView.count; i++) { + [superCustomView addSubview: customArrayView[i]]; + } + + +// UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; +// [btn setTitle:@"触屏拍摄" forState:UIControlStateNormal]; +//// UIImage *img = [self changeUriPathToImage: customArray[0]]; +//// [btn setImage:img forState:UIControlStateNormal];//button的填充图片 +// [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; +// btn.imageEdgeInsets = UIEdgeInsetsMake(- (btn.frame.size.height - btn.titleLabel.frame.size.height- btn.titleLabel.frame.origin.y),(btn.frame.size.width -btn.titleLabel.frame.size.width)/2.0f -btn.imageView.frame.size.width, 0, 0); +// btn.titleEdgeInsets = UIEdgeInsetsMake(btn.frame.size.height-btn.imageView.frame.size.height-btn.imageView.frame.origin.y, -btn.imageView.frame.size.width, 0, 0); +// [superCustomView addSubview: btn]; +// btn.frame = CGRectMake( 10, 50, 300, 200 ); + +// UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickAllScreen:)]; +// [superCustomView addGestureRecognizer:singleTap]; + }; + + model.customViewLayoutBlock = ^( + CGSize screenSize, /// 全屏参数 + CGRect contentViewFrame, /// contentView参数 + CGRect navFrame, /// 导航参数 + CGRect titleBarFrame, /// title参数 + CGRect logoFrame, /// logo区域参数 + CGRect sloganFrame, /// slogan参数 + CGRect numberFrame, /// 号码处参数 + CGRect loginFrame, /// 登录按钮处的参数 + CGRect changeBtnFrame, /// 切换到其他的参数 + CGRect privacyFrame /// 协议区域的参数 + ) { + NSUInteger count = customArrayView.count; + NSInteger contentWidth = contentViewFrame.size.width; + for (int i = 0; i < count; i++) { + UIButton *itemView = (UIButton *)customArrayView[i]; + NSInteger X = (contentWidth - (width * count + space * (count - 1))) / 2 + (space + width) * i; /// 两端评分 + itemView.frame = CGRectMake( X, offsetY, width, height ); + } + }; + } + } + /// 自定义第三方按钮布局 END + + + /// 背景设置 START + NSString * backgroundColor = [viewConfig stringValueForKey: @"backgroundColor" defaultValue: nil]; + if (![backgroundColor isEqual: nil]) { + model.backgroundColor = [self getColor: backgroundColor]; + } + NSString * backgroundImagePath = [viewConfig stringValueForKey: @"pageBackgroundPath" defaultValue: nil]; + if (![backgroundImagePath isEqual: nil]) { + model.backgroundImage = [self changeUriPathToImage: backgroundImagePath]; + } + /// 背景设置 END + return model; +} +#pragma mark - 弹窗-横屏 ++ (TXCustomModel *)buildAlertLandscapeMode:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector{ + TXCustomModel *model = [[TXCustomModel alloc] init]; + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskLandscape; + model.alertCornerRadiusArray = @[@10, @10, @10, @10]; + model.alertTitleBarColor = [UIColor orangeColor]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : [UIColor whiteColor], + NSFontAttributeName : [UIFont systemFontOfSize:20.0] + }; + model.alertTitle = [[NSAttributedString alloc] initWithString:@"一键登录" attributes:attributes]; + model.alertCloseImage = [UIImage imageNamed:@"icon_close_light"]; + model.logoIsHidden = YES; + model.sloganIsHidden = YES; + model.changeBtnIsHidden = YES; + model.privacyOne = @[@"协议1", @"https://www.taobao.com"]; + + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.height = superViewSize.height * 0.8; + frame.size.width = frame.size.height / 0.618; + frame.origin.x = (superViewSize.width - frame.size.width) * 0.5; + frame.origin.y = (superViewSize.height - frame.size.height) * 0.5; + return frame; + }; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.origin.y = 30; + return frame; + }; + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.origin.y = 30 + 20 + 30; + return frame; + }; + +// UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button1 setTitle:button1Title forState:UIControlStateNormal]; +// [button1 addTarget:target1 action:selector1 forControlEvents:UIControlEventTouchUpInside]; +// +// UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button2 setTitle:button2Title forState:UIControlStateNormal]; +// [button2 addTarget:target2 action:selector2 forControlEvents:UIControlEventTouchUpInside]; +// +// model.customViewBlock = ^(UIView * _Nonnull superCustomView) { +// [superCustomView addSubview:button1]; +// [superCustomView addSubview:button2]; +// }; +// model.customViewLayoutBlock = ^(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame) { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// +// button2.frame = CGRectMake(CGRectGetMaxX(button1.frame), +// CGRectGetMinY(button1.frame), +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// }; + return model; +} + +#pragma mark - 底部弹窗 ++ (TXCustomModel *)buildSheetPortraitModel:(NSDictionary *)viewConfig + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = [[TXCustomModel alloc] init]; + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskPortrait; + model.alertCornerRadiusArray = @[@10, @0, @0, @10]; + + // 1状态栏START + model.prefersStatusBarHidden = [viewConfig boolValueForKey: @"isStatusBarHidden" defaultValue: NO]; + bool isLightColor = [viewConfig boolValueForKey: @"lightColor" defaultValue: NO]; + model.preferredStatusBarStyle = isLightColor ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; + // 1状态栏ENd + + model.alertBarIsHidden = [viewConfig boolValueForKey: @"navHidden" defaultValue: NO]; + model.alertTitleBarColor = [self getColor: [viewConfig stringValueForKey: @"alertTitleBarColor" defaultValue: @"#3971fe"]]; + model.alertTitle = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"navText" defaultValue: @"一键登录"] + attributes: @{ + NSForegroundColorAttributeName: UIColor.whiteColor, + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"navTextSize" defaultValue: 20.0]] + } + ];; + + /// 关闭按钮 + model.alertCloseItemIsHidden = [viewConfig boolValueForKey: @"alertCloseItemIsHidden" defaultValue: NO]; + UIImage * alertCloseImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"alertCloseImage" defaultValue: nil]]; + model.alertCloseImage = alertCloseImage?:[UIImage imageNamed:@"icon_close_light"]; + model.alertCloseItemFrameBlock = ^CGRect(CGSize screenSize,CGSize superViewSize,CGRect frame) { + if ([self isHorizontal:screenSize]) { + //横屏时模拟隐藏该控件 + return CGRectZero; + } else { + frame.origin.x = [viewConfig intValueForKey: @"alertCloseImageX" defaultValue: 5]; + frame.origin.y = [viewConfig intValueForKey: @"alertCloseImageY" defaultValue: 5]; + frame.size.width = [viewConfig intValueForKey: @"alertCloseImageW" defaultValue: 30]; + frame.size.height = [viewConfig intValueForKey: @"alertCloseImageH" defaultValue: 30]; + return frame; + } + }; + /// 关闭按钮 + + /// 3logo 设置 START + model.logoIsHidden = [viewConfig boolValueForKey: @"logoHidden" defaultValue: NO]; + UIImage * image = [self changeUriPathToImage: [viewConfig stringValueForKey: @"logoImgPath" defaultValue: nil]]; + if(image != nil){ + /// logo 默认水平居中 + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]; + frame.size.height = [viewConfig floatValueForKey: @"logoHeight" defaultValue: 80]; + frame.origin.y = [viewConfig floatValueForKey: @"logoOffsetY" defaultValue: screenSize.height > screenSize.width ? 30 : 15]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logoWidth" defaultValue: 80]) * 0.5; + return frame; + }; + model.logoImage = image; + } + /// 3logo 设置 END + + /// 4slogan 设置 START + model.sloganIsHidden = [viewConfig boolValueForKey: @"sloganHidden" defaultValue: NO]; + model.sloganText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"sloganText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"sloganTextColor" defaultValue: @"#555555"]], + NSFontAttributeName: [ + UIFont systemFontOfSize: [viewConfig floatValueForKey: @"sloganTextSize" defaultValue: 19] + ] + } + ]; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"sloganOffsetY" defaultValue: 20 + 80 + 20]; + return frame; + } else { + return CGRectZero; + } + }; + /// 4slogan 设置 END + + /// 5number 设置 START + model.numberColor = [self getColor: [viewConfig stringValueForKey: @"numberColor" defaultValue: @"#555"]]; + model.numberFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"numberSize" defaultValue: 17]]; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [viewConfig floatValueForKey: @"numFieldOffsetY" defaultValue: 130 + 20 + 15]; + } else { + frame.origin.y = 15 + 80 + 15; + } + return frame; + }; + /// 5number 设置 END + + /// 6登录按钮 START + model.loginBtnText = [ + [NSAttributedString alloc] + initWithString: [viewConfig stringValueForKey: @"logBtnText" defaultValue: @"一键登录欢迎语"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"logBtnTextColor" defaultValue: @"#ff00ff"]], + NSFontAttributeName: [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"logBtnTextSize" defaultValue: 23]] + } + ]; + NSArray *logBtnCustomBackgroundImagePath = [[viewConfig stringValueForKey: @"logBtnBackgroundPath" defaultValue: @","] componentsSeparatedByString:@","]; + if (logBtnCustomBackgroundImagePath.count == 3) { + // login_btn_normal + UIImage * login_btn_normal = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[0]]; + // login_btn_unable + UIImage * login_btn_unable = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[1]]; + // login_btn_press + UIImage * login_btn_press = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[2]]; + // default + UIImage *defaultClick = [UIImage imageNamed:@"button_click"]; + UIImage *defaultUnClick = [UIImage imageNamed:@"button_unclick"]; + // fix '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]' + if ((login_btn_normal != nil && login_btn_unable != nil && login_btn_press != nil) || (defaultClick != nil && defaultUnClick != nil)) { + // 登录按钮设置 + model.loginBtnBgImgs = @[ + login_btn_normal?:defaultClick, + login_btn_unable?:defaultUnClick, + login_btn_press?:defaultClick + ]; + } + } + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]; + frame.size.height = [viewConfig floatValueForKey: @"logBtnHeight" defaultValue: 40]; + frame.origin.y = [viewConfig floatValueForKey: @"logBtnOffsetY" defaultValue: 170 + 30 + 20]; + frame.origin.x = (superViewSize.width - [viewConfig floatValueForKey: @"logBtnWidth" defaultValue: 300]) * 0.5; + } else { + frame.origin.y = 110 + 30 + 20; + } + return frame; + }; + /// 6登录按钮 END + + /// 7切换到其他标题 START + model.changeBtnIsHidden = ![viewConfig boolValueForKey: @"switchAccHidden" defaultValue: NO]; + model.changeBtnTitle = [ + [NSAttributedString alloc] initWithString: [viewConfig stringValueForKey: @"switchAccText" defaultValue: @"切换到其他方式"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [viewConfig stringValueForKey: @"switchAccTextColor" defaultValue: @"#ccc"]], + NSFontAttributeName : [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"switchAccTextSize" defaultValue: 18]] + } + ]; + model.changeBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + return CGRectMake(10, [viewConfig floatValueForKey: @"switchOffsetY" defaultValue: frame.origin.y], superViewSize.width - 20, 30); + } else { + return CGRectZero; //横屏时模拟隐藏该控件 + } + }; + /// 7切换到其他标题 END + + /// 8自定义第三方按钮布局 START + NSDictionary *customThirdView = [viewConfig dictValueForKey: @"customThirdView" defaultValue: nil]; + if (customThirdView != nil) { + NSMutableArray * customArrayView = [NSMutableArray array]; /// 空数组,有意义 + NSArray * customArray = [customThirdView arrayValueForKey: @"viewItemPath" defaultValue: nil]; //空数组,有意义 + NSArray * customNameArray = [customThirdView arrayValueForKey: @"viewItemName" defaultValue: nil]; //空数组,有意义 + if(customArray != nil && customArray.count > 0){ + /// 第三方图标按钮的相关参数 + int width = [customThirdView intValueForKey: @"itemWidth" defaultValue: 50]; + int height = [customThirdView intValueForKey: @"itemHeight" defaultValue: 50]; + int offsetY = [customThirdView intValueForKey: @"top" defaultValue: 20]; + int textSize = [customThirdView intValueForKey: @"size" defaultValue: 20]; + int space = [customThirdView intValueForKey: @"space" defaultValue: 30]; + NSString* color = [customThirdView stringValueForKey: @"color" defaultValue: @"#3c4E5F"]; + for (int i = 0 ; i < customArray.count; i++) { + /// 动态生成imageView 并且加入到 imageView数组中以备使用 + CustomButton *button = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, width, height)]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont systemFontOfSize: textSize]; + [button setTag: i]; + [button setTitle: customNameArray[i] forState:UIControlStateNormal]; + [button setTitleColor: [self getColor: color] forState:UIControlStateNormal]; + [button setBackgroundImage:[self changeUriPathToImage: customArray[i]] forState:UIControlStateNormal]; + [button addTarget:target action: selector forControlEvents:UIControlEventTouchUpInside]; + + [customArrayView addObject: button]; + } + + /// 添加第三方图标 + model.customViewBlock = ^(UIView * _Nonnull superCustomView) { + for (int i = 0 ; i < customArrayView.count; i++) { + [superCustomView addSubview: customArrayView[i]]; + } + }; + + model.customViewLayoutBlock = ^( + CGSize screenSize, /// 全屏参数 + CGRect contentViewFrame, /// contentView参数 + CGRect navFrame, /// 导航参数 + CGRect titleBarFrame, /// title参数 + CGRect logoFrame, /// logo区域参数 + CGRect sloganFrame, /// slogan参数 + CGRect numberFrame, /// 号码处参数 + CGRect loginFrame, /// 登录按钮处的参数 + CGRect changeBtnFrame, /// 切换到其他的参数 + CGRect privacyFrame /// 协议区域的参数 + ) { + NSUInteger count = customArrayView.count; + NSInteger contentWidth = contentViewFrame.size.width; + for (int i = 0; i < count; i++) { + UIButton *itemView = (UIButton *)customArrayView[i]; + NSInteger X = (contentWidth - (width * count + space * (count - 1))) / 2 + (space + width) * i; /// 两端评分 + itemView.frame = CGRectMake( X, offsetY, width, height ); + } + }; + } + } + /// 8自定义第三方按钮布局 END + + /// 9勾选统一按钮 START + BOOL checkStatus = [viewConfig boolValueForKey: @"checkboxHidden" defaultValue: NO]; + model.checkBoxIsHidden = checkStatus; + if (!checkStatus) { + UIImage* unchecked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"uncheckedImgPath" defaultValue: nil]]; + UIImage* checked = [self changeUriPathToImage: [viewConfig stringValueForKey: @"checkedImgPath" defaultValue: nil]]; + if (unchecked != nil && checked != nil) { + model.checkBoxImages = @[ + unchecked, + checked + ]; + } + } + model.checkBoxIsChecked = [viewConfig boolValueForKey: @"privacyState" defaultValue: NO]; + model.checkBoxWH = [viewConfig floatValueForKey: @"checkBoxHeight" defaultValue: 17.0]; + /// 9勾选统一按钮 END + /// + model.privacyOne = [[viewConfig stringValueForKey: @"appPrivacyOne" defaultValue: nil] componentsSeparatedByString:@","]; + model.privacyTwo = [[viewConfig stringValueForKey: @"appPrivacyTwo" defaultValue: nil] componentsSeparatedByString:@","]; + NSArray *privacyColors = [[viewConfig stringValueForKey: @"appPrivacyColor" defaultValue: nil] componentsSeparatedByString:@","]; + if(privacyColors != nil && privacyColors.count > 1){ + model.privacyColors = @[ + [self getColor: privacyColors[0]], + [self getColor: privacyColors[1]] + ]; + } + + model.privacyAlertContentUnderline = [viewConfig boolValueForKey: @"privacyAlertProtocolNameUseUnderLine" defaultValue: NO]; + // 协议1内容颜色 + model.privacyOneColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnOneColor" defaultValue: @"#000000"]]; + // 协议2内容颜色 + model.privacyTwoColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnTwoColor" defaultValue: @"#000000"]]; + // 协议3内容颜色 + model.privacyThreeColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnThreeColor" defaultValue: @"#000000"]]; + // 运营商协议内容颜色 + model.privacyOperatorColor = [self getColor: [viewConfig stringValueForKey: @"protocolOwnColor" defaultValue: @"#000000"]]; + + // 二次协议1内容颜色 + model.privacyAlertOneColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnOneColor" defaultValue: @"#000000"]]; + // 二次协议2内容颜色 + model.privacyAlertTwoColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnTwoColor" defaultValue: @"#000000"]]; + // 二次协议3内容颜色 + model.privacyAlertThreeColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOwnThreeColor" defaultValue: @"#000000"]]; + // 二次运营商协议内容颜色 + model.privacyAlertOperatorColor = [self getColor: [viewConfig stringValueForKey: @"privacyAlertOperatorColor" defaultValue: @"#000000"]]; + + model.privacyAlignment = [viewConfig intValueForKey: @"protocolLayoutGravity" defaultValue: 1];; + model.privacyFont = [UIFont fontWithName:@"PingFangSC-Regular" size: [viewConfig floatValueForKey: @"privacyTextSize" defaultValue: 12.0]]; + model.privacyPreText = [viewConfig stringValueForKey: @"privacyBefore" defaultValue: @"点击一键登录并登录表示您已阅读并同意"]; + model.privacySufText = [viewConfig stringValueForKey: @"privacyEnd" defaultValue: @"思预云用户协议,隐私"]; + model.privacyOperatorPreText = [viewConfig stringValueForKey: @"vendorPrivacyPrefix" defaultValue: @"《"]; + model.privacyOperatorSufText = [viewConfig stringValueForKey: @"vendorPrivacySuffix" defaultValue: @"》"]; + /// 协议水平垂直设置 + model.privacyFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.y = [viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1]; + } + if ([viewConfig floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.x = [viewConfig floatValueForKey: @"privacyOffsetX" defaultValue: -1]; + } + return frame; + }; + /// 协议 + + + /// 协议页面导航设置 + model.privacyNavColor = [self getColor: [viewConfig stringValueForKey: @"webNavColor" defaultValue: @"#000000"]]; + UIImage * privacyNavBackImage = [self changeUriPathToImage: [viewConfig stringValueForKey: @"webNavReturnImgPath" defaultValue: nil]]; + if(privacyNavBackImage != nil){ + model.privacyNavBackImage = privacyNavBackImage; + } + model.privacyNavTitleFont = [UIFont systemFontOfSize: [viewConfig floatValueForKey: @"webNavTextSize" defaultValue: 18]]; + model.privacyNavTitleColor = [self getColor: [viewConfig stringValueForKey: @"webNavTextColor" defaultValue: @"#000000"]]; + + + /// 授权页面配置 + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = superViewSize.width; + frame.size.height = [viewConfig floatValueForKey: @"dialogHeight" defaultValue: 460]; + frame.origin.x = 0; + frame.origin.y = superViewSize.height - frame.size.height; + return frame; + }; + + /// 点击授权页背景是否关闭授权页,只有在弹窗模式下生效,默认NO + model.tapAuthPageMaskClosePage=[viewConfig boolValueForKey: @"tapAuthPageMaskClosePage" defaultValue: NO]; + + /// 背景设置 START + NSString * backgroundColor = [viewConfig stringValueForKey: @"backgroundColor" defaultValue: nil]; + if (![backgroundColor isEqual: nil]) { + model.backgroundColor = [self getColor: backgroundColor]; + } + NSString * backgroundImagePath = [viewConfig stringValueForKey: @"pageBackgroundPath" defaultValue: nil]; + if (![backgroundImagePath isEqual: nil]) { + model.backgroundImage = [self changeUriPathToImage: backgroundImagePath]; + } + /// 背景设置 END + return model; +} + +#pragma mark - other ++ (TXCustomModel *)buildVideoOrGifBackgroundModel:(NSDictionary *)viewConfig + target:(id)target + style:(PNSBuildModelStyle)style + selector:(SEL)selector { + return [self buildModelOption: viewConfig target:target style: style selector:selector]; +} + +#pragma mark - DIY 动画 ++ (TXCustomModel *)buildAlertFadeModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + + TXCustomModel *model = [self buildAlertPortraitMode:dict + target:target + selector:selector]; + + CABasicAnimation *entryAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + entryAnimation.fromValue = @1.03; + entryAnimation.toValue = @1; + entryAnimation.duration = 0.25; + entryAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.entryAnimation = entryAnimation; + + CABasicAnimation *exitAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + exitAnimation.fromValue = @1; + exitAnimation.toValue = @0; + exitAnimation.duration = 0.25; + exitAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.exitAnimation = exitAnimation; + + //背景本身就是渐变,可以省略不写 model.bgEntryAnimation、model.bgExitAnimation + return model; +} + ++ (TXCustomModel *)buildAlertBounceModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + + TXCustomModel *model = [self buildAlertPortraitMode:dict + target:target + selector:selector]; + + CAKeyframeAnimation *entryAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; + entryAnimation.values = @[@0.01, @1.2, @0.9, @1]; + entryAnimation.keyTimes = @[@0, @0.4, @0.6, @1]; + entryAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; + entryAnimation.duration = 0.25; + model.entryAnimation = entryAnimation; + + CAKeyframeAnimation *exitAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; + exitAnimation.values = @[@1, @1.2, @0.01]; + exitAnimation.keyTimes = @[@0, @0.4, @1]; + exitAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; + exitAnimation.duration = 0.25; + model.exitAnimation = exitAnimation; + + //背景使用默认的渐变效果,可以省略不写 model.bgEntryAnimation、model.bgExitAnimation + return model; +} + ++ (TXCustomModel *)buildFullScreenAutorotateModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = [[TXCustomModel alloc] init]; + + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskAllButUpsideDown; + model.navColor = [UIColor orangeColor]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : [UIColor whiteColor], + NSFontAttributeName : [UIFont systemFontOfSize:20.0] + }; + model.navTitle = [[NSAttributedString alloc] initWithString:@"一键登录" attributes:attributes]; + model.navBackImage = [UIImage imageNamed:@"icon_nav_back_light"]; + model.logoImage = [UIImage imageNamed:@"taobao"]; + model.changeBtnIsHidden = YES; + model.privacyOne = @[@"协议1", @"https://www.taobao.com"]; + + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = 80; + frame.size.height = 80; + frame.origin.y = screenSize.height > screenSize.width ? 30 : 15; + frame.origin.x = (superViewSize.width - 80) * 0.5; + return frame; + }; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = superViewSize.width - 40; + frame.size.height = 20; + frame.origin.x = 20; + frame.origin.y = 20 + 80 + 20; + return frame; + } else { + return CGRectZero; + } + }; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = 130 + 20 + 15; + } else { + frame.origin.y = 15 + 80 + 15; + } + return frame; + }; + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = 170 + 30 + 20; + } else { + frame.origin.y = 110 + 30 + 20; + } + return frame; + }; + +// UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button1 setTitle:button1Title forState:UIControlStateNormal]; +// [button1 addTarget:target1 action:selector1 forControlEvents:UIControlEventTouchUpInside]; +// +// UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button2 setTitle:button2Title forState:UIControlStateNormal]; +// [button2 addTarget:target2 action:selector2 forControlEvents:UIControlEventTouchUpInside]; + +// model.customViewBlock = ^(UIView * _Nonnull superCustomView) { +// [superCustomView addSubview:button1]; +// [superCustomView addSubview:button2]; +// }; +// model.customViewLayoutBlock = ^(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame) { +// if (screenSize.height > screenSize.width) { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame), +// 30); +// +// button2.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(button1.frame) + 15, +// CGRectGetWidth(loginFrame), +// 30); +// } else { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// +// button2.frame = CGRectMake(CGRectGetMaxX(button1.frame), +// CGRectGetMinY(button1.frame), +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// } +// }; + return model; +} + ++ (TXCustomModel *)buildAlertAutorotateMode:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + TXCustomModel *model = [[TXCustomModel alloc] init]; + + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskAllButUpsideDown; + model.alertCornerRadiusArray = @[@10, @10, @10, @10]; + model.alertTitleBarColor = [UIColor orangeColor]; + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : [UIColor whiteColor], + NSFontAttributeName : [UIFont systemFontOfSize:20.0] + }; + model.alertTitle = [[NSAttributedString alloc] initWithString:@"一键登录" attributes:attributes]; + model.alertCloseImage = [UIImage imageNamed:@"icon_close_light"]; + model.logoImage = [UIImage imageNamed:@"taobao"]; + model.sloganIsHidden = YES; + model.changeBtnIsHidden = YES; + model.privacyOne = @[@"自定义协议", @"https://www.taobao.com"]; + + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = superViewSize.width * 0.8; + frame.size.height = frame.size.width / 0.618; + } else { + frame.size.height = superViewSize.height * 0.8; + frame.size.width = frame.size.height / 0.618; + } + frame.origin.x = (superViewSize.width - frame.size.width) * 0.5; + frame.origin.y = (superViewSize.height - frame.size.height) * 0.5; + return frame; + }; + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = 80; + frame.size.height = 80; + frame.origin.y = 20; + frame.origin.x = (superViewSize.width - 80) * 0.5; + } else { + frame = CGRectZero; + } + return frame; + }; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.height = 20; + frame.size.width = superViewSize.width - 40; + frame.origin.x = 20; + frame.origin.y = 20 + 80 + 20; + } else { + frame = CGRectZero; + } + return frame; + }; + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = 120 + 20 + 15; + } else { + frame.origin.y = 30; + } + return frame; + }; + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = 155 + 20 + 30; + } else { + frame.origin.y = 30 + 20 + 30; + } + return frame; + }; + +// UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button1 setTitle:button1Title forState:UIControlStateNormal]; +// [button1 addTarget:target1 action:selector1 forControlEvents:UIControlEventTouchUpInside]; +// +// UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem]; +// [button2 setTitle:button2Title forState:UIControlStateNormal]; +// [button2 addTarget:target2 action:selector2 forControlEvents:UIControlEventTouchUpInside]; +// +// model.customViewBlock = ^(UIView * _Nonnull superCustomView) { +// [superCustomView addSubview:button1]; +// [superCustomView addSubview:button2]; +// }; +// model.customViewLayoutBlock = ^(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame) { +// if (screenSize.height > screenSize.width) { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame), +// 30); +// +// button2.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(button1.frame) + 15, +// CGRectGetWidth(loginFrame), +// 30); +// } else { +// button1.frame = CGRectMake(CGRectGetMinX(loginFrame), +// CGRectGetMaxY(loginFrame) + 20, +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// +// button2.frame = CGRectMake(CGRectGetMaxX(button1.frame), +// CGRectGetMinY(button1.frame), +// CGRectGetWidth(loginFrame) * 0.5, +// 30); +// } +// }; + return model; +} + ++ (TXCustomModel *)buildAlertDropDownModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + + TXCustomModel *model = [self buildAlertPortraitMode:dict + target:target + selector:selector]; + + //提前设置好弹窗大小,弹窗是依赖于全屏布局,所以其父视图大小即为屏幕大小 + CGFloat width = UIScreen.mainScreen.bounds.size.width * 0.8; + CGFloat height = width / 0.618; + CGFloat x = (UIScreen.mainScreen.bounds.size.width - width) * 0.5; + CGFloat y = (UIScreen.mainScreen.bounds.size.height - height) * 0.5; + CGRect contentViewFrame = CGRectMake(x, y, width, height); + + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + return contentViewFrame; + }; + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"]; + animation.values = @[@(-CGRectGetMaxY(contentViewFrame)), @20, @-10, @0]; + animation.keyTimes = @[@0, @0.5, @0.75, @1]; + animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; + animation.duration = 0.25; + model.entryAnimation = animation; + + //背景使用默认的渐变效果,可以省略不写 model.bgEntryAnimation、model.bgExitAnimation + return model; +} + ++ (TXCustomModel *)buildFadeModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + + TXCustomModel *model = [self buildFullScreenPortraitModel:dict + target:target + selector:selector]; + + CABasicAnimation *entryAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + entryAnimation.fromValue = @0.5; + entryAnimation.toValue = @1; + entryAnimation.duration = 0.25; + entryAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.entryAnimation = entryAnimation; + + CABasicAnimation *exitAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + exitAnimation.fromValue = @1; + exitAnimation.toValue = @0; + exitAnimation.duration = 0.25; + exitAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.exitAnimation = exitAnimation; + + return model; +} + ++ (TXCustomModel *)buildScaleModel:(NSDictionary *)dict + target:(id)target + selector:(SEL)selector { + + TXCustomModel *model = [self buildFullScreenPortraitModel:dict + target:target + selector:selector]; + + CABasicAnimation *entryAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + entryAnimation.fromValue = @0; + entryAnimation.toValue = @1; + entryAnimation.duration = 0.25; + entryAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + model.entryAnimation = entryAnimation; + + CABasicAnimation *exitAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + exitAnimation.fromValue = @1; + exitAnimation.toValue = @0; + exitAnimation.duration = 0.25; + exitAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; + model.exitAnimation = exitAnimation; + + return model; +} + +#pragma mark assets -> 自定义图片view ++ (UIImageView *)customView: (NSString *)path + selector:(SEL)selector + target: (id) target + index: (int) index +{ + UIImage * image = [self changeUriPathToImage: path]; + + /// 自定义布局 图片不支持圆角,如需圆角请使用圆角图片 + UIImageView *imageView = [[UIImageView alloc]init]; + imageView.image = image; + imageView.tag = index; + imageView.frame = CGRectMake( 0, 0, 50, 50 ); + /// 设置控件背景颜色 + /// imageView.backgroundColor = [UIColor orangeColor]; + imageView.clipsToBounds = YES; + /// imageView.contentMode = UIViewContentModeScaleAspectFill; + imageView.contentMode = UIViewContentModeScaleToFill; + /// imageView.contentMode = UIViewContentModeScaleAspectFit; + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:target action:selector]; + + [imageView addGestureRecognizer:tapGesture]; + imageView.userInteractionEnabled = YES; + + return imageView; +} + +#pragma mark - build model ++ (TXCustomModel *)buildModelOption:(NSDictionary *)dict + target:(id)target + style:(PNSBuildModelStyle)style + selector:(SEL)selector { + NSLog(@"%@", dict); + TXCustomModel *model = [TXCustomModel mj_objectWithKeyValues: dict]; + for (NSString *key in dict) { + if (key && key.length > 0 && dict[key] != nil) { + NSString *newKey = [AliAuthEnum keyPair][key]?:key; + @try { + /// 分为两种情况数组和字符串 + if ([dict[key] isKindOfClass:[NSArray class]]) { + NSArray *array = [dict arrayValueForKey: key defaultValue: [NSArray array]]; + NSMutableArray *mutableArray = [array mutableCopy]; + if ([key isEqual: @"privacyAlertCornerRadiusArray"] && array.count == 4) { + // 左上、右上、右下、左下 -> 左上,左下,右下,右上 + mutableArray[0] = array[0]; + mutableArray[1] = array[3]; + mutableArray[2] = array[2]; + mutableArray[3] = array[1]; + } else { + // 使用for循环遍历数组 + [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + // 处理数组中的每个元素,并获取其下标 + // NSLog(@"Item at index %lu: %@", (unsigned long)idx, obj); + /// 处理图片路径 + if ([key containsString:@"Path"]) { + mutableArray[idx] = [self changeUriPathToImage: obj]; + } else if ([key containsString:@"Color"]) { + mutableArray[idx] = [self getColor: obj]; + } else { + mutableArray[idx] = obj; + } + }]; + } + [model setValue: [mutableArray copy] forKey:newKey]; + } else { + /// 处理图片路径 + if ([key containsString:@"Path"]) { + /// 为路径时需要转换 + UIImage *btn = [self changeUriPathToImage: dict[key]]; + if ([dict[key] containsString:@","]) { + } else if ([key isEqual:@"privacyAlertBtnBackgroundImgPath"] && btn != nil) { + [model setValue: @[btn, btn] forKey: newKey]; + } else if (btn != nil) { + [model setValue: btn forKey:newKey]; + } + } else if ([key containsString:@"Color"]) { + if ([key isEqual:@"lightColor"]) { + if (@available(iOS 13.0, *)) { + // UIStatusBarStyleLightContent Light content, for use on dark backgrounds + // UIStatusBarStyleDarkContent Dark content, for use on light backgrounds + [model setValue: @([dict boolValueForKey: key defaultValue: NO] ? UIStatusBarStyleDarkContent : UIStatusBarStyleLightContent) forKey:newKey]; + } else { + [model setValue: @([dict boolValueForKey: key defaultValue: NO] ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent) forKey:newKey]; + } + } else { + [model setValue: [self getColor: [dict stringValueForKey: key defaultValue: @"#23effe"]] forKey:newKey]; + } + } else if ([key containsString:@"Size"]) { + [model setValue: [UIFont systemFontOfSize: [dict floatValueForKey: key defaultValue: 17]] forKey:newKey]; + } else { + [model setValue: dict[key] ?: @"" forKey:newKey]; + } + } + } + @catch (NSException *exception) { + // 捕获并处理异常 + NSLog(@"捕获到异常:%@-%@", key, exception); + } + @finally { + // 无论是否抛出异常,这里的代码都会执行 + // 通常用于清理资源或执行一些必要的收尾工作 + // NSLog(@"执行清理工作"); + } + } + } + // NSLog(@"%@", model); + #pragma mark 其他配置 + // 判断背景类型 + PNSBackgroundView *backgroundView = nil; + if (PNSBuildModelStyleGifBackground == style || PNSBuildModelStyleVideoBackground == style) { + backgroundView = [[PNSBackgroundView alloc] init]; + NSURL *backgroundUrl = [NSURL fileURLWithPath:[self changeUriToPath: [dict stringValueForKey: @"pageBackgroundPath" defaultValue: nil]]]; + backgroundView.gifUrl = [NSURL fileURLWithPath: @""]; + backgroundView.videoUrl = [NSURL fileURLWithPath: @""]; + if (PNSBuildModelStyleGifBackground == style) { + backgroundView.gifUrl = backgroundUrl; + } else { + backgroundView.videoUrl = backgroundUrl; + } + [backgroundView show]; + } + if (PNSDIYAlertPortraitFade == style) + { + CABasicAnimation *entryAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + entryAnimation.fromValue = @1.03; + entryAnimation.toValue = @1; + entryAnimation.duration = 0.25; + entryAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.entryAnimation = entryAnimation; + + CABasicAnimation *exitAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + exitAnimation.fromValue = @1; + exitAnimation.toValue = @0; + exitAnimation.duration = 0.25; + exitAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; + model.exitAnimation = exitAnimation; + } + else if (PNSDIYAlertPortraitDropDown == style) { + //提前设置好弹窗大小,弹窗是依赖于全屏布局,所以其父视图大小即为屏幕大小 + CGFloat width = UIScreen.mainScreen.bounds.size.width * 0.8; + CGFloat height = width / 0.618; + CGFloat x = (UIScreen.mainScreen.bounds.size.width - width) * 0.5; + CGFloat y = (UIScreen.mainScreen.bounds.size.height - height) * 0.5; + CGRect contentViewFrame = CGRectMake(x, y, width, height); + + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + return contentViewFrame; + }; + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"]; + animation.values = @[@(-CGRectGetMaxY(contentViewFrame)), @20, @-10, @0]; + animation.keyTimes = @[@0, @0.5, @0.75, @1]; + animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; + animation.duration = 0.25; + model.entryAnimation = animation; + } + #pragma mark 0、公共样式 + #pragma mark 1、状态栏 + #pragma mark 2、导航栏 + NSAttributedString *attrs = [ + [NSAttributedString alloc] + initWithString: [dict stringValueForKey: @"navText" defaultValue: @"一键登录授权页面"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [dict stringValueForKey: @"navTextColor" defaultValue: @"#000000"]], + NSFontAttributeName : [UIFont systemFontOfSize: [dict floatValueForKey: @"navTextSize" defaultValue: 20.0]] + } + ]; + /// 弹窗标题设置不一致 + if (PNSBuildModelStyleAlertPortrait == style || PNSBuildModelStyleAlertLandscape == style || PNSBuildModelStyleSheetPortrait == style){ + model.alertTitle = attrs; + } else { + model.navTitle = attrs; + } + if (model.navIsHidden) { + if (!model.hideNavBackItem) { + /// 获取自定义的参数 + NSDictionary *customReturnBtn = [dict dictValueForKey: @"customReturnBtn" defaultValue: nil]; + /// 获取自定义的资源路径资源 + UIImage * navBackImagePath = [self changeUriPathToImage: [customReturnBtn stringValueForKey: @"imgPath" defaultValue: nil]]; + if (customReturnBtn != nil && navBackImagePath != nil) { + model.navBackImage = navBackImagePath; + /// 自定义返回按钮 + model.navBackButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + UIImageView *imageView = [[UIImageView alloc]init]; + imageView.image = navBackImagePath; + imageView.frame = CGRectMake( + CGRectGetMinX(frame), + CGRectGetMaxY(frame), + CGRectGetWidth(frame), + CGRectGetHeight(frame) + ); + frame.origin.y = [customReturnBtn floatValueForKey: @"top" defaultValue: 5]; + frame.origin.x = [customReturnBtn floatValueForKey: @"left" defaultValue: 15]; + frame.size.width = [customReturnBtn floatValueForKey: @"width" defaultValue: 40]; + frame.size.height = [customReturnBtn floatValueForKey: @"height" defaultValue: 40]; + return frame; + }; + } else { + /// 动态读取assets文件夹下的资源 + UIImage * navBackImage = model.navBackImage?:[UIImage imageNamed:@"icon_close_light"]; + model.navBackImage = navBackImage; + /// 自定义返回按钮 + model.navBackButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + UIImageView *imageView = [[UIImageView alloc]init]; + imageView.image = navBackImage; + imageView.frame = CGRectMake( + CGRectGetMinX(frame), + CGRectGetMaxY(frame), + CGRectGetWidth(frame), + CGRectGetHeight(frame) + ); + frame.origin.y = [dict floatValueForKey: @"navReturnOffsetY" defaultValue: 5]; + frame.origin.x = [dict floatValueForKey: @"navReturnOffsetX" defaultValue: 15]; + frame.size.width = [dict floatValueForKey: @"navReturnImgWidth" defaultValue: 40]; + frame.size.height = [dict floatValueForKey: @"navReturnImgHeight" defaultValue: 40]; + return frame; + }; + } + } + } + #pragma mark 3、Logo + if(!model.logoIsHidden && model.logoImage){ + /// logo 默认水平居中 + model.logoFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = [dict floatValueForKey: @"logoWidth" defaultValue: 80]; + frame.size.height = [dict floatValueForKey: @"logoHeight" defaultValue: 80]; + frame.origin.y = [dict floatValueForKey: @"logoOffsetY" defaultValue: screenSize.height > screenSize.width ? 30 : 15]; + frame.origin.x = (superViewSize.width - [dict floatValueForKey: @"logoWidth" defaultValue: 80]) * 0.5; + return frame; + }; + } + #pragma mark 4、Slogan + if (!model.sloganIsHidden) { + model.sloganText = [ + [NSAttributedString alloc] + initWithString: [dict stringValueForKey: @"sloganText" defaultValue: @"思预云欢迎您使用一键登录"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [dict stringValueForKey: @"sloganTextColor" defaultValue: @"#555555"]], + NSFontAttributeName: [ + UIFont systemFontOfSize: [dict floatValueForKey: @"sloganTextSize" defaultValue: 19] + ] + } + ]; + model.sloganFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [dict floatValueForKey: @"sloganOffsetY" defaultValue: 20 + 80 + 20]; + return frame; + } else { + return CGRectZero; + } + }; + } + #pragma mark 5、掩码栏 + model.numberFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.origin.y = [dict floatValueForKey: @"numFieldOffsetY" defaultValue: 130 + 20 + 15]; + } else { + frame.origin.y = 15 + 80 + 15; + } + return frame; + }; + + #pragma mark 6、登录按钮 + model.loginBtnText = [ + [NSAttributedString alloc] + initWithString: [dict stringValueForKey: @"logBtnText" defaultValue: @"一键登录"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [dict stringValueForKey: @"logBtnTextColor" defaultValue: @"#ff00ff"]], + NSFontAttributeName: [UIFont systemFontOfSize: [dict floatValueForKey: @"logBtnTextSize" defaultValue: 23]] + } + ]; + NSArray *logBtnCustomBackgroundImagePath = [[dict stringValueForKey: @"logBtnBackgroundPath" defaultValue: @","] componentsSeparatedByString:@","]; + if (logBtnCustomBackgroundImagePath.count == 3) { + // login_btn_normal + UIImage* login_btn_normal = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[0]]; + // login_btn_unable + UIImage* login_btn_unable = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[1]]; + // login_btn_press + UIImage* login_btn_press = [self changeUriPathToImage: logBtnCustomBackgroundImagePath[2]]; + UIImage *defaultClick = [UIImage imageNamed:@"button_click"]; + UIImage *defaultUnClick = [UIImage imageNamed:@"button_unclick"]; + // fix '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]' + if ((login_btn_normal != nil && login_btn_unable != nil && login_btn_press != nil) || (defaultClick != nil && defaultUnClick != nil)) { + // 登录按钮设置 + model.loginBtnBgImgs = @[ + login_btn_normal?:defaultClick, + login_btn_unable?:defaultUnClick, + login_btn_press?:defaultClick + ]; + } + } + model.loginBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + frame.size.width = [dict floatValueForKey: @"logBtnWidth" defaultValue: 300]; + frame.size.height = [dict floatValueForKey: @"logBtnHeight" defaultValue: 40]; + frame.origin.y = [dict floatValueForKey: @"logBtnOffsetY" defaultValue: 170 + 30 + 20]; + frame.origin.x = (superViewSize.width - [dict floatValueForKey: @"logBtnWidth" defaultValue: 300]) * 0.5; + } else { + frame.origin.y = 110 + 30 + 20; + } + return frame; + }; + #pragma mark 7、切换到其他方式 + if (!model.changeBtnIsHidden) { + model.changeBtnTitle = [ + [NSAttributedString alloc] initWithString: [dict stringValueForKey: @"switchAccText" defaultValue: @"切换到其他方式"] + attributes: @{ + NSForegroundColorAttributeName: [self getColor: [dict stringValueForKey: @"switchAccTextColor" defaultValue: @"#555555"]], + NSFontAttributeName : [UIFont systemFontOfSize: [dict floatValueForKey: @"switchAccTextSize" defaultValue: 18]] + } + ]; + model.changeBtnFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if (screenSize.height > screenSize.width) { + return CGRectMake( + 10, + [dict floatValueForKey: @"switchOffsetY" defaultValue: frame.origin.y], + superViewSize.width - 20, + 30 + ); + } else { + return CGRectZero; //横屏时模拟隐藏该控件 + } + }; + } + #pragma mark 8、自定义控件区(如其他方式登录) + NSDictionary *customThirdView = [dict dictValueForKey: @"customThirdView" defaultValue: nil]; + if (customThirdView != nil) { + NSMutableArray * customArrayView = [NSMutableArray array]; /// 空数组,有意义 + NSArray * customArray = [customThirdView arrayValueForKey: @"viewItemPath" defaultValue: nil]; //空数组,有意义 + NSArray * customNameArray = [customThirdView arrayValueForKey: @"viewItemName" defaultValue: nil]; //空数组,有意义 + if(customArray != nil && customArray.count > 0){ + /// 第三方图标按钮的相关参数 + int width = [customThirdView intValueForKey: @"itemWidth" defaultValue: 70]; + int height = [customThirdView intValueForKey: @"itemHeight" defaultValue: 70]; + int offsetY = [customThirdView intValueForKey: @"top" defaultValue: 20]; + int space = [customThirdView intValueForKey: @"space" defaultValue: 30]; + int textSize = [customThirdView intValueForKey: @"size" defaultValue: 17]; + NSString *color = [customThirdView stringValueForKey: @"color" defaultValue: @"#3C4E5F"]; + + for (int i = 0 ; i < customArray.count; i++) { + CustomButton *button = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, width, height)]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont systemFontOfSize: textSize]; + [button setTag: i]; + [button setTitle: customNameArray[i] forState:UIControlStateNormal]; + [button setTitleColor: [self getColor: color] forState:UIControlStateNormal]; + [button setBackgroundImage:[self changeUriPathToImage: customArray[i]] forState:UIControlStateNormal]; + [button addTarget:target action: selector forControlEvents:UIControlEventTouchUpInside]; + [customArrayView addObject: button]; + } + /// 添加第三方图标 + model.customViewBlock = ^(UIView * _Nonnull superCustomView) { + if (backgroundView != nil) { + [superCustomView addSubview: backgroundView]; + } + for (int i = 0 ; i < customArrayView.count; i++) { + [superCustomView addSubview: customArrayView[i]]; + } + }; + + model.customViewLayoutBlock = ^( + CGSize screenSize, /// 全屏参数 + CGRect contentViewFrame, /// contentView参数 + CGRect navFrame, /// 导航参数 + CGRect titleBarFrame, /// title参数 + CGRect logoFrame, /// logo区域参数 + CGRect sloganFrame, /// slogan参数 + CGRect numberFrame, /// 号码处参数 + CGRect loginFrame, /// 登录按钮处的参数 + CGRect changeBtnFrame, /// 切换到其他的参数 + CGRect privacyFrame /// 协议区域的参数 + ) { + if (backgroundView != nil) { + backgroundView.frame = CGRectMake(0, -CGRectGetMaxY(navFrame), contentViewFrame.size.width, contentViewFrame.size.height); + } + NSUInteger count = customArrayView.count; + NSInteger contentWidth = screenSize.width; + /// 弹窗模式需要重新获取他的宽度 + if (PNSBuildModelStyleAlertPortrait == style || PNSBuildModelStyleAlertLandscape == style){ + contentWidth = [dict intValueForKey: @"dialogWidth" defaultValue: 0]; + } + for (int i = 0 ; i < count; i++) { + UIButton *itemView = (UIButton *)customArrayView[i]; + NSInteger X = (contentWidth - (width * count + space * (count - 1))) / 2 + (space + width) * i; /// 两端评分 + itemView.frame = CGRectMake( X, offsetY, itemView.frame.size.width, itemView.frame.size.height ); + } + }; + } + } + #pragma mark 9、协议栏 + if (!model.checkBoxIsHidden) { + UIImage* unchecked = [self changeUriPathToImage: [dict stringValueForKey: @"uncheckedImgPath" defaultValue: nil]]; + UIImage* checked = [self changeUriPathToImage: [dict stringValueForKey: @"checkedImgPath" defaultValue: nil]]; + if (unchecked != nil && checked != nil) { + model.checkBoxImages = @[ + unchecked, + checked + ]; + } + } + /// 协议1,[协议名称,协议Url] + model.privacyOne = @[ + [dict stringValueForKey: @"protocolOneName" defaultValue: @""], + [dict stringValueForKey: @"protocolOneURL" defaultValue: @""] + ]; + /// 协议2,[协议名称,协议Url] + model.privacyTwo = @[ + [dict stringValueForKey: @"protocolTwoName" defaultValue: @""], + [dict stringValueForKey: @"protocolTwoURL" defaultValue: @""] + ]; + /// 协议3,[协议名称,协议Url] + model.privacyThree = @[ + [dict stringValueForKey: @"protocolThreeName" defaultValue: @""], + [dict stringValueForKey: @"protocolThreeURL" defaultValue: @""] + ]; + /// 扩大选区 + model.expandAuthPageCheckedScope = YES; + model.privacyFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + if ([dict floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.y = [dict floatValueForKey: @"privacyOffsetY" defaultValue: -1]; + } + if ([dict floatValueForKey: @"privacyOffsetY" defaultValue: -1] > -1) { + frame.origin.x = [dict floatValueForKey: @"privacyOffsetX" defaultValue: -1]; + } + return frame; + }; + #pragma mark 10、弹窗样式 + if (PNSBuildModelStyleAlertPortrait == style || PNSBuildModelStyleAlertLandscape == style || PNSBuildModelStyleSheetPortrait == style) { + model.alertCloseImage = model.alertCloseImage?:[UIImage imageNamed:@"icon_close_light"]; + model.alertCloseItemFrameBlock = ^CGRect(CGSize screenSize,CGSize superViewSize,CGRect frame) { + if ([self isHorizontal:screenSize]) { + //横屏时模拟隐藏该控件 + return CGRectZero; + } else { + frame.origin.x = [dict intValueForKey: @"alertCloseImageX" defaultValue: 5]; + frame.origin.y = [dict intValueForKey: @"alertCloseImageY" defaultValue: 5]; + frame.size.width = [dict intValueForKey: @"alertCloseImageW" defaultValue: 30]; + frame.size.height = [dict intValueForKey: @"alertCloseImageH" defaultValue: 30]; + return frame; + } + }; + + if (PNSBuildModelStyleAlertPortrait == style) { + CGFloat ratio = MAX(TX_SCREEN_WIDTH, TX_SCREEN_HEIGHT) / 667.0; + //实现该block,并且返回的frame的x或y大于0,则认为是弹窗谈起授权页 + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize contentSize, CGRect frame) { + CGFloat alertX = 0; + CGFloat alertY = 0; + CGFloat alertWidth = 0; + CGFloat alertHeight = 0; + if ([self isHorizontal:screenSize]) { + alertX = ratio * TX_Alert_Horizontal_Default_Left_Padding; + alertWidth = [dict intValueForKey: @"dialogWidth" defaultValue: screenSize.width - alertX * 2]; + alertY = (screenSize.height - alertWidth * 0.5) * 0.5; + alertHeight = [dict intValueForKey: @"dialogHeight" defaultValue: screenSize.height - 2 * alertY]; + } else { + alertWidth = [dict intValueForKey: @"dialogWidth" defaultValue: screenSize.width / 2]; + alertHeight = [dict intValueForKey: @"dialogHeight" defaultValue: screenSize.height / 2]; + alertX = [dict intValueForKey: @"dialogOffsetX" defaultValue: (TX_SCREEN_WIDTH - alertWidth) / 2]; + alertY = [dict intValueForKey: @"dialogOffsetY" defaultValue: (TX_SCREEN_HEIGHT - alertHeight) / 2]; + } + return CGRectMake(alertX, alertY, alertWidth, alertHeight); + }; + } else if (PNSBuildModelStyleSheetPortrait == style) { + model.contentViewFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + frame.size.width = superViewSize.width; + frame.size.height = [dict floatValueForKey: @"dialogHeight" defaultValue: 460]; + frame.origin.x = 0; + frame.origin.y = superViewSize.height - frame.size.height; + return frame; + }; + } + } + + #pragma mark 10、二次弹窗 + model.privacyAlertTitleFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + // return CGRectMake(0, 20, frame.size.width, frame.size.height); + return CGRectMake( + 0, + [dict floatValueForKey: @"privacyAlertTitleOffsetY" defaultValue: 20], + frame.size.width, + frame.size.height + ); + }; + /// 弹窗大小 + model.privacyAlertFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + return CGRectMake( + [dict floatValueForKey: @"privacyAlertOffsetX" defaultValue: 40], + [dict floatValueForKey: @"privacyAlertOffsetY" defaultValue: frame.origin.y], + [dict floatValueForKey: @"privacyAlertWidth" defaultValue: frame.size.width - 80], + [dict floatValueForKey: @"privacyAlertHeight" defaultValue: 200] + ); + }; + /// 确认按钮 + model.privacyAlertButtonFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + // return CGRectMake(frame.origin.x,superViewSize.height - 50 - 20, frame.size.width, 50); + return CGRectMake( + frame.origin.x, + frame.origin.y, + [dict floatValueForKey: @"privacyAlertBtnWidth" defaultValue: frame.size.width], + [dict floatValueForKey: @"privacyAlertBtnHeigth" defaultValue: 50] + ); + }; + + + #pragma mark 屏幕方向 + if (model.privacyAlertIsNeedShow) { + model.privacyAlertTitleFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + return CGRectMake(0, 20, frame.size.width, frame.size.height); + }; + model.privacyAlertPrivacyContentFrameBlock = ^CGRect(CGSize screenSize, CGSize superViewSize, CGRect frame) { + return CGRectMake(0, frame.origin.y+10, frame.size.width, frame.size.height); + }; + } + + // 设置后点击富文本不会直接跳转页面 + // model.privacyVCIsCustomized = YES; + + // ========== instantTransition: 无动画过渡(瞬间出现/消失) ========== + if ([dict boolValueForKey:@"instantTransition" defaultValue:NO]) { + CABasicAnimation *entryAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + entryAnimation.fromValue = @1; + entryAnimation.toValue = @1; + entryAnimation.duration = 0.01; + model.entryAnimation = entryAnimation; + + CABasicAnimation *exitAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + exitAnimation.fromValue = @1; + exitAnimation.toValue = @0; + exitAnimation.duration = 0.01; + model.exitAnimation = exitAnimation; + } + + // ========== customTitleText: 自定义标题(通过 customViewBlock 添加) ========== + NSString *customTitleText = [dict stringValueForKey:@"customTitleText" defaultValue:nil]; + if (customTitleText != nil && customTitleText.length > 0) { + void (^existingViewBlock)(UIView *) = model.customViewBlock; + void (^existingLayoutBlock)(CGSize, CGRect, CGRect, CGRect, CGRect, CGRect, CGRect, CGRect, CGRect, CGRect) = model.customViewLayoutBlock; + + NSString *titleColorStr = [dict stringValueForKey:@"customTitleColor" defaultValue:@"#6366F1"]; + CGFloat titleSize = [dict floatValueForKey:@"customTitleSize" defaultValue:28]; + CGFloat titleOffsetY = [dict floatValueForKey:@"customTitleOffsetY" defaultValue:60]; + UIColor *titleColor = [self getColor:titleColorStr]; + + UILabel *titleLabel = [[UILabel alloc] init]; + titleLabel.text = customTitleText; + titleLabel.textColor = titleColor; + titleLabel.font = [UIFont fontWithName:@"PressStart2P-Regular" size:titleSize] ?: [UIFont boldSystemFontOfSize:titleSize]; + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.layer.shadowColor = [titleColor colorWithAlphaComponent:0.3].CGColor; + titleLabel.layer.shadowOffset = CGSizeMake(2, 2); + titleLabel.layer.shadowRadius = 0; + titleLabel.layer.shadowOpacity = 1.0; + titleLabel.tag = 9999; + + model.customViewBlock = ^(UIView * _Nonnull superCustomView) { + [superCustomView addSubview:titleLabel]; + if (existingViewBlock) { + existingViewBlock(superCustomView); + } + }; + + model.customViewLayoutBlock = ^( + CGSize screenSize, + CGRect contentViewFrame, + CGRect navFrame, + CGRect titleBarFrame, + CGRect logoFrame, + CGRect sloganFrame, + CGRect numberFrame, + CGRect loginFrame, + CGRect changeBtnFrame, + CGRect privacyFrame + ) { + CGFloat labelWidth = contentViewFrame.size.width; + titleLabel.frame = CGRectMake(0, titleOffsetY, labelWidth, titleSize + 10); + if (existingLayoutBlock) { + existingLayoutBlock(screenSize, contentViewFrame, navFrame, titleBarFrame, logoFrame, sloganFrame, numberFrame, loginFrame, changeBtnFrame, privacyFrame); + } + }; + } + + model.supportedInterfaceOrientations = UIInterfaceOrientationMaskPortrait; + return model; +} + +/** + 16进制颜色转换为UIColor + @param hexColor 16进制字符串(可以以0x开头,可以以#开头,也可以就是6位的16进制) + @return 16进制字符串对应的颜色 + */ ++(UIColor *) getColor:(NSString *)hexColor{ + if (hexColor.length < 8) { + return [self colorWithHexString: hexColor alpha: 1]; + } + + unsigned int alpha, red, green, blue; + NSRange range; + range.length =2; + + range.location =1; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&alpha];//透明度 + range.location =3; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&red]; + range.location =5; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&green]; + range.location =7; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&blue]; + return [UIColor colorWithRed:(float)(red/255.0f)green:(float)(green/255.0f)blue:(float)(blue/255.0f)alpha:(float)(alpha/255.0f)]; +} + +/** + 16进制颜色转换为UIColor + @param hexColor 16进制字符串(可以以0x开头,可以以#开头,也可以就是6位的16进制) + @param opacity 透明度 + @return 16进制字符串对应的颜色 + */ ++(UIColor *)colorWithHexString:(NSString *)hexColor alpha:(float)opacity{ + NSString * cString = [[hexColor stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; + + + // String should be 6 or 8 characters + if ([cString length] < 6) return [UIColor blackColor]; + + // strip 0X if it appears + if ([cString hasPrefix:@"0X"]) cString = [cString substringFromIndex:2]; + if ([cString hasPrefix:@"#"]) cString = [cString substringFromIndex:1]; + + if ([cString length] != 6) return [UIColor blackColor]; + + // Separate into r, g, b substrings + NSRange range; + range.location = 0; + range.length = 2; + NSString * rString = [cString substringWithRange:range]; + + range.location = 2; + NSString * gString = [cString substringWithRange:range]; + + range.location = 4; + NSString * bString = [cString substringWithRange:range]; + + // Scan values + unsigned int r, g, b; + [[NSScanner scannerWithString:rString] scanHexInt:&r]; + [[NSScanner scannerWithString:gString] scanHexInt:&g]; + [[NSScanner scannerWithString:bString] scanHexInt:&b]; + + return [UIColor colorWithRed:((float)r / 255.0f) + green:((float)g / 255.0f) + blue:((float)b / 255.0f) + alpha:opacity]; +} + ++ (UIColor *)colorWithHex:(NSInteger)hex alpha:(CGFloat)alpha { + return [UIColor colorWithRed:((float)((hex & 0xFF0000) >> 16))/255.0 green:((float)((hex & 0xFF00) >> 8))/255.0 blue:((float)(hex & 0xFF))/255.0 alpha:alpha]; +} + ++ (void) clickAllScreen:(UITapGestureRecognizer *) recognizer { + NSLog(@"点击事件屏蔽"); +} + +#pragma mark 处理参数 +/** + * 处理参数,对参数进行处理包含color、Path + * @param parmas 字典数据 + * @return 处理后的数据 + */ ++ (NSDictionary *) formatParmas: (NSDictionary *)parmas{ + NSArray *keysArray = [parmas allKeys]; + for (int i = 0; i < keysArray.count; i++) { + NSString *key = keysArray[i]; + NSString *value = parmas[key]; + //根据键值处理字典中的每一项 + if ([key containsString: @"color"] && [value containsString: @"#"]) + { + [parmas setValue: [self getColor: value] forKey: key]; + } + // 判断是否时路径字段 + // 排除按钮状态的背景logBtnBackgroundPath + else if ( + ![key containsString: @"logBtnBackgroundPath"] && + ([key containsString: @"path"] || [key containsString: @"Path"]) && + ![value containsString: @"http"] && ![value isEqual: nil] && ![value isEqual: @""]) + { + [parmas setValue: [self changeUriToPath: value] forKey: key]; + } + } + + return parmas; +} + +#pragma mark ======获取flutterVc======== ++(FlutterViewController *)flutterVC{ + UIViewController * viewController = [UIApplication sharedApplication].keyWindow.rootViewController; + if ([viewController isKindOfClass: [FlutterViewController class]]) { + return (FlutterViewController *)viewController; + } else { + return (FlutterViewController *)[self findCurrentViewController]; + } +} ++ (UIViewController *)getRootViewController { + UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; + return window.rootViewController; +} +#pragma mark ======在view上添加UIViewController======== ++ (UIViewController *)findCurrentViewController{ + UIWindow *window = [[UIApplication sharedApplication].delegate window]; + UIViewController *topViewController = [window rootViewController]; + while (true) { + if (topViewController.presentedViewController) { + topViewController = topViewController.presentedViewController; + } else if ([topViewController isKindOfClass:[UINavigationController class]] && [(UINavigationController*)topViewController topViewController]) { + topViewController = [(UINavigationController *)topViewController topViewController]; + } else if ([topViewController isKindOfClass:[UITabBarController class]]) { + UITabBarController *tab = (UITabBarController *)topViewController; + topViewController = tab.selectedViewController; + } else { + break; + } + } + return topViewController; +} + +#pragma mark assets -> 转换成真实路径 ++ (NSString *) changeUriToPath:(NSString *) key{ + NSString* keyPath = [[self flutterVC] lookupKeyForAsset: key]; + NSString* path = [[NSBundle mainBundle] pathForResource: keyPath ofType:nil]; + return path; +} + +#pragma mark assets -> 真实路径转成UIImage ++ (UIImage *) changeUriPathToImage:(NSString *) key{ + if (key == nil || [key isEqual: @""]) return nil; + // NSLog(@"路径为:%@", key); + NSString* path = [self changeUriToPath: key]; + UIImage * image = [UIImage imageWithContentsOfFile: path]; + return image; +} + +/// 是否是横屏 YES:横屏 NO:竖屏 ++ (BOOL)isHorizontal:(CGSize)size { + return size.width > size.height; +} +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.h b/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.h new file mode 100644 index 0000000..72780c5 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.h @@ -0,0 +1,20 @@ +// +// UIColor+Hex.h +// Pods +// +// Created by Yau on 2023/4/13. +// + +#import +#import + +#ifndef UIColor_Hex_h +#define UIColor_Hex_h + +@interface UIColor (Hex) ++ (UIColor*)colorWithHex:(NSString *)hex defaultValue:(NSString *)defaultValue; ++ (UIColor*)colorWithHexString:(NSString *)hex alpha: (CGFloat)opacity defaultValue:(UIColor *)defaultValue; ++ (UIColor*)colorWithAlphaHex:(NSInteger)hex alpha: (CGFloat)opacity defaultValue:(UIColor *)defaultValue; +@end + +#endif /* UIColor_Hex_h */ diff --git a/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.m b/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.m new file mode 100644 index 0000000..6b0e61e --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/Utils/UIColor+Hex.m @@ -0,0 +1,83 @@ +// +// UIColor+Hex.m +// ali_auth +// +// Created by Yau on 2023/4/13. +// +#import "UIColor+Hex.h" + +@implementation UIColor (Hex) ++ (UIColor*)colorWithHex:(NSString*)hexColor defaultValue:(NSString*)defaultValue { + if ( + hexColor == nil || [hexColor isEqual: @""] + ) { + hexColor = defaultValue; + } + + if (hexColor.length < 8) { + return [self colorWithHexString: hexColor alpha: 1 defaultValue: [UIColor blueColor]]; + } + + unsigned int alpha, red, green, blue; + NSRange range; + range.length =2; + + range.location =1; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&alpha];//透明度 + range.location =3; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&red]; + range.location =5; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&green]; + range.location =7; + [[NSScanner scannerWithString:[hexColor substringWithRange:range]]scanHexInt:&blue]; + return [UIColor colorWithRed:(float)(red/255.0f)green:(float)(green/255.0f)blue:(float)(blue/255.0f)alpha:(float)(alpha/255.0f)]; +} + + +/** + 16进制颜色转换为UIColor + @param hexColor 16进制字符串(可以以0x开头,可以以#开头,也可以就是6位的16进制) + @param opacity 透明度 + @return 16进制字符串对应的颜色 + */ ++(UIColor *)colorWithHexString:(NSString *)hexColor alpha:(CGFloat)opacity defaultValue:(UIColor*)defaultValue{ + NSString * cString = [[hexColor stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; + + + // String should be 6 or 8 characters + if ([cString length] < 6) return [UIColor blackColor]; + + // strip 0X if it appears + if ([cString hasPrefix:@"0X"]) cString = [cString substringFromIndex:2]; + if ([cString hasPrefix:@"#"]) cString = [cString substringFromIndex:1]; + + if ([cString length] != 6) return [UIColor blackColor]; + + // Separate into r, g, b substrings + NSRange range; + range.location = 0; + range.length = 2; + NSString * rString = [cString substringWithRange:range]; + + range.location = 2; + NSString * gString = [cString substringWithRange:range]; + + range.location = 4; + NSString * bString = [cString substringWithRange:range]; + + // Scan values + unsigned int r, g, b; + [[NSScanner scannerWithString:rString] scanHexInt:&r]; + [[NSScanner scannerWithString:gString] scanHexInt:&g]; + [[NSScanner scannerWithString:bString] scanHexInt:&b]; + + return [UIColor colorWithRed:((float)r / 255.0f) + green:((float)g / 255.0f) + blue:((float)b / 255.0f) + alpha:opacity]; +} + ++ (UIColor *)colorWithAlphaHex:(NSInteger)hex alpha:(CGFloat)alpha defaultValue:(UIColor*)defaultValue{ + return [UIColor colorWithRed:((float)((hex & 0xFF0000) >> 16))/255.0 green:((float)((hex & 0xFF00) >> 8))/255.0 blue:((float)(hex & 0xFF))/255.0 alpha:alpha]; +} +@end diff --git a/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.h b/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.h new file mode 100644 index 0000000..96a5a8c --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.h @@ -0,0 +1,16 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PNSBackgroundView : UIView + +@property (nonatomic, strong) NSURL *gifUrl; + +@property (nonatomic, strong) NSURL *videoUrl; + +- (void)show; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.m b/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.m new file mode 100644 index 0000000..39b51a8 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/Classes/view/PNSBackgroundView.m @@ -0,0 +1,73 @@ + +#import "PNSBackgroundView.h" +#import +#import "UIImageView+WebCache.h" + +@interface PNSBackgroundView () +@property (nonatomic, strong) UIImageView *imgView; +@property (nonatomic, strong) AVPlayerLayer *playerLayer; +@end + +@implementation PNSBackgroundView + +- (void)show { + // 构建 GIF 背景 + self.imgView = [self buildGIFViewWithUrl:self.gifUrl color:UIColor.blackColor]; + if (self.imgView) { + [self addSubview:self.imgView]; + } + // 构建 video 背景 + self.playerLayer = [self buildVideoViewWithUrl:self.videoUrl color:UIColor.blackColor]; + if (self.playerLayer) { + //设置 session 防止切断系统其他声音 + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; + [self.layer addSublayer:self.playerLayer]; + [self.playerLayer.player play]; //开始播放 + //注册播放完通知,实现循环播放 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(playbackFinished:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:self.playerLayer.player.currentItem]; + } +} + +- (void)playbackFinished:(NSNotification *)notification { + [self.playerLayer.player seekToTime:CMTimeMake(0, 1)]; + [self.playerLayer.player play]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + self.playerLayer.frame = self.bounds; + self.imgView.frame = self.bounds; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (UIImageView *)buildGIFViewWithUrl:(NSURL *)url color:(UIColor *)color { + if (url == nil) { return nil; } + UIImageView *view = [[UIImageView alloc] init]; + view.contentMode = UIViewContentModeScaleAspectFill; + [view sd_setImageWithURL:url]; + if (color) { + view.backgroundColor = color; + } + return view; +} + +- (AVPlayerLayer *)buildVideoViewWithUrl:(NSURL *)url color:(UIColor *)color { + if (url == nil) { return nil; } + AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url]; + AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem]; + player.muted = YES; + AVPlayerLayer *playLayer = [AVPlayerLayer playerLayerWithPlayer:player]; + playLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + if (color) { + playLayer.backgroundColor = color.CGColor; + } + return playLayer; +} + +@end diff --git a/airhub_app/packages/ali_auth/ios/ali_auth.podspec b/airhub_app/packages/ali_auth/ios/ali_auth.podspec index 537f534..2bec730 100644 --- a/airhub_app/packages/ali_auth/ios/ali_auth.podspec +++ b/airhub_app/packages/ali_auth/ios/ali_auth.podspec @@ -1,13 +1,42 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# Pod::Spec.new do |s| s.name = 'ali_auth' s.version = '1.3.7' - s.summary = 'Alibaba Cloud phone auth plugin for Flutter.' - s.homepage = 'https://github.com/CodeGather/flutter_ali_auth' - s.license = { :type => 'MIT' } - s.author = { 'sean' => 'author@example.com' } - s.source = { :http => 'https://github.com/CodeGather/flutter_ali_auth' } - s.source_files = 'Classes/**/*' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC + 是一个集成阿里云号码认证服务SDK的flutter插件 + DESC + s.homepage = 'http://ki5k.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'raohong07@163.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' - s.platform = :ios, '13.0' - s.swift_version = '5.0' + s.dependency 'SDWebImage' + s.dependency 'MJExtension' + # s.dependency 'MBProgressHUD' + + s.frameworks = 'Network' + + s.vendored_frameworks = 'libs/ATAuthSDK.framework', 'libs/YTXMonitor.framework', 'libs/YTXOperators.framework' + s.static_framework = false + + # 解决移动crash + s.xcconfig = { + 'OTHER_LDFLAGS' => '-ObjC', + 'ENABLE_BITCODE' => 'NO' + } + + # 加载静态资源 + s.resources = ['Assets/*'] + + s.ios.deployment_target = '11.0' + # s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.pod_target_xcconfig = {'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } + s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } end + diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK new file mode 100644 index 0000000..0941500 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@2x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@2x.png new file mode 100644 index 0000000..db2ab5b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@3x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@3x.png new file mode 100644 index 0000000..61e1dd6 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_check@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@2x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@2x.png new file mode 100644 index 0000000..96f784b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@3x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@3x.png new file mode 100644 index 0000000..d4f2dad Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_close_gray@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@2x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@2x.png new file mode 100755 index 0000000..c97068b Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@3x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@3x.png new file mode 100755 index 0000000..4ee63e0 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@2x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@2x.png new file mode 100755 index 0000000..4faed82 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@3x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@3x.png new file mode 100755 index 0000000..e4e17e4 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@2x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@2x.png new file mode 100644 index 0000000..e200be8 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@2x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@3x.png b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@3x.png new file mode 100644 index 0000000..bdc8daa Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/ATAuthSDK.bundle/icon_uncheck@3x.png differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/ATAuthSDK.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/ATAuthSDK.h new file mode 100644 index 0000000..aa8323a --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/ATAuthSDK.h @@ -0,0 +1,23 @@ +// +// ATAuthSDK.h +// ATAuthSDK +// +// Created by yangli on 2020/11/11. +// Copyright © 2020. All rights reserved. +// + +#import + +//! Project version number for ATAuthSDK. +FOUNDATION_EXPORT double ATAuthSDKVersionNumber; + +//! Project version string for ATAuthSDK. +FOUNDATION_EXPORT const unsigned char ATAuthSDKVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import "TXCommonHandler.h" +#import "TXCommonUtils.h" +#import "PNSReturnCode.h" +#import "TXCustomModel.h" +#import "PNSReporter.h" diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReporter.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReporter.h new file mode 100644 index 0000000..31e4cbc --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReporter.h @@ -0,0 +1,37 @@ +// +// PNSReporter.h +// ATAuthSDK +// +// Created by 刘超的MacBook on 2020/5/21. +// Copyright © 2020. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, PNSLoggerLevel) { + PNSLoggerLevelVerbose = 1, + PNSLoggerLevelDebug, + PNSLoggerLevelInfo, + PNSLoggerLevelWarn, + PNSLoggerLevelError +}; + +@interface PNSReporter : NSObject + +/** + * 控制台日志输出开关,若开启会以PNS_LOGGER为开始标记对日志进行输出,Release模式记得关闭! + * @param enable 开关参数,默认为NO + */ +- (void)setConsolePrintLoggerEnable:(BOOL)enable; + +/** + * 设置埋点上传开关,但不会对通过 setupUploader: 接口实现的自定义上传方法起作用 + * @param enable 开关设置BOOL值,默认为YES + */ +- (void)setUploadEnable:(BOOL)enable DEPRECATED_MSG_ATTRIBUTE("日志不再上传");; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReturnCode.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReturnCode.h new file mode 100644 index 0000000..0b5ecce --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/PNSReturnCode.h @@ -0,0 +1,88 @@ +// +// PNSReturnCode.h +// ATAuthSDK +// +// Created by 刘超的MacBook on 2019/9/4. +// Copyright © 2019. All rights reserved. +// + +#ifndef PNSReturnCode_h +#define PNSReturnCode_h + +#import + +#pragma mark - 该返回码为阿里云号码认证SDK⾃身的返回码,请注意600011及600012错误内均含有运营商返回码,具体错误在碰到之后查阅 https://help.aliyun.com/document_detail/85351.html?spm=a2c4g.11186623.6.561.32a7360cxvWk6H + + +/// 接口成功 +static NSString * const PNSCodeSuccess = @"600000"; +/// 获取运营商配置信息失败 +static NSString * const PNSCodeGetOperatorInfoFailed = @"600004"; +/// 未检测到sim卡 +static NSString * const PNSCodeNoSIMCard = @"600007"; +/// 蜂窝网络未开启或不稳定 +static NSString * const PNSCodeNoCellularNetwork = @"600008"; +/// 无法判运营商 +static NSString * const PNSCodeUnknownOperator = @"600009"; +/// 未知异常 +static NSString * const PNSCodeUnknownError = @"600010"; +/// 获取token失败 +static NSString * const PNSCodeGetTokenFailed = @"600011"; +/// 预取号失败 +static NSString * const PNSCodeGetMaskPhoneFailed = @"600012"; +/// 运营商维护升级,该功能不可用 +static NSString * const PNSCodeInterfaceDemoted = @"600013"; +/// 运营商维护升级,该功能已达最大调用次数 +static NSString * const PNSCodeInterfaceLimited = @"600014"; +/// 接口超时 +static NSString * const PNSCodeInterfaceTimeout = @"600015"; +/// AppID、Appkey解析失败 +static NSString * const PNSCodeDecodeAppInfoFailed = @"600017"; +/// 该号码已被运营商管控,目前只有联通号码有该功能 +static NSString * const PNSCodePhoneBlack = @"600018"; +/// 运营商已切换 +static NSString * const PNSCodeCarrierChanged = @"600021"; +/// 终端环境检测失败(终端不支持认证 / 终端检测参数错误) +static NSString * const PNSCodeEnvCheckFail = @"600025"; + +/*************** 号码认证授权页相关返回码 START ***************/ + +/// 唤起授权页成功 +static NSString * const PNSCodeLoginControllerPresentSuccess = @"600001"; +/// 唤起授权页失败 +static NSString * const PNSCodeLoginControllerPresentFailed = @"600002"; +/// 授权页已加载时不允许调用加速或预取号接口 +static NSString * const PNSCodeCallPreLoginInAuthPage = @"600026"; +/// 点击返回,⽤户取消一键登录 +static NSString * const PNSCodeLoginControllerClickCancel = @"700000"; +/// 点击切换按钮,⽤户取消免密登录 +static NSString * const PNSCodeLoginControllerClickChangeBtn = @"700001"; +/// 点击登录按钮事件 +static NSString * const PNSCodeLoginControllerClickLoginBtn = @"700002"; +/// 点击CheckBox事件 +static NSString * const PNSCodeLoginControllerClickCheckBoxBtn = @"700003"; +/// 点击协议富文本文字 +static NSString * const PNSCodeLoginControllerClickProtocol = @"700004"; +/// 中断页面消失的时候,也就是suspendDisMissVC设置为YES的时候,点击左上角返回按钮时透出的状态码 +static NSString * const PNSCodeLoginControllerSuspendDisMissVC = @"700010"; +/// 授权页已销毁 +static NSString * const PNSCodeLoginControllerDeallocVC = @"700020"; + + +/*************** 号码认证授权页相关返回码 FINISH ***************/ + + +/*************** 二次授权页返回code码 START ***************/ + +/// 点击一键登录拉起授权页二次弹窗 +static NSString * const PNSCodeLoginClickPrivacyAlertView = @"700006"; +/// 隐私协议二次弹窗关闭 +static NSString * const PNSCodeLoginPrivacyAlertViewClose = @"700007"; +/// 隐私协议二次弹窗点击确认并继续 +static NSString * const PNSCodeLoginPrivacyAlertViewClickContinue = @"700008"; +/// 点击隐私协议二次弹窗上的协议富文本文字 +static NSString * const PNSCodeLoginPrivacyAlertViewPrivacyContentClick = @"700009"; + +/*************** 二次授权页返回code码 FINISH ***************/ + +#endif /* PNSReturnCode_h */ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonHandler.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonHandler.h new file mode 100644 index 0000000..a46f533 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonHandler.h @@ -0,0 +1,138 @@ +// +// TXCommonHandler.h +// ATAuthSDK +// +// Created by yangli on 15/03/2018. + +#import +#import "TXCustomModel.h" +#import "PNSReporter.h" + + +typedef NS_ENUM(NSInteger, PNSAuthType) { + PNSAuthTypeVerifyToken = 1, //本机号码校验 + PNSAuthTypeLoginToken = 2 //一键登录 +}; + +@interface TXCommonHandler : NSObject + +/** + * 获取该类的单例实例对象 + * @return 单例实例对象 + */ ++ (instancetype _Nonnull )sharedInstance; + +/** + * 获取当前SDK版本号 + * @return 字符串,sdk版本号 + */ +- (NSString *_Nonnull)getVersion; + +/** + * SDK鉴权,app生命周期内调用一次 + * @param info app对应的秘钥 + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode + * @note 重复调用时以最新info信息为准 + */ +- (void)setAuthSDKInfo:(NSString * _Nonnull)info complete:(void(^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 检查当前环境是否支持一键登录或号码认证,resultDic 返回 PNSCodeSuccess 说明当前环境支持 + * @param authType 服务类型 PNSAuthTypeVerifyToken 本机号码校验流程,PNSAuthTypeLoginToken 一键登录流程 + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode,只有成功回调才能保障后续接口调用 + */ +- (void)checkEnvAvailableWithAuthType:(PNSAuthType)authType complete:(void (^_Nullable)(NSDictionary * _Nullable resultDic))complete; + +/** + * 加速获取本机号码校验token,防止调用 getVerifyTokenWithTimeout:complete: 获取token时间过长 + * @param timeout 接口超时时间,单位s,默认为3.0s + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, token:..., msg:...},其他情况时"resultCode"值请参考PNSReturnCode + */ +- (void)accelerateVerifyWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 获取本机号码校验Token + * @param timeout 接口超时时间,单位s,默认为3.0s + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, token:..., msg:...},其他情况时"resultCode"值请参考PNSReturnCode + */ +- (void)getVerifyTokenWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 加速一键登录授权页弹起,防止调用 getLoginTokenWithTimeout:controller:model:complete: 等待弹起授权页时间过长 + * @param timeout 接口超时时间,单位s,默认为3.0s + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode + */ +- (void)accelerateLoginPageWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 获取一键登录Token,调用该接口首先会弹起授权页,点击授权页的登录按钮获取Token + * @warning 注意的是,如果前面没有调用 accelerateLoginPageWithTimeout:complete: 接口,该接口内部会自动先帮我们调用,成功后才会弹起授权页,所以有一个明显的等待过程 + * @param timeout 接口超时时间,单位s,默认为3.0s + * @param controller 唤起自定义授权页的容器,内部会对其进行验证,检查是否符合条件 + * @param model 自定义授权页面选项,可为nil,采用默认的授权页面,具体请参考TXCustomModel.h文件 + * @param complete 结果异步回调到主线程,"resultDic"里面的"resultCode"值请参考PNSReturnCode,如下: + * + * 授权页控件点击事件:700000(点击授权页返回按钮)、700001(点击切换其他登录方式)、 + * 700002(点击登录按钮事件,根据返回字典里面的 "isChecked"字段来区分check box是否被选中,只有被选中的时候内部才会去获取Token)、700003(点击check box事件)、700004(点击协议富文本文字) + 接口回调其他事件:600001(授权页唤起成功)、600002(授权页唤起失败)、600000(成功获取Token)、600011(获取Token失败)、 + * 600015(获取Token超时)、600013(运营商维护升级,该功能不可用)、600014(运营商维护升级,该功能已达最大调用次数)..... + */ +- (void)getLoginTokenWithTimeout:(NSTimeInterval)timeout controller:(UIViewController *_Nonnull)controller model:(TXCustomModel *_Nullable)model complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 此接口仅用于开发期间用于一键登录页面不同机型尺寸适配调试(可支持模拟器),非正式页面,手机掩码为0,不能正常登录,请开发者注意下 + * @param controller 唤起自定义授权页的容器,内部会对其进行验证,检查是否符合条件 + * @param model 自定义授权页面选项,可为nil,采用默认的授权页面,具体请参考TXCustomModel.h文件 + * @param complete 结果异步回调到主线程,"resultDic"里面的"resultCode"值请参考PNSReturnCode + */ +- (void)debugLoginUIWithController:(UIViewController *_Nonnull)controller model:(TXCustomModel *_Nullable)model complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete; + +/** + * 授权页弹起后,修改checkbox按钮选中状态,当checkout按钮隐藏时,设置不生效 + */ +- (void)setCheckboxIsChecked:(BOOL)isChecked; + +/** + * 查询授权页checkbox是否勾选,YES:勾选,NO:未勾选 + */ +- (BOOL)queryCheckBoxIsChecked; + +/** + * 授权页协议内容动画执行,注意:必须设置privacyAnimation属性,才会执行动画 + */ +- (void)privacyAnimationStart; + +/** + * 授权页checkbox动画执行,注意:必须设置checkboxAnimation属性,才会执行动画 + */ +- (void)checkboxAnimationStart; + +/** + * 手动隐藏一键登录获取登录Token之后的等待动画,默认为自动隐藏,当设置 TXCustomModel 实例 autoHideLoginLoading = NO 时, 可调用该方法手动隐藏 + */ +- (void)hideLoginLoading; + +/** + * 注销授权页,建议用此方法,对于移动卡授权页的消失会清空一些数据 + * @param flag 是否添加动画 + * @param complete 成功返回 + */ +- (void)cancelLoginVCAnimated:(BOOL)flag complete:(void (^_Nullable)(void))complete; + +/** + * 获取日志埋点相关控制对象 + */ +- (PNSReporter * _Nonnull)getReporter; + +/** + * 关闭二次授权弹窗页 + */ +- (void)closePrivactAlertView; + +/** + * 检查及准备调用环境,resultDic返回PNSCodeSuccess才能调用下面的功能接口 + * @param complete 结果异步回调到主线程,成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode,只有成功回调才能保障后续接口调用 + */ +- (void)checkEnvAvailableWithComplete:(void (^_Nullable)(NSDictionary * _Nullable resultDic))complete DEPRECATED_MSG_ATTRIBUTE("Please use checkEnvAvailableWithAuthType:complete: instead"); + +@end diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonUtils.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonUtils.h new file mode 100644 index 0000000..a56b6db --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCommonUtils.h @@ -0,0 +1,81 @@ +// +// TXCommonUtils.h +// authsdk +// +// Created by yangli on 12/03/2018. + +#import + +@interface TXCommonUtils : NSObject + +/** + 判断当前设备蜂窝数据网络是否开启,即3G/4G + @return 结果 + */ ++ (BOOL)checkDeviceCellularDataEnable; + +/** + 判断当前上网卡运营商是否是中国联通 + @return 结果 + */ ++ (BOOL)isChinaUnicom; + +/** + 判断当前上网卡运营商是否是中国移动 + @return 结果 + */ ++ (BOOL)isChinaMobile; + +/** + 判断当前上网卡运营商是否是中国电信 + @return 结果 + */ ++ (BOOL)isChinaTelecom; + +/** + 获取当前上网卡运营商名称,比如中国移动、中国电信、中国联通 + @return 结果 + */ ++ (NSString *)getCurrentCarrierName; + +/** + 获取当前上网卡网络类型,比如WiFi,4G + @return 结果 + */ ++ (NSString *)getNetworktype; + +/** + 判断当前设备是否有SIM卡 + @return 结果 + */ ++ (BOOL)simSupportedIsOK; + +/** + 判断wwan是否开着(通过p0网卡判断,无wifi或有wifi情况下都能检测到) + @return 结果 + */ ++ (BOOL)isWWANOpen; + +/** + 判断wwan是否开着(仅无wifi情况下) + @return 结果 + */ ++ (BOOL)reachableViaWWAN; + +/** + 获取设备当前网络私网IP地址 + @return 结果 + */ ++ (NSString *)getMobilePrivateIPAddress:(BOOL)preferIPv4; + +/** + 获取当前设备的唯一标识ID + */ ++ (NSString *)getUniqueID; + +/** + 通过颜色设置生成图片,支持弧度设置,比如一键登录按钮背景图片 + */ ++ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size isRoundedCorner:(BOOL )isRounded radius:(CGFloat)radius; + +@end diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCustomModel.h b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCustomModel.h new file mode 100644 index 0000000..3cbd14a --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Headers/TXCustomModel.h @@ -0,0 +1,441 @@ +// +// TXCustomModel.h +// ATAuthSDK +// +// Created by yangli on 2019/4/4. +// Copyright © 2019. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, PNSPresentationDirection){ + PNSPresentationDirectionBottom = 0, + PNSPresentationDirectionRight, + PNSPresentationDirectionTop, + PNSPresentationDirectionLeft, +}; + +/** + * 构建控件的frame,view布局时会调用该block得到控件的frame + * @param screenSize 屏幕的size,可以通过该size来判断是横屏还是竖屏 + * @param superViewSize 该控件的super view的size,可以通过该size,辅助该控件重新布局 + * @param frame 控件默认的位置 + * @return 控件新设置的位置 + */ +typedef CGRect(^PNSBuildFrameBlock)(CGSize screenSize, CGSize superViewSize, CGRect frame); + +@interface TXCustomModel : NSObject + +/** + * 说明,可设置的Y轴距离,waring: 以下所有关于Y轴的设置<=0都将不生效,请注意 + * 全屏模式:默认是以375x667pt为基准,其他屏幕尺寸可以根据(ratio = 屏幕高度/667)比率来适配,比如 Y*ratio + */ + +#pragma mark- 全屏、弹窗模式设置 +/** + * 授权页面中,渲染并显示所有控件的view,称content view,不实现该block默认为全屏模式 + * 实现弹窗的方案 x >= 0 || y >= 0 width <= 屏幕宽度 || height <= 屏幕高度 + */ +@property (nonatomic, copy) PNSBuildFrameBlock contentViewFrameBlock; + +#pragma mark- 竖屏、横屏模式设置 +/** 屏幕是否支持旋转方向,默认UIInterfaceOrientationMaskPortrait,注意:在刘海屏,UIInterfaceOrientationMaskPortraitUpsideDown属性慎用! */ +@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; + +#pragma mark- 仅弹窗模式属性 +/** 底部蒙层背景颜色,默认黑色 */ +@property (nonatomic, strong) UIColor *alertBlurViewColor; +/** 底部蒙层背景透明度,默认0.5 */ +@property (nonatomic, assign) CGFloat alertBlurViewAlpha; +/** contentView背景颜色,默认白色 */ +@property (nonatomic, strong) UIColor *alertContentViewColor; +/** contentView背景透明度,默认1.0 ,即不透明*/ +@property (nonatomic, assign) CGFloat alertContentViewAlpha; +/** contentView的四个圆角值,顺序为左上,左下,右下,右上,需要填充4个值,不足4个值则无效,如果值<=0则为直角 */ +@property (nonatomic, copy) NSArray *alertCornerRadiusArray; +/** 标题栏背景颜色 */ +@property (nonatomic, strong) UIColor *alertTitleBarColor; +/** 标题栏是否隐藏,默认NO */ +@property (nonatomic, assign) BOOL alertBarIsHidden; +/** 标题栏标题,内容、字体、大小、颜色 */ +@property (nonatomic, copy) NSAttributedString *alertTitle; +/** 标题栏右侧关闭按钮图片设置*/ +@property (nonatomic, strong) UIImage *alertCloseImage; +/** 标题栏右侧关闭按钮是否显示,默认NO*/ +@property (nonatomic, assign) BOOL alertCloseItemIsHidden; + +/** 构建标题栏的frame,view布局或布局发生变化时调用,不实现则按默认处理,实现时仅有height生效 */ +@property (nonatomic, copy) PNSBuildFrameBlock alertTitleBarFrameBlock; +/** 构建标题栏标题的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock alertTitleFrameBlock; +/** 构建标题栏右侧关闭按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock alertCloseItemFrameBlock; + +/** 弹窗位置是否根据键盘弹起关闭动态调整,仅在键盘弹起后遮挡弹窗的情况生效,调整后弹窗将居于键盘上方,默认NO*/ +@property (nonatomic, assign) BOOL alertFrameChangeWithKeyboard; + +#pragma mark- 导航栏(只对全屏模式有效) +/**授权页显示中,导航栏是否隐藏,默认NO*/ +@property (nonatomic, assign) BOOL navIsHidden; +/**授权页push到其他页面后,导航栏是否隐藏,默认NO*/ +@property (nonatomic, assign) BOOL navIsHiddenAfterLoginVCDisappear; +/**是否需要中断返回,如果设置为YES,则点击左上角返回按钮的时候默认页面不消失,同时透出状态码700010,需要自己调用TXCommonHandler cancelLoginVCAnimated方法隐藏页面,默认为NO*/ +@property (nonatomic, assign) BOOL suspendDisMissVC; +/** 导航栏主题色 */ +@property (nonatomic, strong) UIColor *navColor; +/** 导航栏标题,内容、字体、大小、颜色 */ +@property (nonatomic, copy) NSAttributedString *navTitle; +/** 导航栏返回图片 */ +@property (nonatomic, strong) UIImage *navBackImage; +/** 是否隐藏授权页导航栏返回按钮,默认不隐藏 */ +@property (nonatomic, assign) BOOL hideNavBackItem; +/** 导航栏右侧自定义控件,可以在创建该VIEW的时候添加手势操作,或者创建按钮或其他赋值给VIEW */ +@property (nonatomic, strong) UIView *navMoreView; + +/** 构建导航栏返回按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock navBackButtonFrameBlock; +/** 构建导航栏标题的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock navTitleFrameBlock; +/** 构建导航栏右侧more view的frame,view布局或布局发生变化时调用,不实现则按默认处理,边界 CGRectGetMinX(frame) >= (superViewSizeViewSize / 0.3) && CGRectGetWidth(frame) <= (superViewSize.width / 3.0) */ +@property (nonatomic, copy) PNSBuildFrameBlock navMoreViewFrameBlock; + +#pragma mark- 全屏、弹窗模式共同属性 + +#pragma mark- 授权页动画相关 +/** 授权页弹出方向,默认PNSPresentationDirectionBottom,该属性只对自带动画起效,不影响自定义动画 */ +@property (nonatomic, assign) PNSPresentationDirection presentDirection; +/** 授权页显示和消失动画时间,默认为0.25s,<= 0 时关闭动画,该属性只对自带动画起效,不影响自定义动画 **/ +@property (nonatomic, assign) CGFloat animationDuration; + +/** 授权页显示动画(弹窗 & 全屏),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/ +@property (nonatomic, strong, nullable) CAAnimation *entryAnimation; +/** 授权页消失动画(弹窗 & 全屏),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/ +@property (nonatomic, strong, nullable) CAAnimation *exitAnimation; + +/** 授权页显示时的背景动画(仅弹窗),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/ +@property (nonatomic, strong, nullable) CAAnimation *bgEntryAnimation; +/** 授权页消失时的背景动画(仅弹窗),不设置或设置为nil默认使用自带动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate) **/ +@property (nonatomic, strong, nullable) CAAnimation *bgExitAnimation; + +#pragma mark- 状态栏 +/** 状态栏是否隐藏,默认NO */ +@property (nonatomic, assign) BOOL prefersStatusBarHidden; +/** 状态栏主题风格,默认UIStatusBarStyleDefault */ +@property (nonatomic, assign) UIStatusBarStyle preferredStatusBarStyle; + +#pragma mark- 背景 +/** 授权页背景色 */ +@property (nonatomic, strong) UIColor *backgroundColor; +/** 授权页背景图片 */ +@property (nonatomic, strong) UIImage *backgroundImage; +/** 授权页背景图片view的 content mode,默认为 UIViewContentModeScaleAspectFill */ +@property (nonatomic, assign) UIViewContentMode backgroundImageContentMode; +/** 点击授权页背景是否关闭授权页,只有在弹窗模式下生效,默认NO*/ +@property (nonatomic, assign) BOOL tapAuthPageMaskClosePage; + +#pragma mark- logo图片 +/** logo图片设置 */ +@property (nonatomic, strong) UIImage *logoImage; +/** logo是否隐藏,默认NO */ +@property (nonatomic, assign) BOOL logoIsHidden; + +/** 构建logo的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock logoFrameBlock; +/** logo的宽设置 */ +@property (nonatomic, assign) CGFloat logoWidth DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead"); +/** logo的高设置 */ +@property (nonatomic, assign) CGFloat logoHeight DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead"); +/** logo相对导航栏底部或标题栏底部的Y轴距离 */ +@property (nonatomic, assign) CGFloat logoTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead"); + +#pragma mark- slogan +/** slogan文案,内容、字体、大小、颜色 */ +@property (nonatomic, copy) NSAttributedString *sloganText; +/** slogan是否隐藏,默认NO */ +@property (nonatomic, assign) BOOL sloganIsHidden; + +/** 构建slogan的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock sloganFrameBlock; +/** slogan相对导航栏底部或标题栏底部的Y轴距离 */ +@property (nonatomic, assign) CGFloat sloganTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use sloganFrameBlock instead"); + +#pragma mark- 号码 +/** 号码颜色设置 */ +@property (nonatomic, strong) UIColor *numberColor; +/** 号码字体设置,大小小于16则不生效 */ +@property (nonatomic, strong) UIFont *numberFont; + +/** + * 构建号码的frame,view布局或布局发生变化时调用,只有x、y生效,不实现则按默认处理, + * 注:设置不能超出父视图 content view + */ +@property (nonatomic, copy) PNSBuildFrameBlock numberFrameBlock; +/** + * 号码相对导航栏底部或标题栏底部的Y轴距离,不设置则按默认处理 + * 注:设置超出父视图 content view 时不生效 + */ +@property (nonatomic, assign) CGFloat numberTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use numberFrameBlock instead"); +/** + * 号码相对屏幕中线的X轴偏移距离,不设置则按默认处理,默认为0水平居中 + * 注:设置不能超出父视图 content view + */ +@property (nonatomic, assign) CGFloat numberOffetX DEPRECATED_MSG_ATTRIBUTE("Please use numberFrameBlock instead"); + +#pragma mark- 登录 +/** 登陆按钮文案,内容、字体、大小、颜色*/ +@property (nonatomic, strong) NSAttributedString *loginBtnText; +/** 登录按钮背景图片组,默认高度50.0pt,@[激活状态的图片,失效状态的图片,高亮状态的图片] */ +@property (nonatomic, strong) NSArray *loginBtnBgImgs; +/** + * 是否自动隐藏点击登录按钮之后授权页上转圈的 loading, 默认为YES,在获取登录Token成功后自动隐藏 + * 如果设置为 NO,需要自己手动调用 [[TXCommonHandler sharedInstance] hideLoginLoading] 隐藏 + */ +@property (nonatomic, assign) BOOL autoHideLoginLoading; +/** + * 构建登录按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 + * 注:不能超出父视图 content view,height不能小于20,width不能小于父视图宽度的一半 + */ +@property (nonatomic, copy) PNSBuildFrameBlock loginBtnFrameBlock; +/** + * 登录按钮相对导航栏底部或标题栏底部的Y轴距离,不设置则按默认处理 + * 注:设置超出父视图 content view 时不生效 + */ +@property (nonatomic, assign) CGFloat loginBtnTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead"); +/** 登录按钮高度,小于20.0pt不生效,不设置则按默认处理 */ +@property (nonatomic, assign) CGFloat loginBtnHeight DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead"); +/** 登录按钮相对content view的左右边距,按钮宽度必须大于等于屏幕的一半,不设置则按默认处理 */ +@property (nonatomic, assign) CGFloat loginBtnLRPadding DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead"); + +#pragma mark- 协议 +/** checkBox图片组,[uncheckedImg,checkedImg]*/ +@property (nonatomic, copy) NSArray *checkBoxImages; +/** checkBox图片距离控件边框的填充,默认为 UIEdgeInsetsZero,确保控件大小减去内填充大小为资源图片大小情况下,图片才不会变形 **/ +@property (nonatomic, assign) UIEdgeInsets checkBoxImageEdgeInsets; +/** checkBox是否勾选,默认NO */ +@property (nonatomic, assign) BOOL checkBoxIsChecked; +/** checkBox是否隐藏,默认NO */ +@property (nonatomic, assign) BOOL checkBoxIsHidden; +/** checkBox大小,高宽一样,必须大于0 */ +@property (nonatomic, assign) CGFloat checkBoxWH; +/** checkBox是否和协议内容垂直居中,默认NO,即顶部对齐 */ +@property (nonatomic, assign) BOOL checkBoxVerticalCenter; + +/** 协议1,[协议名称,协议Url],注:三个协议名称不能相同 */ +@property (nonatomic, copy) NSArray *privacyOne; +/** 协议2,[协议名称,协议Url],注:三个协议名称不能相同 */ +@property (nonatomic, copy) NSArray *privacyTwo; +/** 协议3,[协议名称,协议Url],注:三个协议名称不能相同 */ +@property (nonatomic, copy) NSArray *privacyThree; +/** 协议名称之间连接字符串数组,默认 ["和","、","、"] ,即第一个为"和",其他为"、",按顺序读取,为空则取默认 */ +@property (nonatomic, copy) NSArray *privacyConectTexts; +/** 协议内容颜色数组,[非点击文案颜色,点击文案颜色] */ +@property (nonatomic, copy) NSArray *privacyColors; +/** 运营商协议内容颜色 ,优先级最高,如果privacyOperatorColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyOperatorColor; +/** 协议1内容颜色,优先级最高,如果privacyOneColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyOneColor; +/** 协议2内容颜色,优先级最高,如果privacyTwoColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyTwoColor; +/** 协议3内容颜色,优先级最高,如果privacyThreeColors不设置,则取privacyColors中的点击文案颜色,privacyColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyThreeColor; +/** 协议文案支持居中、居左、居右设置,默认居左 */ +@property (nonatomic, assign) NSTextAlignment privacyAlignment; +/** 协议整体文案,前缀部分文案 */ +@property (nonatomic, copy) NSString *privacyPreText; +/** 协议整体文案,后缀部分文案 */ +@property (nonatomic, copy) NSString *privacySufText; +/** 运营商协议名称前缀文案,仅支持 <([《(【『 */ +@property (nonatomic, copy) NSString *privacyOperatorPreText; +/** 运营商协议名称后缀文案,仅支持 >)]》)】』*/ +@property (nonatomic, copy) NSString *privacyOperatorSufText; +/** 运营商协议指定显示顺序,默认0,即第1个协议显示,最大值可为3,即第4个协议显示*/ +@property (nonatomic, assign) NSInteger privacyOperatorIndex; +/** 协议整体文案字体,小于12.0不生效 */ +@property (nonatomic, strong) UIFont *privacyFont; +/** 协议整体文案行间距,默认0 */ +@property (nonatomic, assign) CGFloat privacyLineSpaceDp; +/** 运营商协议文案字体,仅对运营商协议本体文案和前后缀生效,小于12.0不生效 */ +@property (nonatomic, strong) UIFont *privacyOperatorFont; +/** 运营商协议文案下划线,仅对运营商协议本体文案和前后缀生效,YES:展示下划线;NO:不展示下划线,默认不展示 */ +@property (nonatomic, assign) BOOL privacyOperatorUnderline; +/** checkBox是否扩大按钮可交互范围至"协议前缀部分文案(默认:我已阅读并同意)"区域,默认NO */ +@property (nonatomic, assign) BOOL expandAuthPageCheckedScope; + +/** + * 构建协议整体(包括checkBox)的frame,view布局或布局发生变化时调用,不实现则按默认处理 + * 如果设置的width小于checkBox的宽则不生效,最小x、y为0,最大width、height为父试图宽高 + * 最终会根据设置进来的width对协议文本进行自适应,得到的size是协议控件的最终大小 + */ +@property (nonatomic, copy) PNSBuildFrameBlock privacyFrameBlock; +/** + * 未同意协议时点击登录按钮,协议整体文案的动画效果,不设置或设置为nil默认没有动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeRemoved 及 delegate) + */ +@property (nonatomic, strong, nullable) CAAnimation *privacyAnimation; +/** + * 未同意协议时点击登录按钮,checkbox的动画效果,不设置或设置为nil默认没有动画,SDK内部会主动更改动画的一些属性(包括:removedOnCompletion = NO、fillMode = kCAFillModeRemoved 及 delegate) + */ +@property (nonatomic, strong, nullable) CAAnimation *checkboxAnimation; +/** 协议整体相对屏幕底部的Y轴距离,与其他有区别!!不能小于0 */ +@property (nonatomic, assign) CGFloat privacyBottomOffetY DEPRECATED_MSG_ATTRIBUTE("Please use privacyFrameBlock instead"); +/** 协议整体(包括checkBox)相对content view的左右边距,当协议整体宽度小于(content view宽度-2*左右边距)且居中模式,则左右边距设置无效,不能小于0 */ +@property (nonatomic, assign) CGFloat privacyLRPadding DEPRECATED_MSG_ATTRIBUTE("Please use privacyFrameBlock instead"); + +#pragma mark- 切换到其他方式 +/** changeBtn标题,内容、字体、大小、颜色 */ +@property (nonatomic, copy) NSAttributedString *changeBtnTitle; +/** changeBtn是否隐藏,默认NO*/ +@property (nonatomic, assign) BOOL changeBtnIsHidden; + +/** 构建changeBtn的frame,view布局或布局发生变化时调用,不实现则按默认处理 */ +@property (nonatomic, copy) PNSBuildFrameBlock changeBtnFrameBlock; +/** changeBtn相对导航栏底部或标题栏底部的Y轴距离 */ +@property (nonatomic, assign) CGFloat changeBtnTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use changeBtnFrameBlock instead"); + +#pragma mark- 协议详情页 +/** 协议详情页容器是否自定义,默认NO,若为YES,则根据 PNSCodeLoginControllerClickProtocol 返回码获取协议点击详情信息 */ +@property (nonatomic, assign) BOOL privacyVCIsCustomized; +/** 导航栏背景颜色设置 */ +@property (nonatomic, strong) UIColor *privacyNavColor; +/** 导航栏标题字体、大小 */ +@property (nonatomic, strong) UIFont *privacyNavTitleFont; +/** 导航栏标题颜色 */ +@property (nonatomic, strong) UIColor *privacyNavTitleColor; +/** 导航栏返回图片 */ +@property (nonatomic, strong) UIImage *privacyNavBackImage; + +#pragma mark- 其他自定义控件添加及布局 + +/** + * 自定义控件添加,注意:自定义视图的创建初始化和添加到父视图,都需要在主线程!! + * @param superCustomView 父视图 + */ +@property (nonatomic, copy) void(^customViewBlock)(UIView *superCustomView); + +/** + * 每次授权页布局完成时会调用该block,可以在该block实现里面可设置自定义添加控件的frame + * @param screenSize 屏幕的size + * @param contentViewFrame content view的frame, + * @param navFrame 导航栏的frame,仅全屏时有效 + * @param titleBarFrame 标题栏的frame,仅弹窗时有效 + * @param logoFrame logo图片的frame + * @param sloganFrame slogan的frame + * @param numberFrame 号码栏的frame + * @param loginFrame 登录按钮的frame + * @param changeBtnFrame 切换到其他方式按钮的frame + * @param privacyFrame 协议整体(包括checkBox)的frame + */ +@property (nonatomic, copy) void(^customViewLayoutBlock)(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame); + +#pragma mark - 二次隐私协议弹窗设置 +/** 二次隐私协议弹窗是否需要显示, 默认NO */ +@property (nonatomic, assign) BOOL privacyAlertIsNeedShow; +/** 二次隐私协议弹窗点击按钮是否需要执行登陆,默认YES */ +@property (nonatomic, assign) BOOL privacyAlertIsNeedAutoLogin; +/** 二次隐私协议弹窗显示自定义动画,默认从下往上位移动画 */ +@property (nonatomic, strong, nullable) CAAnimation *privacyAlertEntryAnimation; +/** 二次隐私协议弹窗隐藏自定义动画,默认从上往下位移动画 */ +@property (nonatomic, strong, nullable) CAAnimation *privacyAlertExitAnimation; +/** 二次隐私协议弹窗的四个圆角值,顺序为左上,左下,右下,右上,需要填充4个值,不足4个值则无效,如果值<=0则为直角 ,默认0*/ +@property (nonatomic, copy) NSArray *privacyAlertCornerRadiusArray; +/** 二次隐私协议弹窗背景颜色,默认为白色 */ +@property (nonatomic, strong) UIColor *privacyAlertBackgroundColor; +/** 二次隐私协议弹窗透明度,默认不透明1.0 ,设置范围0.3~1.0之间 */ +@property (nonatomic, assign) CGFloat privacyAlertAlpha; +/** 二次隐私协议弹窗标题文字内容,默认"请阅读并同意以下条款" */ +@property (nonatomic, copy) NSString *privacyAlertTitleContent; +/** 二次隐私协议弹窗标题文字字体,最小12,默认12 */ +@property (nonatomic, strong) UIFont *privacyAlertTitleFont; +/** 二次隐私协议弹窗标题文字颜色,默认黑色 */ +@property (nonatomic, strong) UIColor *privacyAlertTitleColor; +/** 二次隐私协议弹窗标题背景颜色,默认白色*/ +@property (nonatomic, strong) UIColor *privacyAlertTitleBackgroundColor; +/** 二次隐私协议弹窗标题位置,默认居中*/ +@property (nonatomic, assign) NSTextAlignment privacyAlertTitleAlignment; +/** 二次隐私协议弹窗协议内容文字字体,最小12,默认12 */ +@property (nonatomic, strong) UIFont *privacyAlertContentFont; +/** 二次隐私协议弹窗协议内容行间距,默认0 */ +@property (nonatomic, assign) CGFloat privacyAlertLineSpaceDp; +/** 二次隐私协议弹窗协议内容背景颜色,默认白色 */ +@property (nonatomic, strong) UIColor *privacyAlertContentBackgroundColor; +/** 二次隐私协议弹窗协议内容颜色数组,[非点击文案颜色,点击文案颜色],默认[0x999999,0x1890FF] */ +@property (nonatomic, copy) NSArray *privacyAlertContentColors; +/** 二次隐私协议弹窗运营商协议内容文字字体,仅对运营商协议部分的文本生效,最小12,默认12 */ +@property (nonatomic, strong) UIFont *privacyAlertContentOperatorFont; +/** 二次隐私协议弹窗运营商协议内容文字下划线,仅对运营商协议部分的文本生效,YES:展示下划线,NO:不展示下划线,默认不展示 */ +@property (nonatomic, assign) BOOL privacyAlertContentUnderline; +/** 二次隐私协议弹窗协议运营商协议内容颜色,优先级最高,如果privacyAlertOperatorColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyAlertOperatorColor; +/** 二次隐私协议弹窗协议协议1内容颜色 ,优先级最高,如果privacyAlertOneColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyAlertOneColor; +/** 二次隐私协议弹窗协议协议2内容颜色 ,优先级最高,如果privacyAlertTwoColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyAlertTwoColor; +/** 二次隐私协议弹窗协议协议3内容颜色 ,优先级最高,如果privacyAlertThreeColors不设置,则取privacyAlertContentColors中的点击文案颜色,privacyAlertContentColors不设置,则是默认色*/ +@property (nonatomic, strong) UIColor *privacyAlertThreeColor; +/** 二次隐私协议弹窗协议文案支持居中、居左、居右设置,默认居左 */ +@property (nonatomic, assign) NSTextAlignment privacyAlertContentAlignment; + +/** 二次隐私协议弹窗协议整体文案,前缀部分文案 ,如果不赋值,默认使用privacyPreText*/ +@property (nonatomic, copy) NSString *privacyAlertPreText; +/** 二次隐私协议弹窗协议整体文案,后缀部分文案 如果不赋值,默认使用privacySufText*/ +@property (nonatomic, copy) NSString *privacyAlertSufText; + +/** 二次隐私协议弹窗按钮文字内容 默认“同意”*/ +@property (nonatomic, copy) NSString *privacyAlertBtnContent; +/** 二次隐私协议弹窗登录按钮的圆角值,如果值<=0则为直角 ,默认0*/ +@property (nonatomic, assign) CGFloat privacyAlertBtnCornerRadius; +/** 二次隐私协议弹窗按钮按钮背景图片 ,默认高度50.0pt,@[激活状态的图片,高亮状态的图片] */ +@property (nonatomic, copy) NSArray *privacyAlertBtnBackgroundImages; +/** 二次隐私协议弹窗按钮文字颜色,默认黑色, @[激活状态的颜色,高亮状态的颜色] */ +@property (nonatomic, copy) NSArray *privacyAlertButtonTextColors; +/** 二次隐私协议弹窗按钮文字字体,最小10,默认18*/ +@property (nonatomic, strong) UIFont *privacyAlertButtonFont; +/** 二次隐私协议弹窗关闭按钮是否显示,默认显示 */ +@property (nonatomic, assign) BOOL privacyAlertCloseButtonIsNeedShow; +/** 二次隐私协议弹窗右侧关闭按钮图片设置,默认内置的X图片*/ +@property (nonatomic, strong) UIImage *privacyAlertCloseButtonImage; +/** 二次隐私协议弹窗背景蒙层是否显示 ,默认YES*/ +@property (nonatomic, assign) BOOL privacyAlertMaskIsNeedShow; +/** 二次隐私协议弹窗点击背景蒙层是否关闭弹窗 ,默认YES*/ +@property (nonatomic, assign) BOOL tapPrivacyAlertMaskCloseAlert; +/** 二次隐私协议弹窗蒙版背景颜色,默认黑色 */ +@property (nonatomic, strong) UIColor *privacyAlertMaskColor; +/** 二次隐私协议弹窗蒙版透明度 设置范围0.3~1.0之间 ,默认0.5*/ +@property (nonatomic, assign) CGFloat privacyAlertMaskAlpha; +/** 二次隐私协议弹窗蒙版显示动画,默认渐显动画 */ +@property (nonatomic, strong) CAAnimation *privacyAlertMaskEntryAnimation; +/** 二次隐私协议弹窗蒙版消失动画,默认渐隐动画 */ +@property (nonatomic, strong) CAAnimation *privacyAlertMaskExitAnimation; +/** 二次隐私协议弹窗尺寸设置,不能超出父视图 content view,height不能小于50,width不能小于0,默认屏幕居中,宽为屏幕的宽度减掉80,高为200 */ +@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertFrameBlock; +/** 二次隐私协议弹窗标题尺寸,默认x=0,y=0,width=弹窗宽度,最小宽度为100,height=根据文本计算的高度,最小高度为15,不能超出父视图 */ +@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertTitleFrameBlock; +/** 二次隐私协议弹窗内容尺寸,默认为从标题顶部位置开始,最终会根据设置进来的width对协议文本进行自适应,得到的size是协议控件的最终大小。不能超出父视图 */ +@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertPrivacyContentFrameBlock; +/** 二次隐私协议弹窗确认按钮尺寸,默认为父视图的宽度一半,居中显示。高度默认50, */ +@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertButtonFrameBlock; +/** 二次隐私协议弹窗右侧关闭按钮尺寸,默认宽高44,居弹窗右侧15,居弹窗顶部0*/ +@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertCloseFrameBlock; + +/** + * 二次授权页弹窗自定义控件添加,注意:自定义视图的创建初始化和添加到父视图,都需要在主线程!! + * @param superPrivacyAlertCustomView 父视图 + */ +@property (nonatomic, copy) void(^privacyAlertCustomViewBlock)(UIView *superPrivacyAlertCustomView); + +/** + * 二次授权页弹窗布局完成时会调用该block,可以在该block实现里面可设置自定义添加控件的frame + * @param privacyAlertFrame 二次授权页弹窗frame + * @param privacyAlertTitleFrame 二次授权页弹窗标题frame + * @param privacyAlertPrivacyContentFrame 二次授权页弹窗协议内容frame + * @param privacyAlertButtonFrame 二次授权页弹窗确认按钮frame + * @param privacyAlertCloseFrame 二次授权页弹窗右上角关闭按钮frame + */ +@property (nonatomic, copy) void(^privacyAlertCustomViewLayoutBlock)(CGRect privacyAlertFrame, CGRect privacyAlertTitleFrame, CGRect privacyAlertPrivacyContentFrame, CGRect privacyAlertButtonFrame, CGRect privacyAlertCloseFrame); + +@end + +NS_ASSUME_NONNULL_END + diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Info.plist b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Info.plist new file mode 100644 index 0000000..89f1d64 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Info.plist differ diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Modules/module.modulemap b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Modules/module.modulemap new file mode 100644 index 0000000..be47bf2 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module ATAuthSDK { + umbrella header "ATAuthSDK.h" + + export * + module * { export * } +} diff --git a/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/PrivacyInfo.xcprivacy b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..b426e43 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/ATAuthSDK.framework/PrivacyInfo.xcprivacy @@ -0,0 +1,42 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDataTypes + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMLogger.h b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMLogger.h new file mode 100644 index 0000000..9abd531 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMLogger.h @@ -0,0 +1,48 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 日志级别 +extern NSString * const ACM_LOGGER_LEVEL_VERBOSE; +extern NSString * const ACM_LOGGER_LEVEL_DEBUG; +extern NSString * const ACM_LOGGER_LEVEL_INFO; +extern NSString * const ACM_LOGGER_LEVEL_WARN; +extern NSString * const ACM_LOGGER_LEVEL_ERROR; +extern NSString * const ACM_LOGGER_LEVEL_REALTIME; + +@interface ACMLogger : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// 日志是否入库,默认不入库 +@property (nonatomic, assign) BOOL enterDatabase;; + +/// 日志是否允许上传,默认不上传 +@property (nonatomic, assign) BOOL isAllowUpload; + +/** + * 日志入库 + * @param obj 日志的具体内容 + * @param level 日志等级 + */ +- (BOOL)logger:(id)obj level:(NSString *)level ; + +/** + * 上传日志 + * @param startDate 日志开始时间,如果传nil则查询不加该条件 + * @param endDate 日志结束时间,如果传nil则查询不加该条件 + * @param levels 日志等级数组,里面包含对应的日志等级字符串,如果传nil则查询不加该条件 + */ +- (void)uploadLoggersWithLevels:(NSArray * _Nullable)levels + startDate:(NSDate * _Nullable)startDate + endDate:(NSDate * _Nullable)endDate; + +/** + * 上传失败的日志,一般放在重启应用后 + */ +- (void)uploadFailedRecords; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMManager.h b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMManager.h new file mode 100644 index 0000000..9c4858a --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMManager.h @@ -0,0 +1,73 @@ + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ACMManager : NSObject + +/// 日志操作对象 +@property (nonatomic, strong, readonly) ACMLogger *logger; + +/// 埋点操作对象 +@property (nonatomic, strong, readonly) ACMMonitor *monitor; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * 初始化 + * @param databaseName 数据库名,不指定则默认为 “ACMDatabase” + * @param monitorTableName 埋点表名,必须要指定,用来区分不同产品数据 + * @param loggerTablename 日志表名,必须要指定,用来区分不同产品数据 + * @param limitKeyPrefix 限流信息存储到本地key的前缀,用来区分不同产品的限流缓存 + */ +- (instancetype)initWithDatabaseName:(NSString * _Nullable)databaseName + monitorTableName:(NSString *)monitorTableName + loggerTableName:(NSString *)loggerTablename + limitKeyPrefix:(NSString *)limitKeyPrefix; + +/** + * 初始化 + * @param databaseName 数据库名,不指定则默认为 “ACMDatabase” + * @param monitorTableName 埋点表名,必须要指定,用来区分不同产品数据 + * @param loggerTablename 日志表名,必须要指定,用来区分不同产品数据 + * @param limitKeyPrefix 限流信息存储到本地key的前缀,用来区分不同产品的限流缓存 + * @param uploadCount 每次上传条数 + * @param retryRightNow 上传失败是否立马重试,默认立马重试 + * @param uploadOnce 是否只执行一轮上传,默认NO + */ +- (instancetype)initWithDatabaseName:(NSString * _Nullable)databaseName + monitorTableName:(NSString *)monitorTableName + loggerTableName:(NSString *)loggerTablename + limitKeyPrefix:(NSString *)limitKeyPrefix + uploadCount:(NSInteger)uploadCount + retryRightNow:(BOOL)retryRightNow + uploadOnce:(BOOL)uploadOnce; + +/** + * 获取组件当前版本号 + */ +- (NSString *)getVersion; + +/** + * 设置日志埋点上传代理对象 + * 注:这里是强引用 + * @param uploadDelegate 代理对象,需要实现 ACMProtocol 协议 + */ +- (void)setUploadDelegate:(id _Nullable)uploadDelegate; + +/** + * 更新限流相关 + * @param isLimit 是否限流 + * @param limitTimeHour 限流区间大小 + * @param limitCount 区间内限流次数 + */ +- (void)updateLimitConfig:(BOOL)isLimit + limitTimeHour:(NSInteger)limitTimeHour + limitCount:(NSInteger)limitCount; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMMonitor.h b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMMonitor.h new file mode 100644 index 0000000..f74e3fe --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMMonitor.h @@ -0,0 +1,55 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, ACM_DELETE_TYPE) { + ACM_DELETE_TYPE_ALL, + ACM_DELETE_TYPE_FAILED, + ACM_DELETE_TYPE_UNUPLOAD +}; + + + +@interface ACMMonitor : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// 埋点是否入库,默认入库 +@property (nonatomic, assign) BOOL enterDatabase;; + +/// 埋点是否允许上传,默认上传 +@property (nonatomic, assign) BOOL isAllowUpload; + +/** + * 非实时埋点入库 + * @param obj 埋点具体内容 + */ +- (BOOL)monitor:(id)obj; + +/** + * 实时埋点入库 + * @param obj 埋点的具体内容 + */ +- (BOOL)monitorRealtime:(id)obj; + +/** + * 手动触发埋点上传,只上传未上传过的埋点 + */ +- (void)uploadMonitorByManual; + +/** + * 上传失败的埋点,一般放在重启应用后 + */ +- (void)uploadFailedRecords; + +/** + * 删除埋点 + * @param type 删除类型 + * @param block 结果的异步回调 + */ +- (void)deleteRecordsByType:(ACM_DELETE_TYPE)type block:(void (^)(BOOL))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMProtocol.h b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMProtocol.h new file mode 100644 index 0000000..df94a52 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/ACMProtocol.h @@ -0,0 +1,17 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ACMProtocol + +@required + +/// 埋点抛出,可在抛出里面进行上传 +- (BOOL)uploadMonitors:(NSArray *)monitors; +/// 日志抛出,可在抛出里面进行上传 +- (BOOL)uploadLoggers:(NSArray *)loggers; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/YTXMonitor.h b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/YTXMonitor.h new file mode 100644 index 0000000..393206d --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Headers/YTXMonitor.h @@ -0,0 +1,4 @@ + +#import +#import + diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Info.plist b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Info.plist new file mode 100644 index 0000000..c1a55c6 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Info.plist differ diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Modules/module.modulemap b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Modules/module.modulemap new file mode 100644 index 0000000..7c61570 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module YTXMonitor { + umbrella header "YTXMonitor.h" + + export * + module * { export * } +} diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/PrivacyInfo.xcprivacy b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..433213e --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/PrivacyInfo.xcprivacy @@ -0,0 +1,34 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDataTypes + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/YTXMonitor b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/YTXMonitor new file mode 100644 index 0000000..d72bd3a Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/YTXMonitor.framework/YTXMonitor differ diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXNetUtils.h b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXNetUtils.h new file mode 100644 index 0000000..54e4c9d --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXNetUtils.h @@ -0,0 +1,100 @@ +// +// YTXNetUtils.h +// YTXOperators +// +// Created by yangli on 2020/11/9. +// Copyright © 2020 com.alicom. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface YTXNetUtils : NSObject + +/** + 判断当前设备蜂窝数据网络是否开启,即3G/4G + @return 结果 + */ +- (BOOL)checkDeviceCellularDataEnable; + +/** +判断当前上网卡运营商是否是中国联通 +@return 结果 +*/ +- (BOOL)isChinaUnicom; + +/** +判断当前上网卡运营商是否是中国移动 +@return 结果 +*/ +- (BOOL)isChinaMobile; + +/** +判断当前上网卡运营商是否是中国电信 +@return 结果 +*/ +- (BOOL)isChinaTelecom; + +/** +获取当前上网卡运营商名称,比如中国移动、中国电信、中国联通 +@return 结果 +*/ +- (NSString *)getCurrentCarrierName; + +/** +获取当前上网卡运营商编码,比如46000、46001、46003 +@return 结果 +*/ +- (NSString *)getCurrentCarrierCode API_DEPRECATED("废弃,完成不可用,返回空字符串", ios(4.0, 16.0)); + +/** +获取当前上网卡网络类型,比如WiFi,4G +@return 结果 +*/ +- (NSString *)getNetworktype; + +/** +判断当前设备是否有SIM卡 +@return 结果 +*/ +- (BOOL)simSupportedIsOK; + +/** + 判断wwan是否开着(通过p0网卡判断,无wifi或有wifi情况下都能检测到) + @return 结果 + */ +- (BOOL)isWWANOpen; + +/** + 判断WiFi是否开着 + @return 结果 + */ +- (BOOL)isWiFiOpen; + +/** + 判断wwan是否开着(仅无wifi情况下) + @return 结果 + */ +- (BOOL)reachableViaWWAN; + +/** + 获取设备当前网络私网IP地址 + @return 结果 + */ +- (NSString *)getMobilePrivateIPAddress:(BOOL)preferIPv4; + +/** +获取双卡设备下,非上网卡信息 + @return 结果 +*/ +- (NSString *)getOptionalCarrierInfo API_DEPRECATED("废弃,完成不可用,返回空字符串", ios(4.0, 16.0));; + +/** + 获取当前蜂网络Ip地址 + */ +- (NSString *)getCellularIp; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXOperators.h b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXOperators.h new file mode 100644 index 0000000..337b073 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXOperators.h @@ -0,0 +1,20 @@ +// +// YTXOperators.h +// YTXOperators +// +// Created by yangli on 2020/11/9. +// Copyright © 2020 com.alicom. All rights reserved. +// + +#import + +//! Project version number for YTXOperators. +FOUNDATION_EXPORT double YTXOperatorsVersionNumber; + +//! Project version string for YTXOperators. +FOUNDATION_EXPORT const unsigned char YTXOperatorsVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXVendorService.h b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXVendorService.h new file mode 100644 index 0000000..d64852f --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Headers/YTXVendorService.h @@ -0,0 +1,85 @@ +// +// YTXVendorService.h +// ATAuthSDK +// +// Created by 刘超的MacBook on 2020/1/15. +// Copyright © 2020. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface YTXRequest : NSObject +/// 接口调用超时时间,注:目前内部限制最小超时时间为5s,小于5s则按5s设置 +@property (nonatomic, assign) NSTimeInterval timeout; +/// 是否是蜂窝网络 +@property (nonatomic, assign) BOOL isReachableViaWWAN; +@end + +@interface YTXVendorConfig : NSObject +/// 当前供应商标识:中移互联(cm_zyhl),联通小沃(cu_xw),联通在线(cu_zx),电信世纪龙(ct_sjl) +@property (nonatomic, copy) NSString *vendorKey; +/// 供应商二级标识 +@property (nonatomic, copy) NSString *vendorSubKey; +/// 供应商 access id +@property (nonatomic, copy) NSString *vendorAccessId; +/// 供应商 access secret +@property (nonatomic, copy) NSString *vendorAccessSecret; +@end + +@interface YTXVendorService : NSObject + +/** +* 获取SDK版本号 +*/ ++ (NSString *)getVersion; + +/** +* 获取供应商SDK版本号 +*/ ++ (NSDictionary *)getVendorsVersion; + +/** + * 初始化或更新各个供应商的接口调用对象,根据各个供应商的配置信息 + * @param vendorConfigs 各个供应商配置信息 + */ +- (void)updateVendorHandlers:(NSArray *)vendorConfigs; + +/** + * 获取本机号码校验Token + * @param request 请求参数结构体 + * @param vendorConfig 当前供应商配置信息 + * @param complete 结果回调 + */ +- (void)getVerifyTokenWithRequest:(YTXRequest *)request + vendorConfig:(YTXVendorConfig *)vendorConfig + complete:(void(^)(NSDictionary *response))complete; + +/** + * 获取手机掩码 + * @param request 请求参数结构体 + * @param vendorConfig 当前供应商配置信息 + * @param complete 结果回调 + */ +- (void)getMaskNumberWithRequest:(YTXRequest *)request + vendorConfig:(YTXVendorConfig *)vendorConfig + complete:(void(^)(NSDictionary *response))complete; + + +/** + * 电信/联通获取一键登录Token + * @param request 请求参数结构体 + * @param vendorConfig 当前供应商配置信息 + * @param complete 结果回调 + * @abstract 移动的获取登录Token不走这个回调,走弹起授权页的回调 + */ +- (void)getLoginTokenWithRequest:(YTXRequest *)request + vendorConfig:(YTXVendorConfig *)vendorConfig + complete:(void(^)(NSDictionary *response))complete; + +- (void)deleteCacheWithVendorConfigs:(NSArray *)vendorConfigs; + +@end + +NS_ASSUME_NONNULL_END diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Info.plist b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Info.plist new file mode 100644 index 0000000..88f9d47 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Info.plist differ diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Modules/module.modulemap b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Modules/module.modulemap new file mode 100644 index 0000000..0316400 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module YTXOperators { + umbrella header "YTXOperators.h" + + export * + module * { export * } +} diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/PrivacyInfo.xcprivacy b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..0be0921 --- /dev/null +++ b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/PrivacyInfo.xcprivacy @@ -0,0 +1,42 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDataTypes + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/YTXOperators b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/YTXOperators new file mode 100644 index 0000000..8f58ad0 Binary files /dev/null and b/airhub_app/packages/ali_auth/ios/libs/YTXOperators.framework/YTXOperators differ diff --git a/airhub_app/packages/ali_auth/lib/ali_auth_model.dart b/airhub_app/packages/ali_auth/lib/ali_auth_model.dart index 4b030c8..885c7b9 100644 --- a/airhub_app/packages/ali_auth/lib/ali_auth_model.dart +++ b/airhub_app/packages/ali_auth/lib/ali_auth_model.dart @@ -649,6 +649,21 @@ class AliAuthModel { /// 授权页物理返回键禁用 默认false late bool? closeAuthPageReturnBack; + /// 授权页无动画过渡(瞬间出现/消失,减少跳转感) + late bool? instantTransition; + + /// 自定义标题文字(通过 customViewBlock 添加到授权页顶部) + late String? customTitleText; + + /// 自定义标题颜色 + late String? customTitleColor; + + /// 自定义标题字号 + late int? customTitleSize; + + /// 自定义标题Y偏移量 + late int? customTitleOffsetY; + AliAuthModel( this.androidSk, this.iosSk, { @@ -877,6 +892,11 @@ class AliAuthModel { this.authPageUseDayLight=false, this.keepAllPageHideNavigationBar=false, this.closeAuthPageReturnBack=false, + this.instantTransition=false, + this.customTitleText, + this.customTitleColor, + this.customTitleSize, + this.customTitleOffsetY, }) : assert(androidSk != null || iosSk != null), assert(pageType != null), assert(isDelay != null); @@ -1095,6 +1115,11 @@ Map _$AliAuthModelToJson(AliAuthModel instance) => 'authPageUseDayLight': instance.authPageUseDayLight ?? false, 'keepAllPageHideNavigationBar': instance.keepAllPageHideNavigationBar ?? false, 'closeAuthPageReturnBack': instance.closeAuthPageReturnBack ?? false, + 'instantTransition': instance.instantTransition ?? false, + 'customTitleText': instance.customTitleText, + 'customTitleColor': instance.customTitleColor, + 'customTitleSize': instance.customTitleSize, + 'customTitleOffsetY': instance.customTitleOffsetY, }; /// 初始配置&注意事项 diff --git a/airhub_app/packages/ali_auth/lib/ali_auth_web.dart b/airhub_app/packages/ali_auth/lib/ali_auth_web.dart index 7232087..bc79546 100644 --- a/airhub_app/packages/ali_auth/lib/ali_auth_web.dart +++ b/airhub_app/packages/ali_auth/lib/ali_auth_web.dart @@ -1,3 +1,4 @@ +import 'dart:html' as html show window; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'ali_auth_web_api.dart'; import 'ali_auth_platform_interface.dart'; @@ -5,6 +6,7 @@ export 'ali_auth_method_channel.dart'; /// A web implementation of the AliAuthPlatform of the AliAuth plugin. class AliAuthPluginWeb extends AliAuthPlatform { + /// Constructs a AliAuthWeb AliAuthPluginWeb(); AliAuthPluginWebApi aliAuthPluginWebApi = AliAuthPluginWebApi(); @@ -13,26 +15,32 @@ class AliAuthPluginWeb extends AliAuthPlatform { AliAuthPlatform.instance = AliAuthPluginWeb(); } + /// Returns a [String] containing the version of the platform. @override Future getPlatformVersion() async { - return 'web'; + final version = html.window.navigator.userAgent; + return version; } + /// 获取SDK版本号 @override Future getSdkVersion() async { return await aliAuthPluginWebApi.getVersion(); } + /// 网络类型检查接口 @override Future getConnection() async { return await aliAuthPluginWebApi.getConnection(); } + /// 设置SDK是否开启日志。开启后会在控制台打印更多内容便于排查问题。 @override Future setLoggerEnable(bool isEnable) async { return await aliAuthPluginWebApi.setLoggerEnable(isEnable); } + /// 身份鉴权 @override Future checkAuthAvailable(String accessToken, String jwtToken, Function(dynamic) success, Function(dynamic) error) async { @@ -40,6 +48,7 @@ class AliAuthPluginWeb extends AliAuthPlatform { accessToken, jwtToken, success, error); } + /// 获取本机号码校验Token @override Future getVerifyToken( Function(dynamic) success, Function(dynamic) error) async { diff --git a/airhub_app/packages/ali_auth/lib/ali_auth_web_api.dart b/airhub_app/packages/ali_auth/lib/ali_auth_web_api.dart index e652473..373048c 100644 --- a/airhub_app/packages/ali_auth/lib/ali_auth_web_api.dart +++ b/airhub_app/packages/ali_auth/lib/ali_auth_web_api.dart @@ -1,16 +1,79 @@ -/// Web stub for ali_auth — provides matching API surface without dart:js_interop @JS classes. -/// These are never actually called since the app uses conditional imports to skip ali_auth on web. +import 'dart:async'; +import 'dart:js_util'; // Add this import +import 'dart:js_interop'; + +@JS("PhoneNumberServer") +class PhoneNumberServer { + /// 链接 + external getConnection(); + + /// 设置SDK是否开启日志 + external setLoggerEnable(bool isEnable); + + /// 获取版本信息 + external getVersion(); + + /// 鉴权 + external checkAuthAvailable(Params params); + + /// 获取token + external getVerifyToken(Params params); +} + +@JS() +@anonymous +class Params { + /// 传递的参数 + external String get accessToken; + + /// 传递的参数 + external String get jwtToken; + + /// 传递方法 需要使用allowInterop 包裹 + external void Function(dynamic ststus) get success; + + /// 传递方法 需要使用allowInterop 包裹 + external void Function(dynamic ststus) get error; + external factory Params( + {String accessToken, + String jwtToken, + void Function(dynamic ststus) success, + void Function(dynamic ststus) error}); +} class AliAuthPluginWebApi { - Future getConnection() async => null; + /// 网络类型检查接口 + Future getConnection() async { + return await PhoneNumberServer().getConnection(); + } - Future setLoggerEnable(bool isEnable) async {} + /// 设置SDK是否开启日志。开启后会在控制台打印更多内容便于排查问题。 + Future setLoggerEnable(bool isEnable) async { + PhoneNumberServer().setLoggerEnable(isEnable); + } - Future getVersion() async => null; + /// 获取版本号 + Future getVersion() async { + return await PhoneNumberServer().getVersion(); + } + /// 调用之前先去用户服务端获取accessToken和jwtToken Future checkAuthAvailable(String accessToken, String jwtToken, - Function(dynamic ststus) success, Function(dynamic ststus) error) async {} + Function(dynamic ststus) success, Function(dynamic ststus) error) async { + PhoneNumberServer().checkAuthAvailable(Params( + accessToken: accessToken, + jwtToken: jwtToken, + success: allowInterop(success), + error: allowInterop(error), + )); + } + /// 身份鉴权成功后才可调用获取Token接口 Future getVerifyToken( - Function(dynamic ststus) success, Function(dynamic ststus) error) async {} + Function(dynamic ststus) success, Function(dynamic ststus) error) async { + PhoneNumberServer().getVerifyToken(Params( + success: allowInterop(success), + error: allowInterop(error), + )); + } } diff --git a/airhub_app/packages/ali_auth/pubspec.lock b/airhub_app/packages/ali_auth/pubspec.lock index f724d0a..abe45d5 100644 --- a/airhub_app/packages/ali_auth/pubspec.lock +++ b/airhub_app/packages/ali_auth/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.0" boolean_selector: @@ -14,7 +14,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" characters: @@ -22,7 +22,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" clock: @@ -30,7 +30,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" collection: @@ -38,7 +38,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" fake_async: @@ -46,7 +46,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" flutter: @@ -59,7 +59,7 @@ packages: description: name: flutter_lints sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.0.0" flutter_test: @@ -77,7 +77,7 @@ packages: description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -85,7 +85,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.10" leak_tracker_testing: @@ -93,23 +93,23 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" lints: dependency: transitive description: name: lints - sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" - url: "https://pub.dev" + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.0" + version: "6.0.0" matcher: dependency: transitive description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.12.17" material_color_utilities: @@ -117,23 +117,23 @@ packages: description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.flutter-io.cn" source: hosted - version: "1.17.0" + version: "1.16.0" path: dependency: transitive description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" plugin_platform_interface: @@ -141,7 +141,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" sky_engine: @@ -153,16 +153,16 @@ packages: dependency: transitive description: name: source_span - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.2" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" stream_channel: @@ -170,7 +170,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" string_scanner: @@ -178,7 +178,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" term_glyph: @@ -186,23 +186,23 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 - url: "https://pub.dev" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.7" + version: "0.7.6" vector_math: dependency: transitive description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" vm_service: @@ -210,9 +210,9 @@ packages: description: name: vm_service sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.0.2" sdks: - dart: ">=3.8.0 <=3.38.9" + dart: ">=3.8.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/airhub_app/pubspec.lock b/airhub_app/pubspec.lock index e1cde7d..f8b4f0a 100644 --- a/airhub_app/pubspec.lock +++ b/airhub_app/pubspec.lock @@ -9,6 +9,13 @@ packages: url: "https://pub.dev" source: hosted version: "91.0.0" + ali_auth: + dependency: "direct main" + description: + path: "packages/ali_auth" + relative: true + source: path + version: "1.3.7" analyzer: dependency: transitive description: @@ -1371,5 +1378,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.7 <4.0.0" + dart: ">=3.10.7 <=3.38.9" flutter: ">=3.38.4" diff --git a/airhub_app/pubspec.yaml b/airhub_app/pubspec.yaml index e6a5f11..3a07761 100644 --- a/airhub_app/pubspec.yaml +++ b/airhub_app/pubspec.yaml @@ -54,8 +54,9 @@ dependencies: dio: ^5.7.0 shared_preferences: ^2.3.0 - # Aliyun Phone Auth (一键登录) — 本地 Web 调试时禁用 - # ali_auth: ^1.3.7 + # Aliyun Phone Auth (一键登录) — 使用本地包 + ali_auth: + path: packages/ali_auth # Existing dependencies webview_flutter: ^4.4.2