opt video keyboard event

opt code

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-09-12 18:42:06 +08:00
parent d6bff33d29
commit b6352a6a43
42 changed files with 763 additions and 601 deletions

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/app_sign.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
@@ -312,13 +313,19 @@ class AccountManager extends Interceptor {
case DioExceptionType.sendTimeout:
return '发送请求超时,请检查网络设置';
case DioExceptionType.unknown:
final String res =
(await Connectivity().checkConnectivity()).first.title;
return '$res网络异常 ${error.error}';
String desc;
try {
desc = Utils.isMobile
? (await Connectivity().checkConnectivity()).first.desc
: '';
} catch (_) {
desc = '';
}
return '$desc网络异常 ${error.error}';
}
}
}
extension _ConnectivityResultExt on ConnectivityResult {
String get title => const ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
String get desc => const ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
}

View File

@@ -1,9 +1,11 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/permission_handler.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:dio/dio.dart';
@@ -12,7 +14,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:live_photo_maker/live_photo_maker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:saver_gallery/saver_gallery.dart';
import 'package:share_plus/share_plus.dart';
@@ -106,7 +107,7 @@ class ImageUtils {
required int height,
}) async {
try {
if (!await checkPermissionDependOnSdkInt(context)) {
if (Utils.isMobile && !await checkPermissionDependOnSdkInt(context)) {
return false;
}
if (!silentDownImg) SmartDialog.showLoading(msg: '正在下载');
@@ -165,7 +166,9 @@ class ImageUtils {
BuildContext context,
List<String> imgList,
) async {
if (!await checkPermissionDependOnSdkInt(context)) return false;
if (Utils.isMobile && !await checkPermissionDependOnSdkInt(context)) {
return false;
}
CancelToken? cancelToken;
if (!silentDownImg) {
cancelToken = CancelToken();
@@ -193,7 +196,7 @@ class ImageUtils {
await SaverGallery.saveFile(
filePath: filePath,
fileName: name,
androidRelativePath: "Pictures/PiliPlus",
androidRelativePath: "Pictures/${Constants.appName}",
skipIfExists: false,
).whenComplete(File(filePath).tryDel);
}
@@ -271,7 +274,7 @@ class ImageUtils {
result = await SaverGallery.saveImage(
bytes,
fileName: fileName,
androidRelativePath: "Pictures/PiliPlus",
androidRelativePath: "Pictures/${Constants.appName}",
skipIfExists: false,
);
SmartDialog.dismiss();
@@ -313,7 +316,7 @@ class ImageUtils {
result = await SaverGallery.saveFile(
filePath: filePath,
fileName: fileName,
androidRelativePath: "Pictures/PiliPlus",
androidRelativePath: "Pictures/${Constants.appName}",
skipIfExists: false,
).whenComplete(file.tryDel);
} else {

View File

@@ -0,0 +1,201 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
export 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'
show
Permission,
PermissionStatus,
PermissionStatusGetters,
PermissionWithService,
FuturePermissionStatusGetters,
ServiceStatus,
ServiceStatusGetters,
FutureServiceStatusGetters;
PermissionHandlerPlatform get _handler => PermissionHandlerPlatform.instance;
/// Opens the app settings page.
///
/// Returns [true] if the app settings page could be opened, otherwise [false].
Future<bool> openAppSettings() => _handler.openAppSettings();
/// Actions that can be executed on a permission.
extension PermissionActions on Permission {
/// Callback for when permission is denied.
static FutureOr<void>? Function()? _onDenied;
/// Callback for when permission is granted.
static FutureOr<void>? Function()? _onGranted;
/// Callback for when permission is permanently denied.
static FutureOr<void>? Function()? _onPermanentlyDenied;
/// Callback for when permission is restricted.
static FutureOr<void>? Function()? _onRestricted;
/// Callback for when permission is limited.
static FutureOr<void>? Function()? _onLimited;
/// Callback for when permission is Provisional.
static FutureOr<void>? Function()? _onProvisional;
/// Method to set a callback for when permission is denied.
Permission onDeniedCallback(FutureOr<void>? Function()? callback) {
_onDenied = callback;
return this;
}
/// Method to set a callback for when permission is granted.
Permission onGrantedCallback(FutureOr<void>? Function()? callback) {
_onGranted = callback;
return this;
}
/// Method to set a callback for when permission is permanently denied.
Permission onPermanentlyDeniedCallback(FutureOr<void>? Function()? callback) {
_onPermanentlyDenied = callback;
return this;
}
/// Method to set a callback for when permission is restricted.
Permission onRestrictedCallback(FutureOr<void>? Function()? callback) {
_onRestricted = callback;
return this;
}
/// Method to set a callback for when permission is limited.
Permission onLimitedCallback(FutureOr<void>? Function()? callback) {
_onLimited = callback;
return this;
}
/// Method to set a callback for when permission is provisional.
Permission onProvisionalCallback(FutureOr<void>? Function()? callback) {
_onProvisional = callback;
return this;
}
/// Checks the current status of the given [Permission].
///
/// Notes about specific permissions:
/// - **[Permission.bluetooth]**
/// - iOS 13.0 only:
/// - The method will **always** return [PermissionStatus.denied],
/// regardless of the actual status. For the actual permission state,
/// use [Permission.bluetooth.request]. Note that this will show a
/// permission dialog if the permission was not yet requested.
Future<PermissionStatus> get status => _handler.checkPermissionStatus(this);
/// If you should show a rationale for requesting permission.
///
/// This is only implemented on Android, calling this on iOS always returns
/// [false].
Future<bool> get shouldShowRequestRationale async {
if (defaultTargetPlatform != TargetPlatform.android) {
return false;
}
return _handler.shouldShowRequestPermissionRationale(this);
}
/// Request the user for access to this [Permission], if access hasn't already
/// been grant access before.
///
/// Returns the new [PermissionStatus].
Future<PermissionStatus> request() async {
final permissionStatus =
(await [this].request())[this] ?? PermissionStatus.denied;
if (permissionStatus.isDenied) {
_onDenied?.call();
} else if (permissionStatus.isGranted) {
_onGranted?.call();
} else if (permissionStatus.isPermanentlyDenied) {
_onPermanentlyDenied?.call();
} else if (permissionStatus.isRestricted) {
_onRestricted?.call();
} else if (permissionStatus.isLimited) {
_onLimited?.call();
} else if (permissionStatus.isProvisional) {
_onProvisional?.call();
}
return permissionStatus;
}
}
/// Shortcuts for checking the [status] of a [Permission].
extension PermissionCheckShortcuts on Permission {
/// If the user granted this permission.
Future<bool> get isGranted => status.isGranted;
/// If the user denied this permission.
Future<bool> get isDenied => status.isDenied;
/// If the OS denied this permission. The user cannot change the status,
/// possibly due to active restrictions such as parental controls being in
/// place.
/// *Only supported on iOS.*
Future<bool> get isRestricted => status.isRestricted;
/// User has authorized this application for limited access.
/// *Only supported on iOS.(iOS14+ for photos, ios18+ for contacts)*
Future<bool> get isLimited => status.isLimited;
/// Returns `true` when permissions are denied permanently.
///
/// When permissions are denied permanently, no new permission dialog will
/// be showed to the user. Consuming Apps should redirect the user to the
/// App settings to change permissions.
Future<bool> get isPermanentlyDenied => status.isPermanentlyDenied;
/// If the application is provisionally authorized to post noninterruptive user notifications.
/// *Only supported on iOS.*
Future<bool> get isProvisional => status.isProvisional;
}
/// Actions that apply only to permissions that have an associated service.
extension ServicePermissionActions on PermissionWithService {
/// Checks the current status of the service associated with the given
/// [Permission].
///
/// Notes about specific permissions:
/// - **[Permission.phone]**
/// - Android:
/// - The method will return [ServiceStatus.notApplicable] when:
/// - the device lacks the TELEPHONY feature
/// - TelephonyManager.getPhoneType() returns PHONE_TYPE_NONE
/// - when no Intents can be resolved to handle the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// - the SIM card is missing
/// - iOS:
/// - The method will return [ServiceStatus.notApplicable] when:
/// - the native code can not find a handler for the `tel:` scheme
/// - The method will return [ServiceStatus.disabled] when:
/// - the mobile network code (MNC) is either 0 or 65535. See
/// https://stackoverflow.com/a/11595365 for details
/// - **PLEASE NOTE that this is still not a perfect indication** of the
/// device's capability to place & connect phone calls as it also depends
/// on the network condition.
/// - **[Permission.bluetooth]**
/// - iOS:
/// - The method will **always** return [ServiceStatus.disabled] when the
/// Bluetooth permission was denied by the user. It is impossible to
/// obtain the actual Bluetooth service status without having the
/// Bluetooth permission granted.
/// - The method will prompt the user for Bluetooth permission if the
/// permission was not yet requested.
Future<ServiceStatus> get serviceStatus => _handler.checkServiceStatus(this);
}
/// Actions that can be taken on a [List] of [Permission]s.
extension PermissionListActions on List<Permission> {
/// Requests the user for access to these permissions, if they haven't already
/// been granted before.
///
/// Returns a [Map] containing the status per requested [Permission].
Future<Map<Permission, PermissionStatus>> request() =>
_handler.requestPermissions(this);
}

View File

@@ -135,7 +135,8 @@ class SettingBoxKey {
showFsScreenshotBtn = 'showFsScreenshotBtn',
showFsLockBtn = 'showFsLockBtn',
silentDownImg = 'silentDownImg',
showMemberShop = 'showMemberShop';
showMemberShop = 'showMemberShop',
minimizeOnExit = 'minimizeOnExit';
static const String subtitlePreferenceV2 = 'subtitlePreferenceV2',
enableDragSubtitle = 'enableDragSubtitle',

View File

@@ -809,4 +809,7 @@ abstract class Pref {
static bool get showSuperChat =>
_setting.get(SettingBoxKey.showSuperChat, defaultValue: true);
static bool get minimizeOnExit =>
_setting.get(SettingBoxKey.minimizeOnExit, defaultValue: true);
}

View File

@@ -1,6 +1,7 @@
import 'dart:io' show Platform;
import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/ua_type.dart';
@@ -61,7 +62,7 @@ class Update {
Text('${res.data[0]['body']}'),
TextButton(
onPressed: () => PageUtils.launchURL(
'https://github.com/bggRGjQaUbCoE/PiliPlus/commits/main',
'${Constants.sourceCodeUrl}/commits/main',
),
child: Text(
"点此查看完整更新(即commit)内容",
@@ -134,9 +135,7 @@ class Update {
download('ios');
}
} catch (_) {
PageUtils.launchURL(
'https://github.com/bggRGjQaUbCoE/PiliPlus/releases/latest',
);
PageUtils.launchURL('${Constants.sourceCodeUrl}/releases/latest');
}
}
}

View File

@@ -3,6 +3,8 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:PiliPlus/common/constants.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -14,13 +16,24 @@ import 'package:share_plus/share_plus.dart';
class Utils {
static final Random random = Random();
static const channel = MethodChannel("PiliPlus");
static const channel = MethodChannel(Constants.appName);
static final bool isMobile = Platform.isAndroid || Platform.isIOS;
static final bool isDesktop =
Platform.isWindows || Platform.isMacOS || Platform.isLinux;
static Future<bool> get isWiFi async {
try {
return Utils.isMobile &&
(await Connectivity().checkConnectivity()).contains(
ConnectivityResult.wifi,
);
} catch (_) {
return true;
}
}
static Color parseColor(String color) =>
Color(int.parse(color.replaceFirst('#', 'FF'), radix: 16));
@@ -59,6 +72,10 @@ class Utils {
}
static Future<void> shareText(String text) async {
if (Utils.isDesktop) {
copyText(text);
return;
}
try {
await SharePlus.instance.share(
ShareParams(