Browse Source

Merge branch 'v1.1.0' into v1.1.0-iOS

# Conflicts:
#	lib/data/api/atmob_api.g.dart
“HeShaoZe” 4 months ago
parent
commit
c6c11ff7f2

+ 29 - 1
android/app/proguard-rules.pro

@@ -198,4 +198,32 @@ public <methods>;
 -dontwarn okio.BufferedSource
 -dontwarn okio.Okio
 -dontwarn okio.Sink
--dontwarn okio.Source
+-dontwarn okio.Source
+
+
+#网易易盾一键登录
+-dontwarn com.cmic.sso.sdk.**
+-keep class com.cmic.sso.**{*;}
+-dontwarn com.sdk.**
+-keep class com.sdk.** { *;}
+-keep class cn.com.chinatelecom.account.**{*;}
+-keep public class * extends android.view.View
+-keep class com.netease.nis.quicklogin.entity.**{*;}
+-keep class com.netease.nis.quicklogin.listener.**{*;}
+-keep class com.netease.nis.quicklogin.QuickLogin{
+    public <methods>;
+    public <fields>;
+}
+-keep class com.netease.nis.quicklogin.helper.UnifyUiConfig{*;}
+-keep class com.netease.nis.quicklogin.helper.UnifyUiConfig$Builder{
+     public <methods>;
+     public <fields>;
+ }
+-keep class com.netease.nis.quicklogin.utils.LoginUiHelper$CustomViewListener{
+     public <methods>;
+     public <fields>;
+}
+-keep class com.netease.nis.basesdk.**{
+    public *;
+    protected *;
+}

+ 5 - 0
android/app/src/main/res/drawable/bg_login_btn.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="100dp" />
+    <solid android:color="#7B7DFF" />
+</shape>

BIN
android/app/src/main/res/drawable/icon_common_back.webp


BIN
android/app/src/main/res/drawable/icon_login_check_box_checked.webp


BIN
android/app/src/main/res/drawable/icon_login_check_box_unchecked.webp


BIN
android/app/src/main/res/drawable/icon_logo.png


+ 82 - 0
assets/config/android_quick_login_config.json

@@ -0,0 +1,82 @@
+{
+  "statusBarColor": "#ffffff",
+  "isStatusBarDarkColor": true,
+  "navBackIconMargin": 15,
+  "isHideNav": false,
+  "navTitle": " ",
+  "isHideBackIcon": false,
+  "navBackIconWidth": 30,
+  "navBackIconHeight": 30,
+  "logoIconName": "icon_logo",
+  "logoWidth": 80,
+  "logoHeight": 80,
+  "logoTopYOffset": 50,
+  "logoBottomYOffset": 0,
+  "logoXOffset": 0,
+  "isHideLogo": false,
+  "loginBtnText": "本机号码一键登录",
+  "loginBtnTextColor": "#ffffff",
+  "loginBtnTextSize": 16,
+  "loginBtnHeight": 50,
+  "loginBtnWidth": 450,
+  "loginBtnTopYOffset": 240,
+  "loginBtnBottomYOffset": 0,
+  "loginBtnXOffset": 0,
+  "loginBtnMarginLeft": 24,
+  "loginBtnMarginRight": 24,
+  "loginBtnBackgroundRes": "bg_login_btn",
+  "privacySize": 12,
+  "privacyDpSize": 0,
+  "privacyTopYOffset": 0,
+  "privacyBottomYOffset": 20,
+  "privacyMarginLeft": 12,
+  "privacyMarginRight": 12,
+  "privacyState": false,
+  "isHidePrivacySmh": false,
+  "isHidePrivacyCheckBox": false,
+  "isPrivacyTextGravityCenter": false,
+  "privacyTextMarginLeft": 3,
+  "operatorPrivacyAtLast": true,
+  "checkBoxGravity": 48,
+  "checkedImageName": "icon_login_check_box_checked",
+  "unCheckedImageName": "icon_login_check_box_unchecked",
+  "checkBoxWith": 22,
+  "checkBoxHeight": 22,
+  "privacyTextStart": "已阅读并同意",
+  "protocolText": "《用户协议》",
+  "protocolLink": "https://doc.v8dashen.com/doc/546b8b5175a1b4db",
+  "protocol2Text": "《隐私政策》",
+  "protocol2Link": "https://doc.v8dashen.com/doc/bad49f15215daa70",
+  "privacyTextColor": "#202020",
+  "privacyProtocolColor": "#7B7DFF",
+  "privacyTextEnd": "",
+  "protocolNavTitle": "",
+  "protocolNavBackIcon": "icon_common_back",
+  "protocolNavColor": "#ffffff",
+  "protocolNavTitleColor": "#202020",
+  "isLandscape": false,
+  "isDialogMode": false,
+  "dialogWidth": 300,
+  "dialogHeight": 400,
+  "dialogX": 0,
+  "dialogY": 0,
+  "isBottomDialog": false,
+  "isShowPrivacyDialog": true,
+  "isPrivacyDialogAuto": true,
+  "privacyDialogContentEnd": ",点击“同意”,表示您已经阅读并同意以上协议",
+  "widgets": [
+    {
+      "width": 150,
+      "height": 30,
+      "viewId": "code_login",
+      "type": "TextView",
+      "top": 310,
+      "font": 15,
+      "text": "验证码登录",
+      "textColor": "#202020",
+      "isGravityCenter": true,
+      "clickable": true,
+      "action": "code_login"
+    }
+  ]
+}

