fix: все фото в пикере
This commit is contained in:
@@ -99,6 +99,15 @@ class CallBridge {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dropOngoing() async {
|
||||
if (!_android) return;
|
||||
try {
|
||||
await _method.invokeMethod<void>('dropOngoing');
|
||||
} catch (e) {
|
||||
logger.w('CallBridge.dropOngoing: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> notifyEnded() async {
|
||||
if (!_android) return;
|
||||
try {
|
||||
|
||||
@@ -57,6 +57,7 @@ class CallController {
|
||||
Stream<void> get incomingCanceled => _canceled.stream;
|
||||
|
||||
CallSession? _active;
|
||||
StreamSubscription<CallSessionState>? _activeSub;
|
||||
CallSession? get activeSession => _active;
|
||||
|
||||
IncomingCall? _pending;
|
||||
@@ -161,10 +162,7 @@ class CallController {
|
||||
osVersion: _api?.callsOsVersion,
|
||||
);
|
||||
final session = CallSession(ws2Config: config, role: CallRole.caller);
|
||||
_bind(session);
|
||||
await session.start();
|
||||
CallBridge.instance.notifyAccepted();
|
||||
return session;
|
||||
return _launch(session, session.start);
|
||||
}
|
||||
|
||||
Future<CreatedCall> createConference() async {
|
||||
@@ -189,10 +187,7 @@ class CallController {
|
||||
role: CallRole.joiner,
|
||||
isGroup: true,
|
||||
);
|
||||
_bind(session);
|
||||
await session.start();
|
||||
CallBridge.instance.notifyAccepted();
|
||||
return session;
|
||||
return _launch(session, session.start);
|
||||
}
|
||||
|
||||
Future<CallSession> acceptIncoming(IncomingCall call) async {
|
||||
@@ -209,11 +204,14 @@ class CallController {
|
||||
params: call.params,
|
||||
role: CallRole.callee,
|
||||
);
|
||||
_bind(session);
|
||||
await session.start();
|
||||
await session.accept();
|
||||
CallBridge.instance.notifyAccepted(caller: call.callerName);
|
||||
return session;
|
||||
return _launch(
|
||||
session,
|
||||
() async {
|
||||
await session.start();
|
||||
await session.accept();
|
||||
},
|
||||
caller: call.callerName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> rejectIncoming(IncomingCall call) async {
|
||||
@@ -244,18 +242,45 @@ class CallController {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<CallSession> _launch(
|
||||
CallSession session,
|
||||
Future<void> Function() open, {
|
||||
String? caller,
|
||||
}) async {
|
||||
_bind(session);
|
||||
try {
|
||||
await open();
|
||||
} catch (_) {
|
||||
await _release(session);
|
||||
try {
|
||||
await session.hangup();
|
||||
} catch (_) {}
|
||||
rethrow;
|
||||
}
|
||||
CallBridge.instance.notifyAccepted(caller: caller);
|
||||
return session;
|
||||
}
|
||||
|
||||
void _bind(CallSession session) {
|
||||
unawaited(_activeSub?.cancel());
|
||||
_active = session;
|
||||
session.stateStream.listen((state) {
|
||||
if (state == CallSessionState.ended && _active == session) {
|
||||
_active = null;
|
||||
CallBridge.instance.notifyEnded();
|
||||
_ended.add(null);
|
||||
}
|
||||
_activeSub = session.stateStream.listen((state) {
|
||||
if (state != CallSessionState.ended) return;
|
||||
unawaited(_release(session));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _release(CallSession session) async {
|
||||
if (!identical(_active, session)) return;
|
||||
_active = null;
|
||||
await _activeSub?.cancel();
|
||||
_activeSub = null;
|
||||
CallBridge.instance.notifyEnded();
|
||||
_ended.add(null);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_activeSub?.cancel();
|
||||
_pushSub?.cancel();
|
||||
_incoming.close();
|
||||
_ended.close();
|
||||
|
||||
@@ -25,6 +25,15 @@ abstract class GalleryItem {
|
||||
static GalleryItem fromFile(File file) => _FileGalleryItem(file);
|
||||
}
|
||||
|
||||
class GalleryPage {
|
||||
const GalleryPage({required this.items, required this.hasMore});
|
||||
|
||||
static const empty = GalleryPage(items: <GalleryItem>[], hasMore: false);
|
||||
|
||||
final List<GalleryItem> items;
|
||||
final bool hasMore;
|
||||
}
|
||||
|
||||
class PickedPhoto {
|
||||
final GalleryItem item;
|
||||
final File? editedFile;
|
||||
@@ -49,8 +58,10 @@ Future<(int, int)?> imageFileDimensions(File file) async {
|
||||
}
|
||||
|
||||
abstract class GallerySource {
|
||||
static const int pageSize = 120;
|
||||
|
||||
Future<GalleryPermission> ensurePermission();
|
||||
Future<List<GalleryItem>> load({int limit});
|
||||
Future<GalleryPage> load({int offset, int limit});
|
||||
Future<void> openSettings();
|
||||
Future<void> manageAccess();
|
||||
|
||||
@@ -71,20 +82,51 @@ class _PhotoManagerSource implements GallerySource {
|
||||
return GalleryPermission.denied;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GalleryItem>> load({int limit = 120}) async {
|
||||
final paths = await PhotoManager.getAssetPathList(
|
||||
type: RequestType.common,
|
||||
onlyAll: true,
|
||||
filterOption: FilterOptionGroup(
|
||||
orders: const [
|
||||
OrderOption(type: OrderOptionType.createDate, asc: false),
|
||||
],
|
||||
AssetPathEntity? _album;
|
||||
int _total = 0;
|
||||
|
||||
static FilterOptionGroup _filter() => FilterOptionGroup(
|
||||
imageOption: const FilterOption(
|
||||
sizeConstraint: SizeConstraint(ignoreSize: true),
|
||||
),
|
||||
videoOption: const FilterOption(
|
||||
sizeConstraint: SizeConstraint(ignoreSize: true),
|
||||
durationConstraint: DurationConstraint(
|
||||
max: Duration(days: 365),
|
||||
allowNullable: true,
|
||||
),
|
||||
),
|
||||
createTimeCond: DateTimeCond.def().copyWith(ignore: true),
|
||||
orders: const [OrderOption(type: OrderOptionType.createDate, asc: false)],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<GalleryPage> load({
|
||||
int offset = 0,
|
||||
int limit = GallerySource.pageSize,
|
||||
}) async {
|
||||
if (offset == 0 || _album == null) {
|
||||
final paths = await PhotoManager.getAssetPathList(
|
||||
type: RequestType.common,
|
||||
onlyAll: true,
|
||||
filterOption: _filter(),
|
||||
);
|
||||
if (paths.isEmpty) {
|
||||
_album = null;
|
||||
_total = 0;
|
||||
return GalleryPage.empty;
|
||||
}
|
||||
_album = paths.first;
|
||||
_total = await paths.first.assetCountAsync;
|
||||
}
|
||||
final album = _album;
|
||||
if (album == null || offset >= _total) return GalleryPage.empty;
|
||||
final end = offset + limit < _total ? offset + limit : _total;
|
||||
final assets = await album.getAssetListRange(start: offset, end: end);
|
||||
return GalleryPage(
|
||||
items: assets.map((a) => _AssetGalleryItem(a)).toList(),
|
||||
hasMore: end < _total,
|
||||
);
|
||||
if (paths.isEmpty) return const [];
|
||||
final assets = await paths.first.getAssetListRange(start: 0, end: limit);
|
||||
return assets.map((a) => _AssetGalleryItem(a)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -172,8 +214,34 @@ class _DesktopGallerySource implements GallerySource {
|
||||
Future<GalleryPermission> ensurePermission() async =>
|
||||
GalleryPermission.granted;
|
||||
|
||||
List<_FileGalleryItem> _all = const [];
|
||||
|
||||
@override
|
||||
Future<List<GalleryItem>> load({int limit = 120}) async {
|
||||
Future<GalleryPage> load({
|
||||
int offset = 0,
|
||||
int limit = GallerySource.pageSize,
|
||||
}) async {
|
||||
if (offset == 0 || _all.isEmpty) _all = _scan();
|
||||
if (offset >= _all.length) return GalleryPage.empty;
|
||||
final end = offset + limit < _all.length ? offset + limit : _all.length;
|
||||
final items = _all.sublist(offset, end);
|
||||
const batch = 8;
|
||||
const eager = 24;
|
||||
|
||||
Future<void> probeRange(int from, int to) async {
|
||||
for (var i = from; i < to; i += batch) {
|
||||
final stop = i + batch > to ? to : i + batch;
|
||||
await Future.wait(items.sublist(i, stop).map((it) => it.probe()));
|
||||
}
|
||||
}
|
||||
|
||||
final head = items.length < eager ? items.length : eager;
|
||||
await probeRange(0, head);
|
||||
if (head < items.length) unawaited(probeRange(head, items.length));
|
||||
return GalleryPage(items: items, hasMore: end < _all.length);
|
||||
}
|
||||
|
||||
List<_FileGalleryItem> _scan() {
|
||||
final entries = <({File file, DateTime modified})>[];
|
||||
for (final dir in _candidateDirs()) {
|
||||
if (!dir.existsSync()) continue;
|
||||
@@ -185,24 +253,7 @@ class _DesktopGallerySource implements GallerySource {
|
||||
} catch (_) {}
|
||||
}
|
||||
entries.sort((a, b) => b.modified.compareTo(a.modified));
|
||||
final items = entries
|
||||
.take(limit)
|
||||
.map((e) => _FileGalleryItem(e.file))
|
||||
.toList();
|
||||
const batch = 8;
|
||||
const eager = 24;
|
||||
|
||||
Future<void> probeRange(int from, int to) async {
|
||||
for (var i = from; i < to; i += batch) {
|
||||
final end = i + batch > to ? to : i + batch;
|
||||
await Future.wait(items.sublist(i, end).map((it) => it.probe()));
|
||||
}
|
||||
}
|
||||
|
||||
final head = items.length < eager ? items.length : eager;
|
||||
await probeRange(0, head);
|
||||
if (head < items.length) unawaited(probeRange(head, items.length));
|
||||
return items;
|
||||
return entries.map((e) => _FileGalleryItem(e.file)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'logger.dart';
|
||||
|
||||
class ScreenWake {
|
||||
ScreenWake._();
|
||||
|
||||
static final ScreenWake instance = ScreenWake._();
|
||||
|
||||
static const _channel = MethodChannel('ru.komet.app/screen');
|
||||
|
||||
final Set<Object> _holders = <Object>{};
|
||||
|
||||
bool get _supported {
|
||||
try {
|
||||
return Platform.isAndroid || Platform.isIOS;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> acquire(Object holder) async {
|
||||
if (!_supported || !_holders.add(holder)) return;
|
||||
if (_holders.length == 1) await _apply(true);
|
||||
}
|
||||
|
||||
Future<void> release(Object holder) async {
|
||||
if (!_supported || !_holders.remove(holder)) return;
|
||||
if (_holders.isEmpty) await _apply(false);
|
||||
}
|
||||
|
||||
Future<void> _apply(bool enabled) async {
|
||||
try {
|
||||
await _channel.invokeMethod<void>('setKeepAwake', {'enabled': enabled});
|
||||
} catch (e) {
|
||||
logger.w('ScreenWake._apply: enabled=$enabled $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user