From 904b45e8f6c7be6ed3b53549e5cba8b4a8ad5b98 Mon Sep 17 00:00:00 2001 From: zarzet Date: Fri, 8 May 2026 21:37:56 +0700 Subject: [PATCH] chore: housekeeping cleanup and code deduplication - Remove stray tracked files (root AndroidManifest.xml, build.gradle.bak, temp_project template) - Move README-only images out of app asset bundle to reduce APK/IPA size (~1.68MB) - Fix logo filename typo (transparant -> transparent) - Deduplicate _readPositiveInt into shared int_utils.dart - Deduplicate _themeModeFromString (reuse from theme_settings.dart) - Remove deprecated LocalLibraryState.items getter - Remove unused sqflite_common_ffi dependency - Update apps.json version to 4.5.1 - Fix Flutter version in CONTRIBUTING.md (3.38.1 -> 3.41.5) - Improve .gitignore patterns (NUL, *.bak, root AndroidManifest.xml) --- .gitignore | 5 +- AndroidManifest.xml | Bin 16912 -> 0 bytes CONTRIBUTING.md | 2 +- README.md | 14 ++-- android/app/build.gradle.bak | 71 ------------------ .../com/example/temp_project/MainActivity.kt | 5 -- apps.json | 4 +- ...o-transparant.png => logo-transparent.png} | Bin assets/{images => readme}/1.jpg | Bin assets/{images => readme}/2.jpg | Bin assets/{images => readme}/3.jpg | Bin assets/{images => readme}/4.jpg | Bin .../{images => readme}/banner-readme-dark.png | Bin .../banner-readme-light.png | Bin lib/models/theme_settings.dart | 4 +- lib/providers/download_queue_provider.dart | 39 +++------- lib/providers/local_library_provider.dart | 5 -- lib/providers/theme_provider.dart | 10 +-- lib/screens/home_tab.dart | 2 +- lib/screens/settings/about_page.dart | 2 +- lib/screens/setup_screen.dart | 2 +- lib/screens/track_metadata_screen.dart | 38 ++++------ lib/utils/int_utils.dart | 13 ++++ pubspec.yaml | 1 - 24 files changed, 56 insertions(+), 161 deletions(-) delete mode 100644 AndroidManifest.xml delete mode 100644 android/app/build.gradle.bak delete mode 100644 android/app/src/main/kotlin/com/example/temp_project/MainActivity.kt rename assets/images/{logo-transparant.png => logo-transparent.png} (100%) rename assets/{images => readme}/1.jpg (100%) rename assets/{images => readme}/2.jpg (100%) rename assets/{images => readme}/3.jpg (100%) rename assets/{images => readme}/4.jpg (100%) rename assets/{images => readme}/banner-readme-dark.png (100%) rename assets/{images => readme}/banner-readme-light.png (100%) create mode 100644 lib/utils/int_utils.dart diff --git a/.gitignore b/.gitignore index 9150a859..b81c3a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ go_backend/*.xcframework/ # Android android/.gradle/ android/app/libs/gobackend.aar +android/app/libs/gobackend-sources.jar android/local.properties android/*.iml android/key.properties @@ -57,7 +58,6 @@ ios/Pods/ ios/.symlinks/ ios/Flutter/Flutter.framework/ ios/Flutter/Flutter.podspec -android/app/libs/gobackend-sources.jar # Extension folder extension/ @@ -67,7 +67,10 @@ AGENTS.md # Temp/misc nul +NUL network_requests.txt +*.bak +/AndroidManifest.xml # Log files *.log diff --git a/AndroidManifest.xml b/AndroidManifest.xml deleted file mode 100644 index e8ef04455a7ef64a3f0ebbcaff7b43990851d265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16912 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 i - - - SpotiFLAC Mobile + + + SpotiFLAC Mobile

@@ -28,10 +28,10 @@ ## Screenshots

- - - - + + + +

--- diff --git a/android/app/build.gradle.bak b/android/app/build.gradle.bak deleted file mode 100644 index f5ed5e6b..00000000 --- a/android/app/build.gradle.bak +++ /dev/null @@ -1,71 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "com.zarz.spotiflac" - compileSdk flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - applicationId "com.zarz.spotiflac" - minSdkVersion flutter.minSdkVersion - targetSdk flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - signingConfig signingConfigs.debug - minifyEnabled false - shrinkResources false - } - } -} - -flutter { - source '../..' -} - -dependencies { - // Go backend library (gomobile generated) - implementation fileTree(dir: 'libs', include: ['*.aar']) - - // Kotlin coroutines for async Go backend calls - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' -} diff --git a/android/app/src/main/kotlin/com/example/temp_project/MainActivity.kt b/android/app/src/main/kotlin/com/example/temp_project/MainActivity.kt deleted file mode 100644 index 3aebb9bc..00000000 --- a/android/app/src/main/kotlin/com/example/temp_project/MainActivity.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.temp_project - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity : FlutterActivity() diff --git a/apps.json b/apps.json index a6cc8c4c..4bd95482 100644 --- a/apps.json +++ b/apps.json @@ -7,9 +7,9 @@ "name": "SpotiFLAC Mobile", "bundleIdentifier": "com.zarzet.spotiflac", "developerName": "zarzet", - "version": "4.5.0", + "version": "4.5.1", "versionDate": "2026-05-06", - "downloadURL": "https://github.com/zarzet/SpotiFLAC-Mobile/releases/download/v4.5.0/SpotiFLAC-v4.5.0-ios-unsigned.ipa", + "downloadURL": "https://github.com/zarzet/SpotiFLAC-Mobile/releases/download/v4.5.1/SpotiFLAC-v4.5.1-ios-unsigned.ipa", "localizedDescription": "SpotiFLAC Mobile is written in Flutter. Download tracks in true FLAC from Tidal, Qobuz, & Amazon Music.", "iconURL": "https://raw.githubusercontent.com/zarzet/SpotiFLAC-Mobile/main/assets/images/logo.png", "size": 37191956 diff --git a/assets/images/logo-transparant.png b/assets/images/logo-transparent.png similarity index 100% rename from assets/images/logo-transparant.png rename to assets/images/logo-transparent.png diff --git a/assets/images/1.jpg b/assets/readme/1.jpg similarity index 100% rename from assets/images/1.jpg rename to assets/readme/1.jpg diff --git a/assets/images/2.jpg b/assets/readme/2.jpg similarity index 100% rename from assets/images/2.jpg rename to assets/readme/2.jpg diff --git a/assets/images/3.jpg b/assets/readme/3.jpg similarity index 100% rename from assets/images/3.jpg rename to assets/readme/3.jpg diff --git a/assets/images/4.jpg b/assets/readme/4.jpg similarity index 100% rename from assets/images/4.jpg rename to assets/readme/4.jpg diff --git a/assets/images/banner-readme-dark.png b/assets/readme/banner-readme-dark.png similarity index 100% rename from assets/images/banner-readme-dark.png rename to assets/readme/banner-readme-dark.png diff --git a/assets/images/banner-readme-light.png b/assets/readme/banner-readme-light.png similarity index 100% rename from assets/images/banner-readme-light.png rename to assets/readme/banner-readme-light.png diff --git a/lib/models/theme_settings.dart b/lib/models/theme_settings.dart index 5f331d1e..2163cf8f 100644 --- a/lib/models/theme_settings.dart +++ b/lib/models/theme_settings.dart @@ -46,7 +46,7 @@ class ThemeSettings { factory ThemeSettings.fromJson(Map json) { return ThemeSettings( - themeMode: _themeModeFromString(json[kThemeModeKey] as String?), + themeMode: themeModeFromString(json[kThemeModeKey] as String?), useDynamicColor: json[kUseDynamicColorKey] as bool? ?? true, seedColorValue: json[kSeedColorKey] as int? ?? kDefaultSeedColor, useAmoled: json[kUseAmoledKey] as bool? ?? false, @@ -68,7 +68,7 @@ class ThemeSettings { themeMode.hashCode ^ useDynamicColor.hashCode ^ seedColorValue.hashCode ^ useAmoled.hashCode; } -ThemeMode _themeModeFromString(String? value) { +ThemeMode themeModeFromString(String? value) { if (value == null) return ThemeMode.system; return ThemeMode.values.firstWhere( (e) => e.name == value, diff --git a/lib/providers/download_queue_provider.dart b/lib/providers/download_queue_provider.dart index 21e0db5e..3990951f 100644 --- a/lib/providers/download_queue_provider.dart +++ b/lib/providers/download_queue_provider.dart @@ -21,6 +21,7 @@ import 'package:spotiflac_android/utils/logger.dart' hide log; import 'package:spotiflac_android/utils/file_access.dart'; import 'package:spotiflac_android/utils/string_utils.dart'; import 'package:spotiflac_android/utils/artist_utils.dart'; +import 'package:spotiflac_android/utils/int_utils.dart'; export 'package:spotiflac_android/services/history_database.dart' show HistoryLookupRequest, HistoryBatchLookupRequest; @@ -34,19 +35,8 @@ final _trimUnderscoresAndSpacesRegex = RegExp(r'^[_ ]+|[_ ]+$'); final _multiWhitespaceRegex = RegExp(r'\s+'); final _multiUnderscoreRegex = RegExp(r'_+'); -int? _readPositiveIntValue(dynamic value) { - if (value == null) return null; - if (value is num) { - final asInt = value.toInt(); - return asInt > 0 ? asInt : null; - } - final parsed = int.tryParse(value.toString()); - if (parsed == null || parsed <= 0) return null; - return parsed; -} - int? _readPositiveBitrateKbps(dynamic value) { - final parsed = _readPositiveIntValue(value); + final parsed = readPositiveInt(value); if (parsed == null) return null; return parsed >= 10000 ? (parsed / 1000).round() : parsed; } @@ -666,17 +656,6 @@ class DownloadHistoryNotifier extends Notifier { } } - int? _readPositiveInt(dynamic value) { - if (value == null) return null; - if (value is num) { - final asInt = value.toInt(); - return asInt > 0 ? asInt : null; - } - final parsed = int.tryParse(value.toString()); - if (parsed == null || parsed <= 0) return null; - return parsed; - } - bool _supportsAudioMetadataProbe(String filePath) { final trimmed = filePath.trim().toLowerCase(); if (trimmed.isEmpty) return false; @@ -757,8 +736,8 @@ class DownloadHistoryNotifier extends Notifier { return null; } - final bitDepth = _readPositiveInt(result['bit_depth']); - final sampleRate = _readPositiveInt(result['sample_rate']); + final bitDepth = readPositiveInt(result['bit_depth']); + final sampleRate = readPositiveInt(result['sample_rate']); final bitrateKbps = _readPositiveBitrateKbps(result['bitrate']); final quality = _resolveDisplayQuality( filePath: filePath, @@ -768,11 +747,11 @@ class DownloadHistoryNotifier extends Notifier { storedQuality: fallbackQuality, ); final composer = normalizeOptionalString(result['composer']?.toString()); - final duration = _readPositiveInt(result['duration']); - final trackNumber = _readPositiveInt(result['track_number']); - final totalTracks = _readPositiveInt(result['total_tracks']); - final discNumber = _readPositiveInt(result['disc_number']); - final totalDiscs = _readPositiveInt(result['total_discs']); + final duration = readPositiveInt(result['duration']); + final trackNumber = readPositiveInt(result['track_number']); + final totalTracks = readPositiveInt(result['total_tracks']); + final discNumber = readPositiveInt(result['disc_number']); + final totalDiscs = readPositiveInt(result['total_discs']); if (quality == null && bitDepth == null && diff --git a/lib/providers/local_library_provider.dart b/lib/providers/local_library_provider.dart index 64b3ded3..8086716b 100644 --- a/lib/providers/local_library_provider.dart +++ b/lib/providers/local_library_provider.dart @@ -54,11 +54,6 @@ class LocalLibraryState { _isrcSet = isrcSet ?? const {}, _filePathById = filePathById ?? const {}; - @Deprecated( - 'LocalLibraryState no longer owns full track rows. Use DB-backed page providers.', - ) - List get items => const []; - bool hasIsrc(String isrc) => _isrcSet.contains(isrc); bool hasTrack(String trackName, String artistName) { diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme_provider.dart index 0ed2c9e0..6421f348 100644 --- a/lib/providers/theme_provider.dart +++ b/lib/providers/theme_provider.dart @@ -25,7 +25,7 @@ class ThemeNotifier extends Notifier { final useAmoled = prefs.getBool(kUseAmoledKey); state = ThemeSettings( - themeMode: _themeModeFromString(modeString), + themeMode: themeModeFromString(modeString), useDynamicColor: useDynamic ?? true, seedColorValue: seedColor ?? kDefaultSeedColor, useAmoled: useAmoled ?? false, @@ -71,12 +71,4 @@ class ThemeNotifier extends Notifier { state = state.copyWith(useAmoled: value); await _saveToStorage(); } - - ThemeMode _themeModeFromString(String? value) { - if (value == null) return ThemeMode.system; - return ThemeMode.values.firstWhere( - (e) => e.name == value, - orElse: () => ThemeMode.system, - ); - } } diff --git a/lib/screens/home_tab.dart b/lib/screens/home_tab.dart index 95ac0669..d0a0faf3 100644 --- a/lib/screens/home_tab.dart +++ b/lib/screens/home_tab.dart @@ -1549,7 +1549,7 @@ class _HomeTabState extends ConsumerState shape: BoxShape.circle, ), child: Image.asset( - 'assets/images/logo-transparant.png', + 'assets/images/logo-transparent.png', color: colorScheme.onPrimary, fit: BoxFit.contain, errorBuilder: (_, _, _) => ClipRRect( diff --git a/lib/screens/settings/about_page.dart b/lib/screens/settings/about_page.dart index 73d73edb..74e6244e 100644 --- a/lib/screens/settings/about_page.dart +++ b/lib/screens/settings/about_page.dart @@ -302,7 +302,7 @@ class _AppHeaderCard extends StatelessWidget { shape: BoxShape.circle, ), child: Image.asset( - 'assets/images/logo-transparant.png', + 'assets/images/logo-transparent.png', color: colorScheme.onPrimary, fit: BoxFit.contain, errorBuilder: (_, _, _) => ClipRRect( diff --git a/lib/screens/setup_screen.dart b/lib/screens/setup_screen.dart index f15b5896..eb7eda84 100644 --- a/lib/screens/setup_screen.dart +++ b/lib/screens/setup_screen.dart @@ -681,7 +681,7 @@ class _SetupScreenState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( - 'assets/images/logo-transparant.png', + 'assets/images/logo-transparent.png', width: logoSize, height: logoSize, color: colorScheme.primary, diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index 0d65a524..54378da5 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -22,6 +22,7 @@ import 'package:spotiflac_android/utils/lyrics_metadata_helper.dart'; import 'package:spotiflac_android/utils/mime_utils.dart'; import 'package:spotiflac_android/utils/image_cache_utils.dart'; import 'package:spotiflac_android/utils/string_utils.dart'; +import 'package:spotiflac_android/utils/int_utils.dart'; import 'package:spotiflac_android/widgets/audio_analysis_widget.dart'; import 'package:spotiflac_android/widgets/cached_cover_image.dart'; @@ -365,9 +366,9 @@ class _TrackMetadataScreenState extends ConsumerState { return; } - final resolvedBitDepth = _readPositiveInt(metadata['bit_depth']); - final resolvedSampleRate = _readPositiveInt(metadata['sample_rate']); - final resolvedDuration = _readPositiveInt(metadata['duration']); + final resolvedBitDepth = readPositiveInt(metadata['bit_depth']); + final resolvedSampleRate = readPositiveInt(metadata['sample_rate']); + final resolvedDuration = readPositiveInt(metadata['duration']); final resolvedAlbum = metadata['album']?.toString(); final resolvedQuality = buildDisplayAudioQuality( bitDepth: resolvedBitDepth ?? bitDepth, @@ -386,10 +387,10 @@ class _TrackMetadataScreenState extends ConsumerState { // Resolve label/copyright from file when the model doesn't carry them // (e.g. local library items, or download history items without these fields). - final resolvedTrackNumber = _readPositiveInt(metadata['track_number']); - final resolvedTotalTracks = _readPositiveInt(metadata['total_tracks']); - final resolvedDiscNumber = _readPositiveInt(metadata['disc_number']); - final resolvedTotalDiscs = _readPositiveInt(metadata['total_discs']); + final resolvedTrackNumber = readPositiveInt(metadata['track_number']); + final resolvedTotalTracks = readPositiveInt(metadata['total_tracks']); + final resolvedDiscNumber = readPositiveInt(metadata['disc_number']); + final resolvedTotalDiscs = readPositiveInt(metadata['total_discs']); final resolvedComposer = metadata['composer']?.toString(); final resolvedLabel = metadata['label']?.toString(); final resolvedCopyright = metadata['copyright']?.toString(); @@ -614,7 +615,7 @@ class _TrackMetadataScreenState extends ConsumerState { } int? get totalTracks => - _readPositiveInt(_editedMetadata?['total_tracks']) ?? + readPositiveInt(_editedMetadata?['total_tracks']) ?? (_isLocalItem ? _localLibraryItem!.totalTracks : _downloadItem!.totalTracks); @@ -631,7 +632,7 @@ class _TrackMetadataScreenState extends ConsumerState { } int? get totalDiscs => - _readPositiveInt(_editedMetadata?['total_discs']) ?? + readPositiveInt(_editedMetadata?['total_discs']) ?? (_isLocalItem ? _localLibraryItem!.totalDiscs : _downloadItem!.totalDiscs); @@ -670,13 +671,13 @@ class _TrackMetadataScreenState extends ConsumerState { _editedMetadata?['composer']?.toString() ?? (_isLocalItem ? _localLibraryItem!.composer : null); int? get duration => - _readPositiveInt(_editedMetadata?['duration']) ?? + readPositiveInt(_editedMetadata?['duration']) ?? (_isLocalItem ? _localLibraryItem!.duration : _downloadItem!.duration); int? get bitDepth => - _readPositiveInt(_editedMetadata?['bit_depth']) ?? + readPositiveInt(_editedMetadata?['bit_depth']) ?? (_isLocalItem ? _localLibraryItem!.bitDepth : _downloadItem!.bitDepth); int? get sampleRate => - _readPositiveInt(_editedMetadata?['sample_rate']) ?? + readPositiveInt(_editedMetadata?['sample_rate']) ?? (_isLocalItem ? _localLibraryItem!.sampleRate : _downloadItem!.sampleRate); @@ -706,17 +707,6 @@ class _TrackMetadataScreenState extends ConsumerState { String? get _quality => _isLocalItem ? null : _downloadItem!.quality; - int? _readPositiveInt(dynamic value) { - if (value == null) return null; - if (value is num) { - final asInt = value.toInt(); - return asInt > 0 ? asInt : null; - } - final parsed = int.tryParse(value.toString()); - if (parsed == null || parsed <= 0) return null; - return parsed; - } - String _displayServiceTrackId(String value) { final raw = value.trim(); if (raw.isEmpty) return raw; @@ -4394,7 +4384,7 @@ class _TrackMetadataScreenState extends ConsumerState { }; final initialDurationSeconds = - _readPositiveInt(fileMetadata?['duration']) ?? duration ?? 0; + readPositiveInt(fileMetadata?['duration']) ?? duration ?? 0; if (!context.mounted) return; diff --git a/lib/utils/int_utils.dart b/lib/utils/int_utils.dart new file mode 100644 index 00000000..52022da3 --- /dev/null +++ b/lib/utils/int_utils.dart @@ -0,0 +1,13 @@ +/// Parses a dynamic value to a positive integer (> 0), or returns null. +/// +/// Accepts [num] and parseable [String] values. +int? readPositiveInt(dynamic value) { + if (value == null) return null; + if (value is num) { + final asInt = value.toInt(); + return asInt > 0 ? asInt : null; + } + final parsed = int.tryParse(value.toString()); + if (parsed == null || parsed <= 0) return null; + return parsed; +} diff --git a/pubspec.yaml b/pubspec.yaml index d14fe516..9b4c7fa2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,7 +27,6 @@ dependencies: path_provider: ^2.1.5 path: ^1.9.0 sqflite: ^2.4.2+1 - sqflite_common_ffi: ^2.4.0+3 # HTTP & Network http: ^1.6.0