+ 4 - 0
assets/config/ios_quick_login_config.json

@@ -0,0 +1,4 @@
+{
+  "statusBarColor": "#ffffff",
+  "isStatusBarDarkColor": true
+}

+ 2 - 0
assets/string/base/string.xml

@@ -381,4 +381,6 @@
     <string name="location_permission_refuse_desc">
         我们需要您的定位权限,以提供更准确的位置服务,如果未开启,可能会影响功能使用(如实时定位、轨迹记录等)。
     </string>
+    <string name="one_login_error">一键登录失败,请尝试验证码登录</string>
+    <string name="one_login_txt">一键登录</string>
 </resources>

+ 46 - 0
lib/base/base_lifecycle_controller.dart

@@ -0,0 +1,46 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:location/utils/atmob_log.dart';
+
+class BaseLifecycleController extends SuperController {
+  Map? parameters;
+
+  @override
+  void onInit() {
+    super.onInit();
+    _initParameters();
+  }
+
+  void _initParameters() {
+    var getParameters = Get.parameters;
+    var getArguments = Get.arguments;
+    parameters ??= <dynamic, dynamic>{};
+    parameters?.addAll(getParameters);
+    if (getArguments != null && getArguments is Map) {
+      parameters?.addAll(getArguments);
+    }
+  }
+
+  /// 隐藏键盘
+  void hideKeyboard(BuildContext context) {
+    FocusScopeNode currentFocus = FocusScope.of(context);
+    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
+      FocusManager.instance.primaryFocus!.unfocus();
+    }
+  }
+
+  @override
+  void onResumed() {}
+
+  @override
+  void onDetached() {}
+
+  @override
+  void onHidden() {}
+
+  @override
+  void onInactive() {}
+
+  @override
+  void onPaused() {}
+}

+ 4 - 2
lib/data/api/atmob_api.dart

@@ -12,6 +12,7 @@ import 'package:location/data/api/request/login_request.dart';
 import 'package:location/data/api/request/member_list_request.dart';
 import 'package:location/data/api/request/message_request.dart';
 import 'package:location/data/api/request/notification_report_request.dart';
+import 'package:location/data/api/request/one_click_login_request.dart';
 import 'package:location/data/api/request/operation_friend_request.dart';
 import 'package:location/data/api/request/order_status_request.dart';
 import 'package:location/data/api/request/query_track_request.dart';
