mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-02 11:05:38 +02:00
chore: clean up redundant comments across Go backend and Flutter sources
This commit is contained in:
@@ -334,7 +334,6 @@ object NativeDownloadFinalizer {
|
||||
}
|
||||
|
||||
private fun currentStatus(@Suppress("UNUSED_PARAMETER") status: String) {
|
||||
// Kept as a narrow hook for future richer progress snapshots.
|
||||
}
|
||||
|
||||
private fun cleanupFailedFinalizationOutput(
|
||||
|
||||
@@ -314,7 +314,6 @@ func marshalAPETag(tag *APETag) ([]byte, error) {
|
||||
footerFlags := uint32(1 << 31)
|
||||
footer := buildAPEHeaderFooter(version, tagSize, itemCount, footerFlags)
|
||||
|
||||
// Final layout: header + items + footer
|
||||
result := make([]byte, 0, len(header)+len(itemsData)+len(footer))
|
||||
result = append(result, header...)
|
||||
result = append(result, itemsData...)
|
||||
|
||||
@@ -783,7 +783,6 @@ func (c *DeezerClient) GetArtist(ctx context.Context, artistID string) (*ArtistR
|
||||
// not include this field. Albums whose track count is already known (non-zero)
|
||||
// are skipped.
|
||||
func (c *DeezerClient) fetchAlbumTrackCounts(ctx context.Context, albums []ArtistAlbumMetadata) {
|
||||
// Find albums that need track counts
|
||||
type indexedID struct {
|
||||
idx int
|
||||
albumID string
|
||||
|
||||
@@ -1380,7 +1380,6 @@ func ReadFileMetadata(filePath string) (string, error) {
|
||||
} else if isApe || isWv || isMpc {
|
||||
result["format"] = strings.TrimPrefix(filepath.Ext(filePath), ".")
|
||||
result["audio_codec"] = result["format"]
|
||||
// APE, WavPack, Musepack: read APEv2 tags
|
||||
apeTag, apeErr := ReadAPETags(filePath)
|
||||
if apeErr == nil && apeTag != nil {
|
||||
meta := APETagToAudioMetadata(apeTag)
|
||||
@@ -1613,7 +1612,6 @@ func EditFileMetadata(filePath, metadataJSON string) (string, error) {
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
// APE/WV/MPC: write APEv2 tags natively
|
||||
if isApeFile {
|
||||
trackNum := 0
|
||||
totalTracks := 0
|
||||
@@ -2710,8 +2708,6 @@ func ReEnrichFile(requestJSON string) (string, error) {
|
||||
|
||||
GoLog("[ReEnrich] Starting re-enrichment for: %s\n", req.FilePath)
|
||||
|
||||
// When search_online is true, search for metadata from internet using the
|
||||
// configured metadata-provider priority.
|
||||
if req.SearchOnline {
|
||||
found := false
|
||||
|
||||
@@ -2880,7 +2876,6 @@ func ReEnrichFile(requestJSON string) (string, error) {
|
||||
}
|
||||
|
||||
if isFlac {
|
||||
// Native Go FLAC metadata embedding.
|
||||
// Only populate Metadata fields for selected update groups; empty/zero
|
||||
// values cause EmbedMetadata's setComment() to skip those tags,
|
||||
// preserving whatever is already in the file.
|
||||
|
||||
@@ -222,7 +222,6 @@ func (m *ExtensionManifest) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Select type requires options
|
||||
if setting.Type == SettingTypeSelect && len(setting.Options) == 0 {
|
||||
return &ManifestValidationError{
|
||||
Field: fmt.Sprintf("settings[%d].options", i),
|
||||
|
||||
@@ -286,7 +286,6 @@ func (r *extensionRuntime) transformBlockCipher(call goja.FunctionCall, decrypt
|
||||
}
|
||||
switch parsedOptions.Mode {
|
||||
case "cbc", "ctr":
|
||||
// supported
|
||||
default:
|
||||
return r.vm.ToValue(map[string]interface{}{
|
||||
"success": false,
|
||||
|
||||
@@ -370,7 +370,6 @@ func (r *extensionRuntime) fileDownloadChunked(client *http.Client, urlStr, full
|
||||
var totalSize int64
|
||||
contentRange := probeResp.Header.Get("Content-Range")
|
||||
if contentRange != "" {
|
||||
// Format: "bytes 0-1/12345"
|
||||
if idx := strings.LastIndex(contentRange, "/"); idx >= 0 {
|
||||
sizeStr := contentRange[idx+1:]
|
||||
if sizeStr != "*" {
|
||||
@@ -457,7 +456,6 @@ func (r *extensionRuntime) fileDownloadChunked(client *http.Client, urlStr, full
|
||||
break // Success
|
||||
}
|
||||
|
||||
// Non-success status
|
||||
io.Copy(io.Discard, chunkResp.Body)
|
||||
chunkResp.Body.Close()
|
||||
|
||||
@@ -474,7 +472,6 @@ func (r *extensionRuntime) fileDownloadChunked(client *http.Client, urlStr, full
|
||||
})
|
||||
}
|
||||
|
||||
// Read chunk body and write to file
|
||||
chunkWritten := int64(0)
|
||||
for {
|
||||
nr, er := chunkResp.Body.Read(buf)
|
||||
|
||||
@@ -41,8 +41,6 @@ const (
|
||||
wavFormatExtensn = 0xFFFE
|
||||
)
|
||||
|
||||
// ---------- low-level chunk size helpers ----------
|
||||
|
||||
func putUint32(dst []byte, le bool, v uint32) {
|
||||
if le {
|
||||
binary.LittleEndian.PutUint32(dst, v)
|
||||
@@ -95,8 +93,6 @@ func parseExtendedFloat80(b []byte) float64 {
|
||||
return sign * float64(mantissa) * math.Pow(2, float64(exponent-16383-63))
|
||||
}
|
||||
|
||||
// ---------- WAV (RIFF) ----------
|
||||
|
||||
type wavProbe struct {
|
||||
sampleRate int
|
||||
bitDepth int
|
||||
@@ -289,8 +285,6 @@ func ReadWAVTags(filePath string) (*AudioMetadata, error) {
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// ---------- AIFF / AIFC ----------
|
||||
|
||||
type aiffProbe struct {
|
||||
sampleRate int
|
||||
bitDepth int
|
||||
@@ -443,8 +437,6 @@ func ReadAIFFTags(filePath string) (*AudioMetadata, error) {
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// ---------- ID3v2 reading from a buffered chunk ----------
|
||||
|
||||
// readID3v2FromBytes parses an in-memory ID3v2 tag (the contents of a WAV "id3 "
|
||||
// or AIFF "ID3 " chunk) by reusing the existing frame parsers.
|
||||
func readID3v2FromBytes(data []byte) (*AudioMetadata, error) {
|
||||
@@ -535,8 +527,6 @@ func extractAPICFromID3(tag []byte) ([]byte, string) {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// ---------- ID3v2.4 building ----------
|
||||
|
||||
// buildID3v24Tag builds a UTF-8 ID3v2.4 tag from metadata plus optional cover.
|
||||
func buildID3v24Tag(meta *AudioMetadata, coverData []byte, coverMIME string) []byte {
|
||||
var frames bytes.Buffer
|
||||
@@ -642,8 +632,6 @@ func buildID3v24Tag(meta *AudioMetadata, coverData []byte, coverMIME string) []b
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
// ---------- tag writing (streaming chunk rewrite) ----------
|
||||
|
||||
// writeID3Chunk rewrites filePath, replacing any existing tag chunk (chunkID,
|
||||
// matched case-insensitively) with a fresh ID3v2.4 chunk appended at the end.
|
||||
// The audio data and all other chunks are preserved; container size is patched.
|
||||
@@ -692,7 +680,6 @@ func writeID3Chunk(filePath, expectMagic, chunkID string, le bool, id3 []byte) e
|
||||
pad := int64(size) & 1
|
||||
|
||||
if strings.EqualFold(id, chunkID) {
|
||||
// Drop the existing tag chunk.
|
||||
if _, err := in.Seek(int64(size)+pad, io.SeekCurrent); err != nil {
|
||||
cleanup()
|
||||
return err
|
||||
@@ -711,7 +698,6 @@ func writeID3Chunk(filePath, expectMagic, chunkID string, le bool, id3 []byte) e
|
||||
bodyLen += 8 + int64(size) + pad
|
||||
}
|
||||
|
||||
// Append the new tag chunk.
|
||||
newSize := len(id3)
|
||||
chunkHdr := make([]byte, 8)
|
||||
copy(chunkHdr[0:4], chunkID)
|
||||
@@ -890,8 +876,6 @@ func WriteAIFFTags(filePath string, fields map[string]string) error {
|
||||
return writeID3Chunk(filePath, "FORM", id3ChunkAIFF, false, tag)
|
||||
}
|
||||
|
||||
// ---------- library scan integration ----------
|
||||
|
||||
func scanWAVFile(filePath string, result *LibraryScanResult, displayNameHint string) (*LibraryScanResult, error) {
|
||||
if metadata, err := ReadWAVTags(filePath); err == nil && metadata != nil {
|
||||
applyAudioMetadataToScan(metadata, result)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import Gobackend // Import Go framework
|
||||
import Gobackend
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
@@ -632,7 +632,6 @@ import Gobackend // Import Go framework
|
||||
GobackendClearTrackCache()
|
||||
return nil
|
||||
|
||||
// Log methods
|
||||
case "getLogs":
|
||||
let response = GobackendGetLogs()
|
||||
return response
|
||||
@@ -657,7 +656,6 @@ import Gobackend // Import Go framework
|
||||
GobackendSetLoggingEnabled(enabled)
|
||||
return nil
|
||||
|
||||
// Extension System methods
|
||||
case "initExtensionSystem":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let extensionsDir = args["extensions_dir"] as! String
|
||||
@@ -822,7 +820,6 @@ import Gobackend // Import Go framework
|
||||
GobackendCleanupExtensions()
|
||||
return nil
|
||||
|
||||
// Extension Auth API
|
||||
case "getExtensionPendingAuth":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let extensionId = args["extension_id"] as! String
|
||||
@@ -863,7 +860,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Extension FFmpeg API
|
||||
case "getPendingFFmpegCommand":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let commandId = args["command_id"] as! String
|
||||
@@ -885,7 +881,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Extension Custom Search API
|
||||
case "customSearchWithExtension":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let extensionId = args["extension_id"] as! String
|
||||
@@ -907,7 +902,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Extension URL Handler API
|
||||
case "handleURLWithExtension":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let url = args["url"] as! String
|
||||
@@ -926,7 +920,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Extension Post-Processing API
|
||||
case "runPostProcessing":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let filePath = args["file_path"] as! String
|
||||
@@ -948,7 +941,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Extension Store
|
||||
case "initExtensionStore":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let cacheDir = args["cache_dir"] as! String
|
||||
@@ -1006,7 +998,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return nil
|
||||
|
||||
// Extension Home Feed API
|
||||
case "getExtensionHomeFeed":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let extensionId = args["extension_id"] as! String
|
||||
@@ -1022,7 +1013,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// Local Library Scanning
|
||||
case "setLibraryCoverCacheDir":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let cacheDir = args["cache_dir"] as! String
|
||||
@@ -1059,7 +1049,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// iOS Security-Scoped Bookmark for Local Library
|
||||
case "resolveIosBookmark":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let bookmarkBase64 = args["bookmark"] as! String
|
||||
@@ -1079,7 +1068,6 @@ import Gobackend // Import Go framework
|
||||
let path = args["path"] as! String
|
||||
return try createIosBookmarkFromPath(path)
|
||||
|
||||
// Lyrics Provider Settings
|
||||
case "setLyricsProviders":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let providersJson = args["providers_json"] as? String ?? "[]"
|
||||
@@ -1109,7 +1097,6 @@ import Gobackend // Import Go framework
|
||||
if let error = error { throw error }
|
||||
return response
|
||||
|
||||
// CUE Sheet Parsing
|
||||
case "parseCueSheet":
|
||||
let args = call.arguments as! [String: Any]
|
||||
let cuePath = args["cue_path"] as! String
|
||||
|
||||
@@ -29,14 +29,14 @@ class DownloadItem {
|
||||
final DownloadStatus status;
|
||||
final double progress;
|
||||
final double speedMBps;
|
||||
final int bytesReceived; // Bytes downloaded so far
|
||||
final int bytesReceived;
|
||||
final int bytesTotal; // Total bytes when the server provides content length
|
||||
final String? filePath;
|
||||
final String? error;
|
||||
final DownloadErrorType? errorType;
|
||||
final DateTime createdAt;
|
||||
final String? qualityOverride; // Override quality for this specific download
|
||||
final String? playlistName; // Playlist context for folder organization
|
||||
final String? qualityOverride;
|
||||
final String? playlistName;
|
||||
final int? playlistPosition; // 1-based position in the source playlist
|
||||
|
||||
const DownloadItem({
|
||||
|
||||
+10
-10
@@ -15,11 +15,11 @@ class AppSettings {
|
||||
final String storageMode; // 'app' or 'saf'
|
||||
final String downloadTreeUri; // SAF persistable tree URI
|
||||
final bool autoFallback;
|
||||
final bool embedMetadata; // Master switch for metadata/cover/lyrics embedding
|
||||
final bool embedMetadata;
|
||||
final String
|
||||
artistTagMode; // 'joined' or 'split_vorbis' for Vorbis-based formats
|
||||
final bool embedLyrics;
|
||||
final bool embedReplayGain; // Calculate and embed ReplayGain tags
|
||||
final bool embedReplayGain;
|
||||
final bool maxQualityCover;
|
||||
final bool isFirstLaunch;
|
||||
final bool checkForUpdates;
|
||||
@@ -50,7 +50,7 @@ class AppSettings {
|
||||
final bool
|
||||
useAllFilesAccess; // Android 13+ only: enable MANAGE_EXTERNAL_STORAGE
|
||||
final bool
|
||||
autoExportFailedDownloads; // Auto export failed downloads to TXT file
|
||||
autoExportFailedDownloads;
|
||||
final String
|
||||
downloadNetworkMode; // 'any' = WiFi + Mobile, 'wifi_only' = WiFi only
|
||||
final bool
|
||||
@@ -62,20 +62,20 @@ class AppSettings {
|
||||
final bool
|
||||
nativeDownloadWorkerEnabled; // Experimental Android service-owned worker
|
||||
|
||||
final bool localLibraryEnabled; // Enable local library scanning
|
||||
final String localLibraryPath; // Path to scan for audio files
|
||||
final bool localLibraryEnabled;
|
||||
final String localLibraryPath;
|
||||
final String
|
||||
localLibraryBookmark; // Base64-encoded iOS security-scoped bookmark
|
||||
final bool
|
||||
localLibraryShowDuplicates; // Show indicator when searching for existing tracks
|
||||
localLibraryShowDuplicates;
|
||||
final String
|
||||
localLibraryAutoScan; // Auto-scan mode: 'off', 'on_open', 'daily', 'weekly'
|
||||
|
||||
final bool
|
||||
hasCompletedTutorial; // Track if user has completed the app tutorial
|
||||
hasCompletedTutorial;
|
||||
|
||||
final List<String>
|
||||
lyricsProviders; // Ordered list of enabled lyrics provider IDs
|
||||
lyricsProviders;
|
||||
final bool
|
||||
lyricsIncludeTranslationNetease; // Append translated lyrics (Netease)
|
||||
final bool
|
||||
@@ -91,8 +91,8 @@ class AppSettings {
|
||||
lastSeenVersion; // Last app version the user has acknowledged (e.g. '3.7.0')
|
||||
|
||||
final bool
|
||||
deduplicateDownloads; // Skip downloading tracks already present in history
|
||||
final bool saveDownloadHistory; // Record completed downloads in local history
|
||||
deduplicateDownloads;
|
||||
final bool saveDownloadHistory;
|
||||
|
||||
const AppSettings({
|
||||
this.defaultService = '',
|
||||
|
||||
@@ -4539,14 +4539,13 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
.where((item) => _albumRgKey(item.track) == key)
|
||||
.toList();
|
||||
|
||||
// If any item is still in-flight, the album isn't complete yet.
|
||||
final pending = albumItemsInQueue.where(
|
||||
(item) =>
|
||||
item.status == DownloadStatus.queued ||
|
||||
item.status == DownloadStatus.downloading ||
|
||||
item.status == DownloadStatus.finalizing,
|
||||
);
|
||||
if (pending.isNotEmpty) return; // still in progress
|
||||
if (pending.isNotEmpty) return;
|
||||
|
||||
// If any item is failed/skipped, the user might retry it later.
|
||||
// Don't finalize album RG with partial data — wait until all album
|
||||
@@ -4556,7 +4555,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
item.status == DownloadStatus.failed ||
|
||||
item.status == DownloadStatus.skipped,
|
||||
);
|
||||
if (retryable.isNotEmpty) return; // still retryable
|
||||
if (retryable.isNotEmpty) return;
|
||||
|
||||
// The accumulator entries represent successfully scanned tracks. Entries
|
||||
// are only added after a successful ReplayGain scan, removed on retry or
|
||||
@@ -4696,7 +4695,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// If any representative item is available, use its track.
|
||||
final representative = albumItems.first;
|
||||
_checkAndWriteAlbumReplayGain(representative.track);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
},
|
||||
);
|
||||
|
||||
// Watch for new homeFeed extension being installed/enabled after init
|
||||
_homeFeedExtSub = ref.listenManual<bool>(
|
||||
extensionProvider.select(
|
||||
(s) => s.extensions.any((e) => e.enabled && e.hasHomeFeed),
|
||||
|
||||
@@ -772,7 +772,7 @@ class _SwingIconState extends State<SwingIcon>
|
||||
duration: const Duration(milliseconds: 600),
|
||||
vsync: this,
|
||||
);
|
||||
// Create a swinging motion (like a pendulum/sign)
|
||||
|
||||
_rotationAnimation = TweenSequence<double>([
|
||||
TweenSequenceItem(tween: Tween(begin: 0.0, end: -0.2), weight: 20),
|
||||
TweenSequenceItem(tween: Tween(begin: -0.2, end: 0.15), weight: 20),
|
||||
|
||||
@@ -531,8 +531,8 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
|
||||
}
|
||||
|
||||
bool _isStepCompleted(int step) {
|
||||
if (step == 0) return true; // Welcome
|
||||
if (step == 1) return true; // Language (always valid)
|
||||
if (step == 0) return true;
|
||||
if (step == 1) return true;
|
||||
|
||||
final logicStep = step - 2;
|
||||
|
||||
|
||||
@@ -834,7 +834,6 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// Fetch ISRC from Deezer track metadata if still missing
|
||||
if (needsIsrc &&
|
||||
(enriched['isrc'] ?? '').trim().isEmpty &&
|
||||
deezerId != null) {
|
||||
|
||||
@@ -2691,7 +2691,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
cleanFilePath,
|
||||
tempOutput,
|
||||
);
|
||||
// Fall back to downloading from URL if extraction failed.
|
||||
if (result['error'] != null &&
|
||||
_coverUrl != null &&
|
||||
_coverUrl!.isNotEmpty) {
|
||||
@@ -2782,13 +2781,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
|
||||
Map<String, dynamic> result;
|
||||
if (_fileExists) {
|
||||
// Prefer extracting cover from the already-downloaded file to avoid
|
||||
// a redundant network request.
|
||||
result = await PlatformBridge.extractCoverToFile(
|
||||
cleanFilePath,
|
||||
outputPath,
|
||||
);
|
||||
// Fall back to downloading from URL if extraction failed.
|
||||
if (result['error'] != null &&
|
||||
_coverUrl != null &&
|
||||
_coverUrl!.isNotEmpty) {
|
||||
@@ -2850,7 +2846,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
}
|
||||
|
||||
if (_isSafFile) {
|
||||
// SAF file: save to temp, then copy to SAF tree
|
||||
final tempDir = await Directory.systemTemp.createTemp('lyrics_');
|
||||
final tempOutput =
|
||||
'${tempDir.path}${Platform.pathSeparator}$baseName.lrc';
|
||||
@@ -4407,7 +4402,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
|
||||
var finalOutputPaths = outputPaths;
|
||||
|
||||
// Embed cover art into split FLAC files using Go backend
|
||||
if (coverPath != null && finalOutputPaths != null) {
|
||||
for (final path in finalOutputPaths) {
|
||||
if (path.toLowerCase().endsWith('.flac')) {
|
||||
|
||||
@@ -1338,7 +1338,6 @@ class FFmpegService {
|
||||
final tempFile = File(tempOutput);
|
||||
if (await tempFile.exists()) {
|
||||
if (returnTempPath) {
|
||||
// Caller will handle SAF write-back and cleanup.
|
||||
onTempReady?.call(tempOutput);
|
||||
return true;
|
||||
}
|
||||
@@ -3051,7 +3050,6 @@ class FFmpegService {
|
||||
case 'COMMENT':
|
||||
id3Map['comment'] = value;
|
||||
break;
|
||||
// ReplayGain as TXXX user-defined frames
|
||||
// FFmpeg writes these as TXXX frames automatically with uppercase keys
|
||||
case 'REPLAYGAINTRACKGAIN':
|
||||
id3Map['REPLAYGAIN_TRACK_GAIN'] = value;
|
||||
|
||||
@@ -2027,8 +2027,6 @@ class PlatformBridge {
|
||||
return const <String, dynamic>{};
|
||||
}
|
||||
|
||||
// MARK: - iOS Security-Scoped Bookmark
|
||||
|
||||
/// Create a security-scoped bookmark from a filesystem path picked by
|
||||
/// FilePicker on iOS. Must be called while the picker session is still active.
|
||||
/// Returns base64-encoded bookmark data, or null on failure.
|
||||
|
||||
@@ -16,11 +16,6 @@ class AppTheme {
|
||||
// cost of the predictive-back preview animation.
|
||||
static const PageTransitionsTheme _pageTransitionsTheme = PageTransitionsTheme(
|
||||
builders: <TargetPlatform, PageTransitionsBuilder>{
|
||||
// Android default is PredictiveBackPageTransitionsBuilder, whose
|
||||
// _PredictiveBackGestureDetector mis-routes the back gesture to a nested
|
||||
// Navigator (flutter#152323). For NON-gesture transitions that builder
|
||||
// already delegates to FadeForwardsPageTransitionsBuilder, so we use it
|
||||
// directly: identical push/pop animation, minus the buggy gesture detector.
|
||||
TargetPlatform.android: FadeForwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
|
||||
|
||||
@@ -542,7 +542,7 @@ class ArtistScreenSkeleton extends StatelessWidget {
|
||||
borderRadius: 4,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// Mimics the small "In Library" badge pill.
|
||||
|
||||
const SkeletonBox(
|
||||
width: 64,
|
||||
height: 14,
|
||||
|
||||
@@ -121,10 +121,6 @@ Future<void> showAppAnnouncementDialog(
|
||||
required RemoteAnnouncement announcement,
|
||||
required VoidCallback onDismiss,
|
||||
}) {
|
||||
// barrierDismissible is false so a stray tap outside the dialog can no longer
|
||||
// close (and silently mark-as-seen) the notice. Dismissal — and the
|
||||
// mark-as-seen side effect in onDismiss — only happens via the explicit close
|
||||
// button or the CTA, both of which call onDismiss themselves.
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
|
||||
@@ -1646,7 +1646,6 @@ class _SpectrogramView extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
// Intensity color legend (matches the spectrogram colormap).
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(40, 0, 10, 8),
|
||||
child: Row(
|
||||
@@ -1731,7 +1730,6 @@ class _SpectrogramPainter extends CustomPainter {
|
||||
);
|
||||
if (plot.width <= 0 || plot.height <= 0) return;
|
||||
|
||||
// Spectrogram image.
|
||||
canvas.drawImageRect(
|
||||
image,
|
||||
Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()),
|
||||
@@ -1760,7 +1758,6 @@ class _SpectrogramPainter extends CustomPainter {
|
||||
}
|
||||
}
|
||||
|
||||
// Time axis (X): 0 at the left, duration at the right.
|
||||
if (durationSec > 0) {
|
||||
final stepSec = _niceStepSec(durationSec);
|
||||
for (double ts = 0; ts <= durationSec + 0.001; ts += stepSec) {
|
||||
|
||||
Reference in New Issue
Block a user