import 'dart:typed_data'; import 'package:aves/model/entry.dart'; import 'package:aves/services/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; import 'package:latlong2/latlong.dart'; class AndroidAppService { static const platform = MethodChannel('deckers.thibault/aves/app'); static Future> getPackages() async { try { final result = await platform.invokeMethod('getPackages'); final packages = (result as List).cast().map((map) => Package.fromMap(map)).toSet(); // additional info for known directories final kakaoTalk = packages.firstWhereOrNull((package) => package.packageName == 'com.kakao.talk'); if (kakaoTalk != null) { kakaoTalk.ownedDirs.add('KakaoTalkDownload'); } return packages; } on PlatformException catch (e) { await reportService.recordChannelError('getPackages', e); } return {}; } static Future getAppIcon(String packageName, double size) async { try { final result = await platform.invokeMethod('getAppIcon', { 'packageName': packageName, 'sizeDip': size, }); if (result != null) return result as Uint8List; } on PlatformException catch (e) { await reportService.recordChannelError('getAppIcon', e); } return Uint8List(0); } static Future copyToClipboard(String uri, String? label) async { try { final result = await platform.invokeMethod('copyToClipboard', { 'uri': uri, 'label': label, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('copyToClipboard', e); } return false; } static Future edit(String uri, String mimeType) async { try { final result = await platform.invokeMethod('edit', { 'uri': uri, 'mimeType': mimeType, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('edit', e); } return false; } static Future open(String uri, String mimeType) async { try { final result = await platform.invokeMethod('open', { 'uri': uri, 'mimeType': mimeType, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('open', e); } return false; } static Future openMap(LatLng latLng) async { final latitude = roundToPrecision(latLng.latitude, decimals: 6); final longitude = roundToPrecision(latLng.longitude, decimals: 6); final geoUri = 'geo:$latitude,$longitude?q=$latitude,$longitude'; try { final result = await platform.invokeMethod('openMap', { 'geoUri': geoUri, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('openMap', e); } return false; } static Future setAs(String uri, String mimeType) async { try { final result = await platform.invokeMethod('setAs', { 'uri': uri, 'mimeType': mimeType, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('setAs', e); } return false; } static Future shareEntries(Iterable entries) async { // loosen mime type to a generic one, so we can share with badly defined apps // e.g. Google Lens declares receiving "image/jpeg" only, but it can actually handle more formats final urisByMimeType = groupBy(entries, (e) => e.mimeTypeAnySubtype).map((k, v) => MapEntry(k, v.map((e) => e.uri).toList())); try { final result = await platform.invokeMethod('share', { 'urisByMimeType': urisByMimeType, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('shareEntries', e); } return false; } static Future shareSingle(String uri, String mimeType) async { try { final result = await platform.invokeMethod('share', { 'urisByMimeType': { mimeType: [uri] }, }); if (result != null) return result as bool; } on PlatformException catch (e) { await reportService.recordChannelError('shareSingle', e); } return false; } }