Initial commit
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# OpenVinyl — Flutter (Android)
|
||||
|
||||
A free clone of the OpenVinyl turntable visualizer for Android.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
main.dart ← App entry, permission check
|
||||
models/
|
||||
media_info.dart ← Data class for now-playing track
|
||||
services/
|
||||
media_service.dart ← Platform channel bridge (Flutter side)
|
||||
screens/
|
||||
player_screen.dart ← Main turntable screen
|
||||
permission_screen.dart ← Shown if Notification Access not granted
|
||||
widgets/
|
||||
vinyl_record.dart ← Spinning record with album art + grooves
|
||||
tonearm.dart ← Animated needle arm
|
||||
|
||||
android/app/src/main/
|
||||
kotlin/pt/ruifpb/openvinyl/
|
||||
MainActivity.kt ← Platform channel setup (MethodChannel + EventChannel)
|
||||
MediaListenerService.kt ← NotificationListenerService reads now-playing data
|
||||
res/xml/
|
||||
notification_listener_service.xml
|
||||
AndroidManifest.xml ← Service declaration with BIND_NOTIFICATION_LISTENER
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Package name
|
||||
By default the package is `pt.ruifpb.openvinyl`. If you want to change it:
|
||||
- Update the `package` line in both `.kt` files
|
||||
- Update `applicationId` in `android/app/build.gradle`
|
||||
- Update the directory path under `kotlin/`
|
||||
|
||||
### 2. Install dependencies
|
||||
```bash
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### 3. Run
|
||||
```bash
|
||||
flutter run
|
||||
```
|
||||
|
||||
On first launch the app will show a permission screen. Tap **Open Settings**,
|
||||
find **OpenVinyl** in the Notification Listener list, and enable it. Return to
|
||||
the app — it will detect the change automatically.
|
||||
|
||||
---
|
||||
|
||||
## Adding Home Screen Widgets (later)
|
||||
|
||||
The code is pre-wired for this. When you're ready:
|
||||
|
||||
1. **Uncomment** the `home_widget` import and `_pushToWidget()` call in
|
||||
`lib/services/media_service.dart`
|
||||
2. Create a Glance widget in Kotlin:
|
||||
`android/app/src/main/kotlin/pt/ruifpb/openvinyl/VinylWidgetReceiver.kt`
|
||||
3. Add the `<receiver>` block in `AndroidManifest.xml` (stub already in the file)
|
||||
4. Add widget info XML at `android/app/src/main/res/xml/vinyl_widget_info.xml`
|
||||
|
||||
The `home_widget` package (already in `pubspec.yaml`) handles the Flutter ↔
|
||||
widget data bridge via `HomeWidget.saveWidgetData()` and
|
||||
`HomeWidget.updateWidget()`.
|
||||
|
||||
---
|
||||
|
||||
## How the Notification Listener Works
|
||||
|
||||
`MediaListenerService` extends Android's `NotificationListenerService`.
|
||||
When any app posts a notification that carries a `MediaSession.Token` extra
|
||||
(Spotify, YouTube Music, Podcast apps, etc.), the service:
|
||||
|
||||
1. Creates a `MediaController` from the token
|
||||
2. Reads metadata (title, artist, album art bitmap) and playback state
|
||||
3. Serialises the bitmap to JPEG bytes
|
||||
4. Pushes a `Map` through the `EventChannel` to Flutter
|
||||
|
||||
Flutter's `MediaService` receives the map, deserialises it into a `MediaInfo`
|
||||
object, and re-broadcasts it to any listener (currently `PlayerScreen`).
|
||||
Reference in New Issue
Block a user