mirror of
https://github.com/IllusionMan1212/lyrics-grabbr.git
synced 2026-10-24 02:12:27 +00:00
feat: finish foreground service work
This commit is contained in:
@@ -45,7 +45,7 @@ android {
|
|||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.illusionware.lyricsgrabbr.lyricsgrabbr"
|
applicationId "com.illusionware.lyricsgrabbr.lyricsgrabbr"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 30
|
targetSdkVersion 31
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Lyrics Grabbr"
|
android:label="Lyrics Grabbr"
|
||||||
android:name=".App"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.illusionware.lyricsgrabbr.lyricsgrabbr
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.os.StrictMode
|
|
||||||
|
|
||||||
class App : Application() {
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
StrictMode.setThreadPolicy(
|
|
||||||
StrictMode.ThreadPolicy.Builder()
|
|
||||||
.detectAll() // or .detectAll() for all detectable problems
|
|
||||||
.penaltyLog()
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
StrictMode.setVmPolicy(
|
|
||||||
StrictMode.VmPolicy.Builder()
|
|
||||||
.detectAll()
|
|
||||||
.penaltyLog()
|
|
||||||
.penaltyDeath()
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-31
@@ -7,14 +7,12 @@ import android.util.Log
|
|||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
|
||||||
import io.flutter.plugin.common.EventChannel
|
import io.flutter.plugin.common.EventChannel
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity() {
|
||||||
// private var streamHandler : NotificationStreamHandler? = null
|
private var streamHandler : NotificationStreamHandler? = null
|
||||||
private var listenerIntent : Intent? = null
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"
|
private const val ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"
|
||||||
@@ -25,8 +23,6 @@ class MainActivity: FlutterActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
listenerIntent = Intent(applicationContext, NotificationListener::class.java)
|
|
||||||
|
|
||||||
val methodChannel = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, METHOD_CHANNEL_NAME)
|
val methodChannel = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, METHOD_CHANNEL_NAME)
|
||||||
methodChannel.setMethodCallHandler { call, result ->
|
methodChannel.setMethodCallHandler { call, result ->
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
@@ -36,19 +32,13 @@ class MainActivity: FlutterActivity() {
|
|||||||
"plugin.openPermissionSettings" -> {
|
"plugin.openPermissionSettings" -> {
|
||||||
result.success(openPermissionSettings())
|
result.success(openPermissionSettings())
|
||||||
}
|
}
|
||||||
"plugin.startService" -> {
|
|
||||||
result.success(startService())
|
|
||||||
}
|
|
||||||
"plugin.stopService" -> {
|
|
||||||
result.success(stopService())
|
|
||||||
}
|
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// val eventChannel = EventChannel(flutterEngine!!.dartExecutor.binaryMessenger, EVENT_CHANNEL_NAME)
|
val eventChannel = EventChannel(flutterEngine!!.dartExecutor.binaryMessenger, EVENT_CHANNEL_NAME)
|
||||||
// streamHandler = NotificationStreamHandler(context)
|
streamHandler = NotificationStreamHandler(context)
|
||||||
// eventChannel.setStreamHandler(streamHandler)
|
eventChannel.setStreamHandler(streamHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isPermissionGranted(): Boolean {
|
private fun isPermissionGranted(): Boolean {
|
||||||
@@ -67,21 +57,4 @@ class MainActivity: FlutterActivity() {
|
|||||||
context.startActivity(Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
context.startActivity(Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||||
return true
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -60,6 +60,11 @@ class NotificationListener : NotificationListenerService() {
|
|||||||
getCurrentNotifications(applicationContext)
|
getCurrentNotifications(applicationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onListenerDisconnected() {
|
||||||
|
stopForegroundService()
|
||||||
|
super.onListenerDisconnected()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
override fun onNotificationPosted(sbn: StatusBarNotification) {
|
||||||
if (sbn.packageName == SPOTIFY_PACKAGE) {
|
if (sbn.packageName == SPOTIFY_PACKAGE) {
|
||||||
sendNotificationBroadcast(sbn, applicationContext)
|
sendNotificationBroadcast(sbn, applicationContext)
|
||||||
@@ -140,7 +145,7 @@ class NotificationListener : NotificationListenerService() {
|
|||||||
|
|
||||||
notificationManager?.notify(NOTIFICATION_ID, notification)
|
notificationManager?.notify(NOTIFICATION_ID, notification)
|
||||||
|
|
||||||
//sendBroadcast(intent)
|
sendBroadcast(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
+12
-11
@@ -9,20 +9,21 @@ import androidx.core.content.ContextCompat
|
|||||||
class NotificationStreamHandler(private val context: Context) : StreamHandler {
|
class NotificationStreamHandler(private val context: Context) : StreamHandler {
|
||||||
private var eventSink: EventSink? = null
|
private var eventSink: EventSink? = null
|
||||||
private var receiver: NotificationReceiver? = null
|
private var receiver: NotificationReceiver? = null
|
||||||
|
private var listenerIntent : Intent? = null
|
||||||
|
|
||||||
// Called whenever the event channel is subscribed to in Flutter
|
// Called whenever the event channel is subscribed to in Flutter
|
||||||
override fun onListen(o: Any?, eventSink: EventSink?) {
|
override fun onListen(o: Any?, eventSink: EventSink?) {
|
||||||
this.eventSink = eventSink
|
this.eventSink = eventSink
|
||||||
|
|
||||||
val listenerIntent = Intent(context, NotificationListener::class.java)
|
listenerIntent = Intent(context, NotificationListener::class.java)
|
||||||
listenerIntent.action = NotificationListener.START_FOREGROUND_SERVICE_ACTION
|
listenerIntent?.action = NotificationListener.START_FOREGROUND_SERVICE_ACTION
|
||||||
|
|
||||||
//receiver = NotificationReceiver()
|
receiver = NotificationReceiver()
|
||||||
//val intentFilter = IntentFilter()
|
val intentFilter = IntentFilter()
|
||||||
//intentFilter.addAction(NotificationListener.NOTIFICATION_INTENT)
|
intentFilter.addAction(NotificationListener.NOTIFICATION_INTENT)
|
||||||
//context.registerReceiver(receiver, intentFilter)
|
context.registerReceiver(receiver, intentFilter)
|
||||||
|
|
||||||
ContextCompat.startForegroundService(context, listenerIntent)
|
ContextCompat.startForegroundService(context, listenerIntent!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called whenever the event channel subscription is cancelled in Flutter
|
// Called whenever the event channel subscription is cancelled in Flutter
|
||||||
@@ -30,11 +31,11 @@ class NotificationStreamHandler(private val context: Context) : StreamHandler {
|
|||||||
eventSink = null
|
eventSink = null
|
||||||
Log.d("onCancel", "subscription cancelled, stopping service")
|
Log.d("onCancel", "subscription cancelled, stopping service")
|
||||||
|
|
||||||
val listenerIntent = Intent(context, NotificationListener::class.java)
|
// val listenerIntent = Intent(context, NotificationListener::class.java)
|
||||||
listenerIntent.action = NotificationListener.STOP_FOREGROUND_SERVICE_ACTION
|
listenerIntent?.action = NotificationListener.STOP_FOREGROUND_SERVICE_ACTION
|
||||||
|
|
||||||
ContextCompat.startForegroundService(context, listenerIntent)
|
ContextCompat.startForegroundService(context, listenerIntent!!)
|
||||||
//context.unregisterReceiver(receiver)
|
context.unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inner class NotificationReceiver : BroadcastReceiver() {
|
internal inner class NotificationReceiver : BroadcastReceiver() {
|
||||||
|
|||||||
+48
-20
@@ -35,7 +35,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
late AndroidNotificationListener _notifications;
|
late AndroidNotificationListener _notifications;
|
||||||
late StreamSubscription<NotificationEvent> _subscription;
|
late StreamSubscription<NotificationEvent> _subscription;
|
||||||
BuildContext? permissionDialogCtx;
|
BuildContext? permissionDialogCtx;
|
||||||
final List<NotificationEvent> _notifs = [];
|
NotificationEvent? currentlyPlaying;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -67,7 +67,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showNotificationPermDialog() {
|
void _showNotificationPermDialog() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
@@ -107,7 +107,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
if (perm != null && perm) {
|
if (perm != null && perm) {
|
||||||
startListening();
|
startListening();
|
||||||
} else {
|
} else {
|
||||||
showNotificationPermDialog();
|
_showNotificationPermDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
// if the app is in the foreground, update the notif, and
|
// if the app is in the foreground, update the notif, and
|
||||||
// fetch the new lyrics and display them.
|
// fetch the new lyrics and display them.
|
||||||
setState(() {
|
setState(() {
|
||||||
_notifs.add(event);
|
currentlyPlaying = event;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,17 +125,15 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
// that keeps track of which song is playing
|
// that keeps track of which song is playing
|
||||||
// and if pressed will fetch the lyrics and display them.
|
// and if pressed will fetch the lyrics and display them.
|
||||||
|
|
||||||
//_notifications = AndroidNotificationListener();
|
_notifications = AndroidNotificationListener();
|
||||||
|
|
||||||
//_subscription = _notifications.notificationStream.listen(onData);
|
_subscription = _notifications.notificationStream.listen(onData);
|
||||||
AndroidNotificationListener.startService();
|
|
||||||
setState(() => running = true);
|
setState(() => running = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopListening() {
|
void stopListening() {
|
||||||
setState(() => running = false);
|
setState(() => running = false);
|
||||||
AndroidNotificationListener.stopService();
|
_subscription.cancel();
|
||||||
//_subscription.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -144,19 +142,49 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.title),
|
title: Text(widget.title),
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: Container(
|
||||||
itemCount: _notifs.length,
|
padding: const EdgeInsets.all(20),
|
||||||
reverse: true,
|
child: Center(
|
||||||
itemBuilder: (BuildContext context, int idx) {
|
child: Column(
|
||||||
final entry = _notifs[idx];
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
return ListTile(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
leading: Text(entry.songTitle),
|
children: [
|
||||||
trailing: Text(entry.artist),
|
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(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: running ? stopListening : startListening,
|
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),
|
child: running ? const Icon(Icons.stop) : const Icon(Icons.play_arrow),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -85,12 +85,4 @@ class AndroidNotificationListener {
|
|||||||
static Future<bool?> openPermissionSettings() async {
|
static Future<bool?> openPermissionSettings() async {
|
||||||
return await _methodChannel.invokeMethod('plugin.openPermissionSettings');
|
return await _methodChannel.invokeMethod('plugin.openPermissionSettings');
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool?> startService() async {
|
|
||||||
return await _methodChannel.invokeMethod('plugin.startService');
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<bool?> stopService() async {
|
|
||||||
return await _methodChannel.invokeMethod('plugin.stopService');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user