diff --git a/android/app/build.gradle b/android/app/build.gradle index cb71a9c..c27fa28 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,7 +45,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.illusionware.lyricsgrabbr.lyricsgrabbr" minSdkVersion 23 - targetSdkVersion 30 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a394b38..52685e2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ when (call.method) { @@ -36,19 +32,13 @@ class MainActivity: FlutterActivity() { "plugin.openPermissionSettings" -> { result.success(openPermissionSettings()) } - "plugin.startService" -> { - result.success(startService()) - } - "plugin.stopService" -> { - result.success(stopService()) - } else -> result.notImplemented() } } -// val eventChannel = EventChannel(flutterEngine!!.dartExecutor.binaryMessenger, EVENT_CHANNEL_NAME) -// streamHandler = NotificationStreamHandler(context) -// eventChannel.setStreamHandler(streamHandler) + val eventChannel = EventChannel(flutterEngine!!.dartExecutor.binaryMessenger, EVENT_CHANNEL_NAME) + streamHandler = NotificationStreamHandler(context) + eventChannel.setStreamHandler(streamHandler) } private fun isPermissionGranted(): Boolean { @@ -67,21 +57,4 @@ class MainActivity: FlutterActivity() { context.startActivity(Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) return true } - - private fun startService(): Boolean { - listenerIntent?.action = NotificationListener.START_FOREGROUND_SERVICE_ACTION - ContextCompat.startForegroundService(applicationContext, listenerIntent!!) - Log.d("startService", "starting") - - return true - } - - private fun stopService(): Boolean { - listenerIntent?.action = NotificationListener.STOP_FOREGROUND_SERVICE_ACTION - ContextCompat.startForegroundService(applicationContext, listenerIntent!!) - Log.d("stopService", "stopping") - //applicationContext.stopService(listenerIntent!!) - - return true - } } diff --git a/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationListener.kt b/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationListener.kt index d2a2679..e818f02 100644 --- a/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationListener.kt +++ b/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationListener.kt @@ -60,6 +60,11 @@ class NotificationListener : NotificationListenerService() { getCurrentNotifications(applicationContext) } + override fun onListenerDisconnected() { + stopForegroundService() + super.onListenerDisconnected() + } + override fun onNotificationPosted(sbn: StatusBarNotification) { if (sbn.packageName == SPOTIFY_PACKAGE) { sendNotificationBroadcast(sbn, applicationContext) @@ -140,7 +145,7 @@ class NotificationListener : NotificationListenerService() { notificationManager?.notify(NOTIFICATION_ID, notification) - //sendBroadcast(intent) + sendBroadcast(intent) } companion object { diff --git a/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationStreamHandler.kt b/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationStreamHandler.kt index 8b6bf0f..3314675 100644 --- a/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationStreamHandler.kt +++ b/android/app/src/main/kotlin/com/illusionware/lyricsgrabbr/lyricsgrabbr/NotificationStreamHandler.kt @@ -9,20 +9,21 @@ import androidx.core.content.ContextCompat class NotificationStreamHandler(private val context: Context) : StreamHandler { private var eventSink: EventSink? = null private var receiver: NotificationReceiver? = null + private var listenerIntent : Intent? = null // Called whenever the event channel is subscribed to in Flutter override fun onListen(o: Any?, eventSink: EventSink?) { this.eventSink = eventSink - val listenerIntent = Intent(context, NotificationListener::class.java) - listenerIntent.action = NotificationListener.START_FOREGROUND_SERVICE_ACTION + listenerIntent = Intent(context, NotificationListener::class.java) + listenerIntent?.action = NotificationListener.START_FOREGROUND_SERVICE_ACTION - //receiver = NotificationReceiver() - //val intentFilter = IntentFilter() - //intentFilter.addAction(NotificationListener.NOTIFICATION_INTENT) - //context.registerReceiver(receiver, intentFilter) + receiver = NotificationReceiver() + val intentFilter = IntentFilter() + intentFilter.addAction(NotificationListener.NOTIFICATION_INTENT) + context.registerReceiver(receiver, intentFilter) - ContextCompat.startForegroundService(context, listenerIntent) + ContextCompat.startForegroundService(context, listenerIntent!!) } // Called whenever the event channel subscription is cancelled in Flutter @@ -30,11 +31,11 @@ class NotificationStreamHandler(private val context: Context) : StreamHandler { eventSink = null Log.d("onCancel", "subscription cancelled, stopping service") - val listenerIntent = Intent(context, NotificationListener::class.java) - listenerIntent.action = NotificationListener.STOP_FOREGROUND_SERVICE_ACTION +// val listenerIntent = Intent(context, NotificationListener::class.java) + listenerIntent?.action = NotificationListener.STOP_FOREGROUND_SERVICE_ACTION - ContextCompat.startForegroundService(context, listenerIntent) - //context.unregisterReceiver(receiver) + ContextCompat.startForegroundService(context, listenerIntent!!) + context.unregisterReceiver(receiver) } internal inner class NotificationReceiver : BroadcastReceiver() { diff --git a/lib/main.dart b/lib/main.dart index 5fd366e..7fc01ce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -35,7 +35,7 @@ class _HomePageState extends State with WidgetsBindingObserver { late AndroidNotificationListener _notifications; late StreamSubscription _subscription; BuildContext? permissionDialogCtx; - final List _notifs = []; + NotificationEvent? currentlyPlaying; bool running = false; @override @@ -67,7 +67,7 @@ class _HomePageState extends State with WidgetsBindingObserver { } } - void showNotificationPermDialog() { + void _showNotificationPermDialog() { showDialog( context: context, barrierDismissible: false, @@ -107,7 +107,7 @@ class _HomePageState extends State with WidgetsBindingObserver { if (perm != null && perm) { startListening(); } else { - showNotificationPermDialog(); + _showNotificationPermDialog(); } } @@ -116,7 +116,7 @@ class _HomePageState extends State with WidgetsBindingObserver { // if the app is in the foreground, update the notif, and // fetch the new lyrics and display them. setState(() { - _notifs.add(event); + currentlyPlaying = event; }); } @@ -125,17 +125,15 @@ class _HomePageState extends State with WidgetsBindingObserver { // that keeps track of which song is playing // and if pressed will fetch the lyrics and display them. - //_notifications = AndroidNotificationListener(); + _notifications = AndroidNotificationListener(); - //_subscription = _notifications.notificationStream.listen(onData); - AndroidNotificationListener.startService(); + _subscription = _notifications.notificationStream.listen(onData); setState(() => running = true); } void stopListening() { setState(() => running = false); - AndroidNotificationListener.stopService(); - //_subscription.cancel(); + _subscription.cancel(); } @override @@ -144,19 +142,49 @@ class _HomePageState extends State with WidgetsBindingObserver { appBar: AppBar( title: Text(widget.title), ), - body: ListView.builder( - itemCount: _notifs.length, - reverse: true, - itemBuilder: (BuildContext context, int idx) { - final entry = _notifs[idx]; - return ListTile( - leading: Text(entry.songTitle), - trailing: Text(entry.artist), - ); - }), + body: Container( + padding: const EdgeInsets.all(20), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + running ? RichText( + text: TextSpan( + text: currentlyPlaying?.songTitle, + style: const TextStyle( + fontSize: 24.0, + color: Colors.black, + fontWeight: FontWeight.w700, + ), + children: [ + const TextSpan( + text: "\n", + ), + TextSpan( + text: currentlyPlaying?.artist, + style: const TextStyle( + fontSize: 20.0, + color: Color(0xFF303030), + ) + ) + ] + ), + textAlign: TextAlign.center, + ) : + const Text("Listener stopped"), + running ? Text( + currentlyPlaying?.playbackState == PlaybackState.STATE_PLAYING ? "Spotify Playing" : "Spotify Paused", + style: Theme.of(context).textTheme.headline6, + ) : + const Text("") + ], + ), + ), + ), floatingActionButton: FloatingActionButton( onPressed: running ? stopListening : startListening, - tooltip: running ? 'Stop service' : "Start service", + tooltip: running ? 'Stop listener' : "Start listener", child: running ? const Icon(Icons.stop) : const Icon(Icons.play_arrow), ), ); diff --git a/lib/notification_listener.dart b/lib/notification_listener.dart index 6948269..93642d9 100644 --- a/lib/notification_listener.dart +++ b/lib/notification_listener.dart @@ -85,12 +85,4 @@ class AndroidNotificationListener { static Future openPermissionSettings() async { return await _methodChannel.invokeMethod('plugin.openPermissionSettings'); } - - static Future startService() async { - return await _methodChannel.invokeMethod('plugin.startService'); - } - - static Future stopService() async { - return await _methodChannel.invokeMethod('plugin.stopService'); - } }