@@ -261,6 +262,7 @@ abstract class AtmobApi {
   Future<BaseResponse<ElectricQueryResponse>> userElectricQuery(
       @Body() FriendsOperationRequest request);
 
-  @POST("/s/v1/chat/daily/exception/analyse")
-  Future<dynamic> dailyExceptionAnalyse(@Body() QueryTrackRequest request);
+  @POST("/s/v1/user/login/oneclick")
+  Future<BaseResponse<LoginResponse>> oneClickLogin(
+      @Body() OneClickLoginRequest request);
 }

+ 32 - 1
lib/data/api/atmob_api.g.dart

@@ -1700,12 +1700,14 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
-  Future<dynamic> dailyExceptionAnalyse(QueryTrackRequest request) async {
+  Future<BaseResponse<LoginResponse>> oneClickLogin(
+      OneClickLoginRequest request) async {
     final _extra = <String, dynamic>{};
     final queryParameters = <String, dynamic>{};
     final _headers = <String, dynamic>{};
     final _data = <String, dynamic>{};
     _data.addAll(request.toJson());
+<<<<<<< HEAD
     final _options = _setStreamType<dynamic>(
       Options(method: 'POST', headers: _headers, extra: _extra)
           .compose(
@@ -1718,6 +1720,35 @@ class _AtmobApi implements AtmobApi {
     );
     final _result = await _dio.fetch(_options);
     final _value = _result.data;
+=======
+    final _options = _setStreamType<BaseResponse<LoginResponse>>(Options(
+      method: 'POST',
+      headers: _headers,
+      extra: _extra,
+    )
+        .compose(
+          _dio.options,
+          '/s/v1/user/login/oneclick',
+          queryParameters: queryParameters,
+          data: _data,
+        )
+        .copyWith(
+            baseUrl: _combineBaseUrls(
+          _dio.options.baseUrl,
+          baseUrl,
+        )));
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<LoginResponse> _value;
+    try {
+      _value = BaseResponse<LoginResponse>.fromJson(
+        _result.data!,
+        (json) => LoginResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+>>>>>>> v1.1.0
     return _value;
   }
 

+ 21 - 0
lib/data/api/request/one_click_login_request.dart

@@ -0,0 +1,21 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:location/base/app_base_request.dart';
+
+part 'one_click_login_request.g.dart';
+
+@JsonSerializable()
+class OneClickLoginRequest extends AppBaseRequest {
+  @JsonKey(name: 'token')
+  String token;
+
+  @JsonKey(name: 'accessToken')
+  String accessToken;
+
+  OneClickLoginRequest(this.token, this.accessToken);
+
+  factory OneClickLoginRequest.fromJson(Map<String, dynamic> json) =>
+      _$OneClickLoginRequestFromJson(json);
+
+  @override
+  Map<String, dynamic> toJson() => _$OneClickLoginRequestToJson(this);
+}

+ 73 - 0
lib/data/api/request/one_click_login_request.g.dart

@@ -0,0 +1,73 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'one_click_login_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+OneClickLoginRequest _$OneClickLoginRequestFromJson(
+        Map<String, dynamic> json) =>
+    OneClickLoginRequest(
+      json['token'] as String,
+      json['accessToken'] as String,
+    )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$OneClickLoginRequestToJson(
+        OneClickLoginRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+      'authToken': instance.authToken,
+      'token': instance.token,
+      'accessToken': instance.accessToken,
+    };

+ 4 - 1
lib/data/api/response/login_response.dart

@@ -7,7 +7,10 @@ class LoginResponse {
   @JsonKey(name: "authToken")
   String authToken;
 
-  LoginResponse(this.authToken);
+  @JsonKey(name: "phone")
+  String? phone;
+
+  LoginResponse(this.authToken, this.phone);
 
   factory LoginResponse.fromJson(Map<String, dynamic> json) =>
       _$LoginResponseFromJson(json);

+ 2 - 0
lib/data/api/response/login_response.g.dart

@@ -9,9 +9,11 @@ part of 'login_response.dart';
 LoginResponse _$LoginResponseFromJson(Map<String, dynamic> json) =>
     LoginResponse(
       json['authToken'] as String,
+      json['phone'] as String?,
     );
 
 Map<String, dynamic> _$LoginResponseToJson(LoginResponse instance) =>
     <String, dynamic>{
       'authToken': instance.authToken,
+      'phone': instance.phone,
     };

+ 2 - 0
lib/data/consts/build_config.dart

@@ -8,6 +8,8 @@ final class BuildConfig {
   static bool get isDebug => kDebugMode;
 
   static const String qiyuKEY = "09ea6e0a6d006e25462906fbf6758c99";
+
+  static const String quickLoginBusinessId = "83c29845ebc04106b186a7396396d97c";
 }
 
 final class BuglyConfig {

+ 1 - 1
lib/data/consts/constants.dart

@@ -1,7 +1,7 @@
 class Constants {
   Constants._();
 
-  static const String env = envProd;
+  static const String env = envTest;
 
   static const String envDev = 'dev';
 

+ 11 - 1
lib/data/repositories/account_repository.dart

@@ -27,6 +27,7 @@ import 'package:location/utils/mmkv_util.dart';
 
 import '../../sdk/map/map_helper.dart';
 import '../api/request/notification_report_request.dart';
+import '../api/request/one_click_login_request.dart';
 import '../api/request/user_avatar_update_request.dart';
 import '../api/response/login_response.dart';
 import '../api/response/member_status_response.dart';
@@ -164,7 +165,6 @@ class AccountRepository {
     urgentContactRepository.clearContactList();
 
     phoneEventRepository.stopReportPhoneEvent();
-
   }
 
   void refreshMemberStatus() {
@@ -231,6 +231,16 @@ class AccountRepository {
     });
   }
 
+  Future<void> oneClickLogin(
+      {required String token, required String accessToken}) {
+    return atmobApi
+        .oneClickLogin(OneClickLoginRequest(token, accessToken))
+        .then(HttpHandler.handle(false))
+        .then((response) {
+      onLoginSuccess(response.phone ?? '', response.authToken);
+    });
+  }
+
   bool memberIsExpired() {
     return memberStatusInfo.value == null ||
         memberStatusInfo.value?.expired == true;

+ 3 - 0
lib/main.dart

@@ -15,6 +15,7 @@ import 'package:location/router/app_pages.dart';
 import 'package:location/sdk/bugly/bugly_helper.dart';
 import 'package:location/sdk/gravity/gravity_helper.dart';
 import 'package:location/sdk/map/map_helper.dart';
+import 'package:location/sdk/quicklogin/quick_login_helper.dart';
 import 'package:location/sdk/umeng/umeng_helper.dart';
 import 'package:location/sdk/wechat/wechat_helper.dart';
 import 'package:location/utils/app_info_util.dart';
@@ -91,6 +92,8 @@ class AppInitTask implements EnsurePolicyGrant {
     UmengHelper.initCommon();
     //地图sdk
     await MapHelper.init();
+    //一键登录
+    QuickLoginHelper.init();
   }
 }
 

+ 77 - 7
lib/module/login/login_controller.dart

@@ -1,12 +1,15 @@
+import 'dart:async';
+
 import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
 import 'package:location/base/base_controller.dart';
 import 'package:location/data/consts/error_code.dart';
 import 'package:location/resource/string.gen.dart';
+import 'package:location/sdk/quicklogin/quick_login_helper.dart';
+import 'package:location/utils/atmob_log.dart';
 import 'package:location/utils/http_handler.dart';
 import 'package:location/utils/toast_util.dart';
-
 import '../../data/repositories/account_repository.dart';
 import '../../utils/de_bounce.dart';
 
@@ -35,9 +38,69 @@ class LoginController extends BaseController {
   final Debounce _saveDebounce = Debounce(debounceTime: 1000);
   bool isRequestLogin = false;
 
+  final Rx<LoginStatus> _loginStatus = Rx(LoginStatus.loading);
+
+  LoginStatus get loginStatus => _loginStatus.value;
+
+  final RxBool _isSupportOneLogin = RxBool(false);
+
+  bool get isSupportOneLogin => _isSupportOneLogin.value;
+
+  bool? _isOneLoginSuccess;
+
+  StreamSubscription? eventOneLoginSubscription;
+
   @override
   void onReady() {
     super.onReady();
+    QuickLoginHelper.preFetchNumber(onSuccess: (token) {
+      _isSupportOneLogin.value = true;
+      onePassLogin();
+    }, onError: (errorMsg) {
+      _loginStatus.value = LoginStatus.codeLogin;
+    });
+
+    eventOneLoginSubscription = QuickLoginHelper.getEventChannel()
+        .receiveBroadcastStream()
+        .listen(_onOneLoginData);
+  }
+
+  void _onOneLoginData(response) {
+    AtmobLog.d('LoginController', 'response:$response');
+    if (response is Map) {
+      var action = (response)["action"];
+      switch (action) {
+        case "authViewWillAppear":
+          _loginStatus.value = LoginStatus.oneLogin;
+          break;
+        case "authViewWillDisappear":
+          if (_isOneLoginSuccess != true &&
+              loginStatus == LoginStatus.oneLogin) {
+            Get.back();
+          }
+        case "code_login":
+          QuickLoginHelper.closeLoginAuthView();
+          _loginStatus.value = LoginStatus.codeLogin;
+          break;
+      }
+    }
+  }
+
+  void onePassLogin() {
+    //尝试打开一键登录页面
+    QuickLoginHelper.onePassLogin(onSuccess: (ydToken, accessToken) {
+      _isOneLoginSuccess = true;
+      accountRepository
+          .oneClickLogin(token: ydToken, accessToken: accessToken)
+          .then((_) {
+        Get.back();
+      }).catchError((error) {
+        _loginStatus.value = LoginStatus.codeLogin;
+        ToastUtil.show(StringName.oneLoginError);
+      });
+    }, onError: (msg) {
+      _loginStatus.value = LoginStatus.codeLogin;
+    });
   }
 
   void onPhoneChanged(String value) {
@@ -104,12 +167,6 @@ class LoginController extends BaseController {
     _isAgreePrivacy.value = !_isAgreePrivacy.value;
   }
 
-  @override
-  void onClose() {
-    super.onClose();
-    _countDown.value = null;
-  }
-
   void onLoginClick() {
     _saveDebounce.onClick(() {
       login();
@@ -153,4 +210,17 @@ class LoginController extends BaseController {
       }
     });
   }
+
+  void onOneLoginClick() {
+    onePassLogin();
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    _countDown.value = null;
+    eventOneLoginSubscription?.cancel();
+  }
 }
+
+enum LoginStatus { loading, codeLogin, oneLogin }

+ 38 - 0
lib/module/login/login_page.dart

@@ -29,6 +29,26 @@ class LoginPage extends BasePage<LoginController> {
 
   @override
   Widget buildBody(BuildContext context) {
+    return Obx(() {
+      if (controller.loginStatus == LoginStatus.loading ||
+          controller.loginStatus == LoginStatus.oneLogin) {
+        return _buildLoadingView();
+      } else {
+        return _buildPhoneNumberLoginView(context);
+      }
+    });
+  }
+
+  Widget _buildLoadingView() {
+    return Center(
+      child: CircularProgressIndicator(
+        strokeWidth: 3.5.w,
+        valueColor: const AlwaysStoppedAnimation(ColorName.black30),
+      ),
+    );
+  }
+
+  Widget _buildPhoneNumberLoginView(BuildContext context) {
     return GestureDetector(
       onTap: () {
         // 点击空白处关闭键盘
@@ -66,6 +86,8 @@ class LoginPage extends BasePage<LoginController> {
                         buildPrivacyTxt(),
                         SizedBox(height: 148.h),
                         buildLoginBtn(),
+                        SizedBox(height: 10.w),
+                        buildOneLoginTxt()
                       ],
                     ),
                   ),
@@ -78,6 +100,22 @@ class LoginPage extends BasePage<LoginController> {
     );
   }
 
+  Widget buildOneLoginTxt() {
+    return Obx(() {
+      return Visibility(
+        visible: controller.isSupportOneLogin,
+        child: GestureDetector(
+          onTap: controller.onOneLoginClick,
+          child: Padding(
+            padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.w),
+            child: Text(StringName.oneLoginTxt,
+                style: TextStyle(fontSize: 14.sp, color: ColorName.black50)),
+          ),
+        ),
+      );
+    });
+  }
+
   Widget buildPrivacyTxt() {
     return Padding(
       padding: EdgeInsets.symmetric(horizontal: 24.w),

+ 15 - 0
lib/resource/assets.gen.dart

@@ -24,6 +24,20 @@ class $AssetsAnimGen {
   List<String> get values => [locationAnalyseRobot, locationLabel];
 }
 
+class $AssetsConfigGen {
+  const $AssetsConfigGen();
+
+  /// File path: assets/config/android_quick_login_config.json
+  String get androidQuickLoginConfig =>
+      'assets/config/android_quick_login_config.json';
+
+  /// File path: assets/config/ios_quick_login_config.json
+  String get iosQuickLoginConfig => 'assets/config/ios_quick_login_config.json';
+
+  /// List of all assets
+  List<String> get values => [androidQuickLoginConfig, iosQuickLoginConfig];
+}
+
 class $AssetsImagesGen {
   const $AssetsImagesGen();
 
@@ -811,6 +825,7 @@ class Assets {
   const Assets._();
 
   static const $AssetsAnimGen anim = $AssetsAnimGen();
+  static const $AssetsConfigGen config = $AssetsConfigGen();
   static const $AssetsImagesGen images = $AssetsImagesGen();
 }
 

+ 4 - 0
lib/resource/string.gen.dart

@@ -312,6 +312,8 @@ class StringName {
   static String get trackNoDoubtDesc => 'track_no_doubt_desc'.tr; // 未授权原因:\n 1.可能是自己/TA手机解锁权限尚未开启,请在个人中心设置中开启权限\n 2.可能是TA手机尚未开启本软件
   static String get trackNoStayData => 'track_no_stay_data'.tr; // 暂无停留数据
   static String get locationPermissionRefuseDesc => 'location_permission_refuse_desc'.tr; // 我们需要您的定位权限,以提供更准确的位置服务,如果未开启,可能会影响功能使用(如实时定位、轨迹记录等)。
+  static String get oneLoginError => 'one_login_error'.tr; // 一键登录失败,请尝试验证码登录
+  static String get oneLoginTxt => 'one_login_txt'.tr; // 一键登录
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -627,6 +629,8 @@ class StringMultiSource {
       'track_no_doubt_desc': '未授权原因:\n 1.可能是自己/TA手机解锁权限尚未开启,请在个人中心设置中开启权限\n 2.可能是TA手机尚未开启本软件',
       'track_no_stay_data': '暂无停留数据',
       'location_permission_refuse_desc': '我们需要您的定位权限,以提供更准确的位置服务,如果未开启,可能会影响功能使用(如实时定位、轨迹记录等)。',
+      'one_login_error': '一键登录失败,请尝试验证码登录',
+      'one_login_txt': '一键登录',
     },
   };
 }

+ 96 - 0
lib/sdk/quicklogin/quick_login_helper.dart

@@ -0,0 +1,96 @@
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+import 'package:location/data/consts/build_config.dart';
+import 'package:location/data/consts/web_url.dart';
+import 'package:location/resource/assets.gen.dart';
+import 'package:location/utils/atmob_log.dart';
+import 'package:quickpass_yidun_flutter/quickpass_flutter_plugin.dart';
+
+class QuickLoginHelper {
+  QuickLoginHelper._();
+
+  static final String tag = 'QuickLoginHelper';
+
+  static final String _businessId = BuildConfig.quickLoginBusinessId;
+
+  static final String userAgreement = WebUrl.userAgreement;
+  static final String privacyPolicy = WebUrl.privacyPolicy;
+
+  static final QuickpassFlutterPlugin quickLoginPlugin =
+      QuickpassFlutterPlugin();
+
+  static Future<void> init() {
+    return quickLoginPlugin.init(_businessId).then((map) {
+      bool result = map?['success'];
+      AtmobLog.d(tag, 'init success:$result');
+      _setUiConfig();
+    });
+  }
+
+  static EventChannel getEventChannel() {
+    return const EventChannel("yd_quicklogin_flutter_event_channel");
+  }
+
+  static void closeLoginAuthView() {
+    quickLoginPlugin.closeLoginAuthView();
+  }
+
+  static void _setUiConfig() {
+    Map<String, dynamic> configMap;
+    String file = "";
+    if (Platform.isIOS) {
+      file = Assets.config.iosQuickLoginConfig;
+    } else if (Platform.isAndroid) {
+      file = Assets.config.androidQuickLoginConfig;
+    }
+    rootBundle.loadString(file).then((value) async {
+      configMap = {"uiConfig": json.decode(value)};
+      quickLoginPlugin.setUiConfig(configMap);
+    });
+  }
+
+  static void preFetchNumber({
+    required void Function(String token) onSuccess,
+    required void Function(String errorMsg) onError,
+  }) async {
+    // _setUiConfig();//仅测试
+    Map<dynamic, dynamic>? map = await quickLoginPlugin.preFetchNumber();
+    if (map?['success'] == true) {
+      var ydToken = map?['token'];
+      AtmobLog.d(tag, 'preFetchNumber success token:$ydToken');
+      onSuccess(ydToken);
+    } else {
+      //获取预设手机号失败
+      var ydToken = map?['token'];
+      var errorMsg = map?['errorMsg'];
+      AtmobLog.e(tag, "preFetchNumber error token:$ydToken,errorMsg:$errorMsg");
+      onError(errorMsg);
+    }
+  }
+
+  static void onePassLogin({
+    required void Function(String ydToken, String accessToken) onSuccess,
+    required void Function(String errorMsg) onError,
+  }) async {
+    try {
+      Map<dynamic, dynamic>? map = await quickLoginPlugin.onePassLogin();
+      if (map?["success"]) {
+        var accessToken = map?["accessToken"];
+        var ydToken = map?["ydToken"];
+        AtmobLog.d(tag,
+            "onePassLogin success ydToken:$ydToken, accessToken:$accessToken");
+        onSuccess(ydToken, accessToken);
+      } else {
+        var errorMsg = map?["msg"];
+        AtmobLog.e(tag, "onePassLogin error msg:$errorMsg");
+        onError(errorMsg);
+      }
+      closeLoginAuthView();
+    } catch (e) {
+      onError(e.toString());
+      closeLoginAuthView();
+    }
+  }
+}

+ 8 - 0
pubspec.lock

@@ -1241,6 +1241,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.1.0"
+  quickpass_yidun_flutter:
+    dependency: "direct main"
+    description:
+      name: quickpass_yidun_flutter
+      sha256: f04f7bb1dd82d116e6e837c7c9d501af11cc4ce116998b4f739a50a5d718b108
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.5.6"
   recase:
     dependency: transitive
     description:

+ 4 - 0
pubspec.yaml

@@ -161,6 +161,9 @@ dependencies:
   #获取wifi相关信息
   network_info_plus: ^6.1.4
 
+  #一键登录
+  quickpass_yidun_flutter: ^1.5.6
+
   ######################地图########################
   flutter_map:
     path: plugins/map
@@ -267,6 +270,7 @@ flutter:
   assets:
     - assets/images/
     - assets/anim/
+    - assets/config/
 
   fonts:
     - family: OppoSans