Compare commits

..

11 Commits

9 changed files with 137 additions and 23 deletions

View File

@ -16,6 +16,7 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/pages/main/view.dart';
import 'package:pilipala/services/disable_battery_opt.dart';
import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/data.dart';
@ -71,6 +72,7 @@ void main() async {
)); ));
Data.init(); Data.init();
PiliSchame.init(); PiliSchame.init();
DisableBatteryOpt();
}); });
} }

View File

@ -148,9 +148,9 @@ class _BangumiPanelState extends State<BangumiPanel> {
Expanded( Expanded(
child: Material( child: Material(
child: ScrollablePositionedList.builder( child: ScrollablePositionedList.builder(
itemCount: widget.pages.length, itemCount: widget.pages.length + 1,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
bool isLastItem = index == widget.pages.length - 1; bool isLastItem = index == widget.pages.length;
bool isCurrentIndex = currentIndex == index; bool isCurrentIndex = currentIndex == index;
return isLastItem return isLastItem
? SizedBox( ? SizedBox(

View File

@ -161,9 +161,9 @@ class _SeasonPanelState extends State<SeasonPanel> {
Expanded( Expanded(
child: Material( child: Material(
child: ScrollablePositionedList.builder( child: ScrollablePositionedList.builder(
itemCount: episodes.length, itemCount: episodes.length + 1,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
bool isLastItem = index == episodes.length - 1; bool isLastItem = index == episodes.length;
bool isCurrentIndex = currentIndex == index; bool isCurrentIndex = currentIndex == index;
return isLastItem return isLastItem
? SizedBox( ? SizedBox(

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:flutter_volume_controller/flutter_volume_controller.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -674,13 +675,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_distance.value = dy; _distance.value = dy;
} else { } else {
// 右边区域 👈 // 右边区域 👈
EasyThrottle.throttle(
'setVolume', const Duration(milliseconds: 20), () {
final double level = (_.isFullScreen.value final double level = (_.isFullScreen.value
? Get.size.height ? Get.size.height
: screenWidth * 9 / 16) * : screenWidth * 9 / 16);
3; final double volume = _volumeValue.value -
final double volume = _volumeValue.value - delta / level; double.parse(delta.toStringAsFixed(1)) / level;
final double result = volume.clamp(0.0, 1.0); final double result = volume.clamp(0.0, 1.0);
setVolume(result); setVolume(result);
});
} }
}, },
onVerticalDragEnd: (DragEndDetails details) {}, onVerticalDragEnd: (DragEndDetails details) {},
@ -799,7 +803,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
// 锁 // 锁
Obx( Obx(
() => Visibility( () => Visibility(
visible: _.videoType.value != 'live', visible: _.videoType.value != 'live' && _.isFullScreen.value,
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FractionalTranslation( child: FractionalTranslation(

View File

@ -0,0 +1,40 @@
import 'dart:io';
import 'package:disable_battery_optimization/disable_battery_optimization.dart';
import 'package:pilipala/utils/storage.dart';
void DisableBatteryOpt() async {
if (!Platform.isAndroid) {
return;
}
// 本地缓存中读取 是否禁用了电池优化 默认未禁用
bool isDisableBatteryOptLocal =
GStrorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false);
if (!isDisableBatteryOptLocal) {
final isBatteryOptimizationDisabled =
await DisableBatteryOptimization.isBatteryOptimizationDisabled;
if (isBatteryOptimizationDisabled == false) {
final hasDisabled = await DisableBatteryOptimization
.showDisableBatteryOptimizationSettings();
// 设置为已禁用
GStrorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true);
}
}
bool isManufacturerBatteryOptimizationDisabled = GStrorage.localCache
.get('isManufacturerBatteryOptimizationDisabled', defaultValue: false);
if (!isManufacturerBatteryOptimizationDisabled) {
final isManBatteryOptimizationDisabled = await DisableBatteryOptimization
.isManufacturerBatteryOptimizationDisabled;
if (isManBatteryOptimizationDisabled == false) {
final hasDisabled = await DisableBatteryOptimization
.showDisableManufacturerBatteryOptimizationSettings(
"当前设备可能有额外的电池优化",
"按照步骤操作以禁用电池优化,以保证应用在后台正常运行",
);
// 设置为已禁用
GStrorage.localCache.put(
'isManufacturerBatteryOptimizationDisabled', hasDisabled == true);
}
}
}

View File

@ -1,40 +1,94 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:saver_gallery/saver_gallery.dart'; import 'package:saver_gallery/saver_gallery.dart';
class DownloadUtils { class DownloadUtils {
// 获取存储权限 // 获取存储权限
static requestStoragePer() async { static Future<bool> requestStoragePer() async {
Map<Permission, PermissionStatus> statuses = await [ await Permission.storage.request();
Permission.storage, PermissionStatus status = await Permission.storage.status;
Permission.photos, if (status == PermissionStatus.denied ||
].request(); status == PermissionStatus.permanentlyDenied) {
statuses[Permission.storage].toString(); SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('存储权限未授权'),
actions: [
TextButton(
onPressed: () async {
openAppSettings();
},
child: const Text('去授权'),
)
],
);
},
);
return false;
} else {
return true;
}
}
// 获取相册权限
static Future<bool> requestPhotoPer() async {
await Permission.photos.request();
PermissionStatus status = await Permission.photos.status;
if (status == PermissionStatus.denied ||
status == PermissionStatus.permanentlyDenied) {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('相册权限未授权'),
actions: [
TextButton(
onPressed: () async {
openAppSettings();
},
child: const Text('去授权'),
)
],
);
},
);
return false;
} else {
return true;
}
} }
static Future<bool> downloadImg(String imgUrl, static Future<bool> downloadImg(String imgUrl,
{String imgType = 'cover'}) async { {String imgType = 'cover'}) async {
try { try {
await requestStoragePer(); if (!await requestPhotoPer()) {
return false;
}
SmartDialog.showLoading(msg: '保存中'); SmartDialog.showLoading(msg: '保存中');
var response = await Dio() var response = await Dio()
.get(imgUrl, options: Options(responseType: ResponseType.bytes)); .get(imgUrl, options: Options(responseType: ResponseType.bytes));
final String imgSuffix = imgUrl.split('.').last;
String picName = String picName =
"plpl_${imgType}_${DateTime.now().toString().split('-').join()}"; "plpl_${imgType}_${DateTime.now().toString().replaceAll(RegExp(r'[- :]'), '').split('.').first}";
final SaveResult result = await SaverGallery.saveImage( final SaveResult result = await SaverGallery.saveImage(
Uint8List.fromList(response.data), Uint8List.fromList(response.data),
quality: 60, name: '$picName.$imgSuffix',
name: picName,
// 保存到 PiliPala文件夹 // 保存到 PiliPala文件夹
androidRelativePath: "Pictures/PiliPala", androidRelativePath: "Pictures/PiliPala",
androidExistNotSave: false, androidExistNotSave: false,
); );
SmartDialog.dismiss(); SmartDialog.dismiss();
if (result.isSuccess) { if (result.isSuccess) {
await SmartDialog.showToast('$picName」已保存 '); await SmartDialog.showToast('${'$picName.$imgSuffix'}」已保存 ');
} }
return true; return true;
} catch (err) { } catch (err) {

View File

@ -172,6 +172,10 @@ class LocalCacheKey {
// 代理host port // 代理host port
systemProxyHost = 'systemProxyHost', systemProxyHost = 'systemProxyHost',
systemProxyPort = 'systemProxyPort'; systemProxyPort = 'systemProxyPort';
static const String isDisableBatteryOptLocal = 'isDisableBatteryOptLocal',
isManufacturerBatteryOptimizationDisabled =
'isManufacturerBatteryOptimizationDisabled';
} }
class VideoBoxKey { class VideoBoxKey {

View File

@ -393,6 +393,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.0" version: "2.4.0"
disable_battery_optimization:
dependency: "direct main"
description:
name: disable_battery_optimization
sha256: "6b2ba802f984af141faf1b6b5fb956d5ef01f9cd555597c35b9cc335a03185ba"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
dismissible_page: dismissible_page:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -140,6 +140,8 @@ dependencies:
catcher_2: ^1.1.0 catcher_2: ^1.1.0
logger: ^2.0.2+1 logger: ^2.0.2+1
path: 1.8.3 path: 1.8.3
# 电池优化
disable_battery_optimization: ^1.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: