diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 15a18c0..e9277b7 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -399,7 +399,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 8; DART_OBFUSCATION = true; DEVELOPMENT_TEAM = TW3K3253KL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = TW3K3253KL; @@ -545,7 +545,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 8; DART_OBFUSCATION = true; DEVELOPMENT_TEAM = TW3K3253KL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = TW3K3253KL; @@ -581,7 +581,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 8; DART_OBFUSCATION = true; DEVELOPMENT_TEAM = TW3K3253KL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = TW3K3253KL; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 1dbce2a..e56ad79 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -32,11 +32,11 @@ NSCameraUsageDescription - TThis enables you to take and upload photos to your private space. Please grant camera access. + Visual Wallpaper needs access to your camera to take photos for creating custom wallpapers. The photos you take will only be used to make personalized wallpapers within the app. NSPhotoLibraryAddUsageDescription We need access to your photo album so you can save wallpapers from the app to your album. NSPhotoLibraryUsageDescription - This enables you to upload photos and save wallpapers. Please grant photo library access. + This enables you to upload photos and save wallpapers. NSUserTrackingUsageDescription We need your permission to access the advertising identifier to provide better ad services. CFBundleURLTypes diff --git a/lib/common/components/pin_code_verification_screen.dart b/lib/common/components/pin_code_verification_screen.dart index fbe077e..8a55745 100644 --- a/lib/common/components/pin_code_verification_screen.dart +++ b/lib/common/components/pin_code_verification_screen.dart @@ -58,195 +58,197 @@ class _PinCodeVerificationScreenState extends State { backgroundColor: Colors.black, key: scaffoldKey, body: SafeArea( - child: Container( - margin: EdgeInsets.only(top: 94.w), - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Please input a', - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 28.sp, - color: Colors.white, - ), - ), - Text( - 'password', - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 28.sp, - color: Colors.white, - ), - ) - ], - ), - Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15.r), - ), - child: Image.asset( - Assets.imagesCustomLock, - width: 100.w, - height: 100.w, - ), - ), - ], - ), - SizedBox(height: 64.w), - Form( - key: formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 30, - ).w, - child: PinCodeTextField( - appContext: context, - pastedTextStyle: TextStyle( - color: Colors.green.shade600, - fontWeight: FontWeight.bold, - ), - dialogConfig: DialogConfig(platform: PinCodePlatform.iOS), - length: 4, - obscureText: true, - obscuringCharacter: '*', - animationType: AnimationType.fade, - pinTheme: PinTheme( - activeColor: const Color(0xff262626), - selectedColor: const Color(0xff262626), - selectedFillColor: Colors.grey, - inactiveFillColor: const Color(0xff262626), - errorBorderColor: const Color(0xff262626), - inactiveColor: const Color(0xff262626), - shape: PinCodeFieldShape.box, - borderRadius: BorderRadius.circular(30.r), - fieldHeight: 58.w, - fieldWidth: 58.w, - activeFillColor: hasError - ? const Color(0xff262626) - : const Color(0xff262626), - ), - showCursor: false, - animationDuration: const Duration(milliseconds: 300), - textStyle: TextStyle( - fontSize: 32.sp, - fontWeight: FontWeight.w700, - color: Colors.white, - height: 2.0, - ), - backgroundColor: Colors.transparent, - enableActiveFill: true, - errorAnimationController: errorController, - controller: textEditingController, - keyboardType: TextInputType.number, - boxShadows: const [ - BoxShadow( - offset: Offset(0, 1), - color: Colors.black12, - blurRadius: 30, + child: SingleChildScrollView( + child: Container( + margin: EdgeInsets.only(top: 94.h), + height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top - kBottomNavigationBarHeight - 94.h, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Please input a', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28.sp, + color: Colors.white, + ), ), + Text( + 'password', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28.sp, + color: Colors.white, + ), + ) ], - onCompleted: (v) {}, - onChanged: (value) { - setState(() { - currentText = value; - }); - }, - beforeTextPaste: (text) { - setState(() { - currentText = text!; - }); - return true; - }, - )), - ), - Visibility( - visible: widget.checkPassword == "", - child: Text( - "First input as password", - style: TextStyle( - color: Colors.red, - fontSize: 14.sp, + ), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.r), + ), + child: Image.asset( + Assets.imagesCustomLock, + width: 100.w, + height: 100.w, + ), + ), + ], + ), + SizedBox(height: 64.w), + Form( + key: formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 30, + ).w, + child: PinCodeTextField( + appContext: context, + pastedTextStyle: TextStyle( + color: Colors.green.shade600, + fontWeight: FontWeight.bold, + ), + dialogConfig: DialogConfig(platform: PinCodePlatform.iOS), + length: 4, + obscureText: true, + obscuringCharacter: '*', + animationType: AnimationType.fade, + pinTheme: PinTheme( + activeColor: const Color(0xff262626), + selectedColor: const Color(0xff262626), + selectedFillColor: Colors.grey, + inactiveFillColor: const Color(0xff262626), + errorBorderColor: const Color(0xff262626), + inactiveColor: const Color(0xff262626), + shape: PinCodeFieldShape.box, + borderRadius: BorderRadius.circular(30.r), + fieldHeight: 58.w, + fieldWidth: 58.w, + activeFillColor: hasError + ? const Color(0xff262626) + : const Color(0xff262626), + ), + showCursor: false, + animationDuration: const Duration(milliseconds: 300), + textStyle: TextStyle( + fontSize: 32.sp, + fontWeight: FontWeight.w700, + color: Colors.white, + height: 2.0, + ), + backgroundColor: Colors.transparent, + enableActiveFill: true, + errorAnimationController: errorController, + controller: textEditingController, + keyboardType: TextInputType.number, + boxShadows: const [ + BoxShadow( + offset: Offset(0, 1), + color: Colors.black12, + blurRadius: 30, + ), + ], + onCompleted: (v) {}, + onChanged: (value) { + setState(() { + currentText = value; + }); + }, + beforeTextPaste: (text) { + setState(() { + currentText = text!; + }); + return true; + }, + )), + ), + Visibility( + visible: widget.checkPassword == "", + child: Text( + "First input as password", + style: TextStyle( + color: Colors.red, + fontSize: 14.sp, + ), ), ), - ), - GestureDetector( - onTap: () { - formKey.currentState?.validate(); - if (currentText.length == 4 && widget.checkPassword == "") { - UPCache.getInstance() - .setData("custom_password", currentText); - widget.callback(); - } else if (currentText.length != 4 || - currentText != widget.checkPassword) { - errorController.add(ErrorAnimationType - .shake); // Triggering error shake animation - setState(() { - hasError = true; - textEditingController.clear(); - }); - } else { - InterstitialAdManager().showAdIfReady(onTap: () { + GestureDetector( + onTap: () { + formKey.currentState?.validate(); + if (currentText.length == 4 && widget.checkPassword == "") { + UPCache.getInstance() + .setData("custom_password", currentText); widget.callback(); + } else if (currentText.length != 4 || + currentText != widget.checkPassword) { + errorController.add(ErrorAnimationType + .shake); // Triggering error shake animation setState(() { - hasError = false; + hasError = true; + textEditingController.clear(); }); - }); - } - }, - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 24, - horizontal: 40, - ).w, - padding: const EdgeInsets.symmetric( - vertical: 17, - ).w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.r), - gradient: const LinearGradient( - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [ - Color(0xffBEEF32), - Color(0xff2795E5), - Color(0xff8041FD), - ], + } else { + InterstitialAdManager().showAdIfReady(onTap: () { + widget.callback(); + setState(() { + hasError = false; + }); + }); + } + }, + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 24, + horizontal: 40, + ).w, + padding: const EdgeInsets.symmetric( + vertical: 17, + ).w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.r), + gradient: const LinearGradient( + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + Color(0xffBEEF32), + Color(0xff2795E5), + Color(0xff8041FD), + ], + ), ), - ), - child: Center( - child: Text( - "Confirm", - style: TextStyle( - color: Colors.white, - fontSize: 18.sp, - fontWeight: FontWeight.bold), + child: Center( + child: Text( + "Confirm", + style: TextStyle( + color: Colors.white, + fontSize: 18.sp, + fontWeight: FontWeight.bold), + ), ), ), ), - ), - GestureDetector( - child: Text( - "Clear", - style: TextStyle( - color: Colors.white, - fontSize: 18.sp, + GestureDetector( + child: Text( + "Clear", + style: TextStyle( + color: Colors.white, + fontSize: 18.sp, + ), ), + onTap: () { + textEditingController.clear(); + }, ), - onTap: () { - textEditingController.clear(); - }, - ), - ], + ], + ), ), ), ), diff --git a/lib/common/utils/permission_util.dart b/lib/common/utils/permission_util.dart index 5edc505..54aae6a 100644 --- a/lib/common/utils/permission_util.dart +++ b/lib/common/utils/permission_util.dart @@ -1,11 +1,18 @@ +// Author: fengshengxiong +// Date: 2024/5/10 +// Description: 权限处理 + +import 'dart:io'; + import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:wallpaperx/common/components/dialog/remind_dialog.dart'; +import 'package:wallpaperx/global/app_config.dart'; class PermissionUtil { /// 检测是否有权限 /// [permissionList] 权限申请列表 - static Future checkPermission(List permissionList) async { + static Future checkPermission(List permissionList, {bool showDialog = true}) async { // 一个新待申请权限列表 List newPermissionList = []; // 遍历当前权限申请列表 @@ -21,19 +28,19 @@ class PermissionUtil { if (newPermissionList.isNotEmpty) { PermissionStatus permissionStatus = await _requestPermission(newPermissionList); switch (permissionStatus) { - // 拒绝状态 + // 拒绝状态 case PermissionStatus.denied: - showFailedDialog(newPermissionList); + if (showDialog) _showFailedDialog(newPermissionList); return false; - // 允许状态 + // 允许状态 case PermissionStatus.granted: case PermissionStatus.limited: - case PermissionStatus.provisional: return true; - // 永久拒绝 活动限制 + // 永久拒绝 + case PermissionStatus.provisional: case PermissionStatus.restricted: case PermissionStatus.permanentlyDenied: - showFailedDialog(newPermissionList, isPermanentlyDenied: true); + if (showDialog) _showFailedDialog(newPermissionList, isPermanentlyDenied: true); break; } } else { @@ -55,58 +62,53 @@ class PermissionUtil { return currentPermissionStatus; } + /// 请求位置权限 static Future checkLocationAlways() async { // 获取前置状态 // Android没有这一步 ios会先访问这个再访问其他的 - PermissionStatus status = PermissionStatus.granted; - status = await _checkSinglePermission(Permission.locationWhenInUse); + PermissionStatus status1 = PermissionStatus.granted; + status1 = await _checkSinglePermission(Permission.locationWhenInUse); // 获取第二个状态 PermissionStatus status2 = PermissionStatus.denied; // 如果前置状态为成功才能执行获取第二个状态 - if (status.isGranted) { + if (status1.isGranted) { status2 = await _checkSinglePermission(Permission.locationAlways); } // 如果两个都成功那么就返回成功 - if (status.isGranted && status2.isGranted) { + if (status1.isGranted && status2.isGranted) { return true; - - // 如果有一个拒绝那么就失败了 - } else if (status.isDenied || status2.isDenied) { - showFailedDialog( - [Permission.locationWhenInUse, Permission.locationAlways]); } else { - showFailedDialog( - [Permission.locationWhenInUse, Permission.locationAlways], - isPermanentlyDenied: true, + // 如果有一个拒绝那么就失败了 + _showFailedDialog([Permission.locationWhenInUse, Permission.locationAlways], + isPermanentlyDenied: Platform.isIOS ? true : false, ); } return false; } + /// 请求单个权限 static _checkSinglePermission(Permission permission) async { // 获取当前状态 PermissionStatus status = await permission.status; PermissionStatus currentPermissionStatus = PermissionStatus.granted; - // 如果它状态不是允许那么就去获取 if (!status.isGranted) { currentPermissionStatus = await _requestPermission([permission]); } - // 返回最终状态 return currentPermissionStatus; } /// 权限拒绝后弹窗 - static showFailedDialog(List permissionList, {bool isPermanentlyDenied = false}) async { + static _showFailedDialog(List permissionList, {bool isPermanentlyDenied = false}) async { Get.dialog( barrierDismissible: false, RemindDialog( - content: await _getInstructions(permissionList), - confirmText: isPermanentlyDenied ? 'Go Settings' : 'Confirm', + content: await _getDescription(permissionList), + confirmText: isPermanentlyDenied ? 'Go open' : 'Confirm', confirmOnTap: () { if (isPermanentlyDenied) { openAppSettings(); @@ -118,24 +120,26 @@ class PermissionUtil { ); } - /// 获取权限使用说明 - static Future _getInstructions(List permissionList) async { + /// 获取权限描述 + static Future _getDescription(List permissionList) async { late Permission failedPermission; - - // 遍历当前权限申请列表 for (Permission permission in permissionList) { - PermissionStatus status = await permission.status; - - // 如果不是允许状态就添加到新的申请列表中 - if (!status.isGranted || !status.isLimited) { + if (!await permission.status.isGranted) { failedPermission = permission; break; } } - String explain = ''; - if (failedPermission == Permission.storage || failedPermission == Permission.photosAddOnly) { - explain = 'We need access to your photo album so you can save wallpapers from the app to your album'; + + String description = ''; + if (failedPermission == Permission.storage) { + description = '$appName needs access to your camera to take photos for creating custom wallpapers. The photos you take will only be used to make personalized wallpapers within the app.'; + } else if (failedPermission == Permission.camera) { + description = 'This app can take photos and upload them to a private space.'; + } else if (failedPermission == Permission.photos) { + description = 'This enables you to upload photos and save wallpapers.'; + } else if (failedPermission == Permission.appTrackingTransparency) { + description = 'We need your permission to access the advertising identifier to provide better ad services.'; } - return explain; + return description; } -} +} \ No newline at end of file diff --git a/lib/global/app_config.dart b/lib/global/app_config.dart index 893d604..b6831b0 100644 --- a/lib/global/app_config.dart +++ b/lib/global/app_config.dart @@ -4,7 +4,7 @@ const appName = 'Visual Wallpaper'; /// 保底逻辑:本地设定截止日期(提交日期+3天,网络时间超过,则默认展示所有广告) DateTime getGuaranteedDate () { - DateTime? commitDate = DateUtils.getDateTime('2024-08-15 18:30:00'); + DateTime? commitDate = DateUtils.getDateTime('2024-08-16 09:30:00'); if (commitDate != null) { return commitDate.add(const Duration(days: 3)); } else { diff --git a/lib/page/custom/custom_view.dart b/lib/page/custom/custom_view.dart index df639c3..a920e31 100644 --- a/lib/page/custom/custom_view.dart +++ b/lib/page/custom/custom_view.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:flip_card/flip_card.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 73ae83a..059a0b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: wallpaperx description: "A new Flutter project." publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.2.0+6 +version: 1.2.0+8 environment: sdk: '>=3.4.1 <4.0.0'