feat: rebuild cross-extension sharing and queue controls

Co-authored-by: Amonoman <musaauron87@gmail.com>
This commit is contained in:
zarzet
2026-06-02 00:58:41 +07:00
co-authored by Amonoman
parent f52527a41b
commit ffdaf14ba5
38 changed files with 1850 additions and 122 deletions
@@ -0,0 +1,52 @@
import 'package:spotiflac_android/services/platform_bridge.dart';
class CrossExtensionShareResult {
final String extensionId;
final String displayName;
final bool found;
final String? url;
final String? itemName;
final String? itemArtists;
final String? error;
const CrossExtensionShareResult({
required this.extensionId,
required this.displayName,
required this.found,
this.url,
this.itemName,
this.itemArtists,
this.error,
});
factory CrossExtensionShareResult.fromJson(Map<String, dynamic> json) {
return CrossExtensionShareResult(
extensionId: json['extension_id'] as String? ?? '',
displayName: json['display_name'] as String? ?? '',
found: json['found'] as bool? ?? false,
url: json['url'] as String?,
itemName: json['item_name'] as String?,
itemArtists: json['item_artists'] as String?,
error: json['error'] as String?,
);
}
}
class CrossExtensionShareService {
const CrossExtensionShareService();
Future<List<CrossExtensionShareResult>> findAcrossExtensions({
required String name,
required String artists,
required String type,
required String sourceExtensionId,
}) async {
final results = await PlatformBridge.findCollectionAcrossExtensions(
name: name,
artists: artists,
type: type,
sourceExtensionId: sourceExtensionId,
);
return results.map(CrossExtensionShareResult.fromJson).toList();
}
}
+8
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -236,6 +237,7 @@ class NotificationService {
bool alreadyInLibrary = false,
}) async {
if (!_isInitialized) await initialize();
unawaited(HapticFeedback.mediumImpact());
String title;
if (alreadyInLibrary) {
@@ -286,6 +288,11 @@ class NotificationService {
}) async {
if (!_isInitialized) await initialize();
if (completedCount <= 0 && failedCount <= 0) return;
unawaited(
failedCount > 0
? HapticFeedback.heavyImpact()
: HapticFeedback.mediumImpact(),
);
final title = failedCount > 0
? (_l10n?.notifDownloadsFinished(completedCount, failedCount) ??
@@ -330,6 +337,7 @@ class NotificationService {
Future<void> showQueueCanceled({required int canceledCount}) async {
if (!_isInitialized) await initialize();
if (canceledCount <= 0) return;
unawaited(HapticFeedback.lightImpact());
final title = _l10n?.notifDownloadsCanceledTitle ?? 'Downloads canceled';
final body =
+19
View File
@@ -1309,6 +1309,25 @@ class PlatformBridge {
return _decodeMapListResult(result, 'searchTracksWithMetadataProviders');
}
static Future<List<Map<String, dynamic>>> findCollectionAcrossExtensions({
required String name,
required String artists,
required String type,
required String sourceExtensionId,
}) async {
final requestJson = jsonEncode({
'name': name,
'artists': artists,
'type': type,
'source_extension_id': sourceExtensionId,
});
final result = await _channel.invokeMethod(
'findCollectionAcrossExtensions',
requestJson,
);
return _decodeMapListResult(result, 'findCollectionAcrossExtensions');
}
static Future<void> cleanupExtensions() async {
_log.d('cleanupExtensions');
await _channel.invokeMethod('cleanupExtensions');
+6
View File
@@ -31,6 +31,12 @@ class ShareIntentService {
return url;
}
void injectUrl(String url) {
if (url.isEmpty) return;
_pendingUrl = url;
_sharedUrlController.add(url);
}
Future<void> initialize() async {
if (_initialized) return;
_initialized = true;