refactor(discord): remove rich presence and bundled SDK

Remove the presence setting, platform bridge, SDK archive, and SDK preparation from local and CI builds. Reject leftover Discord native libraries in release APK audits.
This commit is contained in:
zarzet committed 2026-09-26 03:07:04 +07:00
1 parent 863d1536dd
commit 728d179d69
43 files changed
+37 -1171

No files matched your search

-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "ar",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "de",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "en",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "es_ES",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "fr",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "hi",
"@@last_modified": "2026-04-28",
"appName": "Dd",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Bagian tampilan untuk memilih gaya visual aplikasi"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Bagikan lagu yang diputar di pemutar bawaan ke Discord. Discord harus terpasang dan sudah login.",
"discordPresenceMissing": "Pasang Discord dan login, lalu matikan dan nyalakan kembali pengaturan ini.",
"discordPresenceUnavailable": "Discord Rich Presence tidak tersedia pada build atau perangkat ini.",
"@@locale": "id",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "it",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "ja",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "디스코드 리치 프레즌스",
"discordPresenceDescription": "내부 플레이어에서 재생 중인 트랙을 디스코드에 공유합니다. 디스코드가 설치되어 있고 로그인되어 있어야 합니다",
"discordPresenceMissing": "디스코드를 설치하고 로그인한 다음 이 설정을 비활성화했다가 다시 활성화하세요",
"discordPresenceUnavailable": "이 빌드 또는 이 기기에서는 디스코드 리치 프레즌스를 사용할 수 없습니다",
"@@locale": "ko",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "nl",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "pt_PT",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Установите Discord и войдите в систему, а затем отключите и снова включите этот параметр.",
"discordPresenceUnavailable": "Discord Rich Presence не доступен в этой сборке или на этом устройстве.",
"@@locale": "ru",
"@@last_modified": "2026-04-28",
"appName": "Spotify",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "tr",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC Mobile",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "uk",
"@@last_modified": "2026-04-28",
"appName": "",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "zh_CN",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC 移动版",
-4
View File
@@ -39,10 +39,6 @@
"@appearanceStyle": {
"description": "Appearance section for choosing the app visual style"
},
"discordRichPresence": "Discord Rich Presence",
"discordPresenceDescription": "Share the track playing in the built-in player on Discord. Requires Discord to be installed and signed in.",
"discordPresenceMissing": "Install Discord and sign in, then turn this setting off and on again.",
"discordPresenceUnavailable": "Discord Rich Presence is unavailable in this build or on this device.",
"@@locale": "zh_TW",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC 行動版",
-4
View File
@@ -32,7 +32,6 @@ class AppSettings {
// Apply ReplayGain/R128 tags as volume normalization in the built-in player.
final bool playbackNormalization;
final bool autoMix;
final bool discordRichPresence;
final bool isFirstLaunch;
final bool checkForUpdates;
final String updateChannel;
@@ -145,7 +144,6 @@ class AppSettings {
this.embedReplayGain = false,
this.playbackNormalization = false,
this.autoMix = false,
this.discordRichPresence = false,
this.isFirstLaunch = true,
this.checkForUpdates = true,
this.updateChannel = 'stable',
@@ -233,7 +231,6 @@ class AppSettings {
bool? embedReplayGain,
bool? playbackNormalization,
bool? autoMix,
bool? discordRichPresence,
bool? isFirstLaunch,
bool? checkForUpdates,
String? updateChannel,
@@ -317,7 +314,6 @@ class AppSettings {
playbackNormalization:
playbackNormalization ?? this.playbackNormalization,
autoMix: autoMix ?? this.autoMix,
discordRichPresence: discordRichPresence ?? this.discordRichPresence,
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
checkForUpdates: checkForUpdates ?? this.checkForUpdates,
updateChannel: updateChannel ?? this.updateChannel,
-2
View File
@@ -23,7 +23,6 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
embedReplayGain: json['embedReplayGain'] as bool? ?? false,
playbackNormalization: json['playbackNormalization'] as bool? ?? false,
autoMix: json['autoMix'] as bool? ?? false,
discordRichPresence: json['discordRichPresence'] as bool? ?? false,
isFirstLaunch: json['isFirstLaunch'] as bool? ?? true,
checkForUpdates: json['checkForUpdates'] as bool? ?? true,
updateChannel: json['updateChannel'] as String? ?? 'stable',
@@ -123,7 +122,6 @@ Map<String, dynamic> _$AppSettingsToJson(
'embedReplayGain': instance.embedReplayGain,
'playbackNormalization': instance.playbackNormalization,
'autoMix': instance.autoMix,
'discordRichPresence': instance.discordRichPresence,
'isFirstLaunch': instance.isFirstLaunch,
'checkForUpdates': instance.checkForUpdates,
'updateChannel': instance.updateChannel,
-5
View File
@@ -556,11 +556,6 @@ class SettingsNotifier extends Notifier<AppSettings> {
_saveSettings();
}
void setDiscordRichPresence(bool enabled) {
state = state.copyWith(discordRichPresence: enabled);
_saveSettings();
}
void setEmbedMetadata(bool enabled) {
state = state.copyWith(embedMetadata: enabled);
_saveSettings();
-12
View File
@@ -12,7 +12,6 @@ import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/constants/app_info.dart';
import 'package:spotiflac_android/screens/upgrade_intro_screen.dart';
import 'package:spotiflac_android/services/upgrade_intro_service.dart';
import 'package:spotiflac_android/services/discord_presence_service.dart';
import 'package:spotiflac_android/providers/download_queue_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/providers/repo_provider.dart';
@@ -92,11 +91,6 @@ class _MainShellState extends ConsumerState<MainShell>
ref.read(settingsProvider).playbackNormalization,
);
setAutoMixEnabled(ref.read(settingsProvider).autoMix);
unawaited(
DiscordPresenceService.instance.setEnabled(
ref.read(settingsProvider).discordRichPresence,
),
);
// Deezer & co. localize artist/genre names by IP unless told the app's
// language (issue #480).
unawaited(
@@ -727,12 +721,6 @@ class _MainShellState extends ConsumerState<MainShell>
@override
Widget build(BuildContext context) {
ref.listen(settingsProvider.select((s) => s.discordRichPresence), (
_,
enabled,
) {
unawaited(DiscordPresenceService.instance.setEnabled(enabled));
});
ref.listen(settingsProvider.select((s) => s.playbackNormalization), (
_,
enabled,
@@ -11,7 +11,6 @@ import 'package:spotiflac_android/models/settings.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/providers/local_library_provider.dart';
import 'package:spotiflac_android/services/library_database.dart';
import 'package:spotiflac_android/services/discord_presence_service.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';
import 'package:spotiflac_android/utils/adaptive_layout.dart';
import 'package:spotiflac_android/widgets/duplicate_review_sheet.dart';
@@ -891,27 +890,8 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
value: settings.autoMix,
onChanged: (value) =>
ref.read(settingsProvider.notifier).setAutoMix(value),
showDivider: Platform.isAndroid,
showDivider: false,
),
if (Platform.isAndroid)
ValueListenableBuilder<String>(
valueListenable: DiscordPresenceService.instance.status,
builder: (context, status, _) => SettingsSwitchItem(
icon: Icons.share_outlined,
title: context.l10n.discordRichPresence,
subtitle: status == 'discord_missing'
? context.l10n.discordPresenceMissing
: status == 'sdk_unavailable' ||
status == 'unavailable'
? context.l10n.discordPresenceUnavailable
: context.l10n.discordPresenceDescription,
value: settings.discordRichPresence,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setDiscordRichPresence(value),
showDivider: false,
),
),
],
),
),
-135
View File
@@ -1,135 +0,0 @@
import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
/// Opt-in Rich Presence, inspired by @itsmegaaa's contribution in #575/#576.
/// Playback remains independent of Discord availability.
class DiscordPresenceService {
DiscordPresenceService({
MethodChannel channel = const MethodChannel('com.zarz.spotiflac/discord'),
}) : _channel = channel {
_channel.setMethodCallHandler((call) async {
if (call.method == 'status' && _enabled && _transportReady) {
status.value = call.arguments as String;
}
});
}
static final instance = DiscordPresenceService();
final MethodChannel _channel;
final ValueNotifier<String> status = ValueNotifier('disabled');
StreamSubscription<MediaItem?>? _mediaSubscription;
StreamSubscription<PlaybackState>? _stateSubscription;
MediaItem? _item;
PlaybackState? _playback;
bool _enabled = false;
bool _configured = false;
bool _transportReady = false;
int _generation = 0;
String? _lastPayload;
int? _lastStart;
Future<void> _pending = Future.value();
void bind(AudioHandler handler) {
unawaited(_mediaSubscription?.cancel());
unawaited(_stateSubscription?.cancel());
_mediaSubscription = handler.mediaItem.listen((item) {
_item = item;
_publish();
});
_stateSubscription = handler.playbackState.listen((state) {
_playback = state;
_publish();
});
}
Future<void> setEnabled(bool enabled) {
if (_configured && _enabled == enabled) return _pending;
_configured = true;
_enabled = enabled;
_transportReady = false;
final generation = ++_generation;
_lastPayload = null;
_lastStart = null;
_pending = _pending.then((_) async {
try {
final result = await _channel.invokeMethod<String>('configure', {
'enabled': enabled,
});
if (generation != _generation) return;
status.value = result ?? 'unavailable';
_transportReady = enabled && result == 'ready';
if (_transportReady) _publish();
} on PlatformException {
if (generation == _generation) status.value = 'unavailable';
} on MissingPluginException {
if (generation == _generation) status.value = 'sdk_unavailable';
}
});
return _pending;
}
void _publish() {
if (!_enabled || !_transportReady) return;
final item = _item;
final playback = _playback;
final playing =
item != null &&
playback != null &&
playback.playing &&
playback.processingState == AudioProcessingState.ready;
final position = playback?.position ?? Duration.zero;
final now = DateTime.now().millisecondsSinceEpoch;
final start = now - position.inMilliseconds;
final payload = playing
? <String, Object?>{
'title': item.title,
'artist': item.artist ?? '',
'album': item.album ?? '',
'cover': item.artUri?.scheme == 'https'
? item.artUri.toString()
: '',
'start': start,
'end': item.duration == null
? 0
: start + item.duration!.inMilliseconds,
}
: null;
// Position streams tick frequently. Only republish on track/state changes
// or a seek that shifts the playback timeline by at least two seconds.
final key = playing
? '${item.id}|${item.title}|${item.artist}|${item.album}|${item.artUri}|${item.duration}'
: 'clear';
if (key == _lastPayload &&
(!playing ||
(_lastStart != null && (start - _lastStart!).abs() < 2000))) {
return;
}
_lastPayload = key;
_lastStart = start;
final generation = _generation;
_pending = _pending.then((_) async {
if (!_enabled || generation != _generation) return;
try {
await _channel.invokeMethod<void>(
payload == null ? 'clear' : 'update',
payload,
);
} on PlatformException {
_lastPayload = null;
} on MissingPluginException {
_lastPayload = null;
}
});
}
Future<void> dispose() async {
await _mediaSubscription?.cancel();
await _stateSubscription?.cancel();
await setEnabled(false);
_channel.setMethodCallHandler(null);
status.dispose();
}
}
-2
View File
@@ -3,7 +3,6 @@ import 'dart:io';
import 'dart:math';
import 'package:audio_service/audio_service.dart';
import 'package:spotiflac_android/services/discord_presence_service.dart';
import 'package:spotiflac_android/services/player_widget_service.dart';
import 'package:audio_session/audio_session.dart'
show AudioSession, AudioSessionConfiguration, AudioInterruptionType;
@@ -1648,7 +1647,6 @@ Future<MusicPlayerHandler> _doInitMusicPlayer() async {
),
);
_handler = handler;
DiscordPresenceService.instance.bind(handler);
if (Platform.isAndroid || Platform.isIOS) {
PlayerWidgetService.instance.bind(handler);
}