Hello how you been?, hope everything is going well.
I wanted to learn Kotlin for native Android development, I already knew how to do it with java, so the only obstacle for me is learning Kotlin.
Kotlin
- Kotlin trello board that I used to track progress.
- After learning the basics for Kotlin, Kotlin koans is a great place for practice (beginner to advanced).
Chosing what to do with Kotlin
I use Accuradio android app to stream music, but it does not have a paid tear, So the ads are not skippable and there was no existing adblocker that does this(which is a pain). So I decided to write one in Kotlin.
So its decided, I am doing a project in Kotlin to block/silence ads in Accuradio, some additional things I decided to implement are the following.
- should be extensible to other apps like Spotify,…etc
- should have minimal UI
- should be lightweight
- should be completely openSource.
To be extensbile, I should have a common pattern/way to detect ads in these apps… One such way is by using app’s notification.
I can determine what an app is currenly playing using the notification, using the notification listener service and parsing the posted notifications, mute/unmute the app based on the content.
An update, I already added support for Spotify and Tidal.
Implementation
I pretty much have to just
- implement the NotificationListenerService service, and parse the posted notifications,
extend the NotificationListenerService class and implement the onNotificationPosted method & onNotificationConnected method.
class NotificationListener : NotificationListenerService() {
override fun onListenerConnected() {
super.onListenerConnected()
appNotificationHelper?.updateNotification("AdSilence, listening for ads")?.run {
startForeground(NOTIFICATION_ID, this) // persistent notification
}
Log.v(TAG, "notification listener connected")
}
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
}
}
why do I need to run it as a foreground service?
- because In recent versions of android, any app that is actively running the background must post a notification to the status bar. Otherwise the app will be killed by the system.
then mute/unmute the app based on the content,
sbn?.let { with(AppNotification(applicationContext, it.notification, sbn.packageName)) { Preference(applicationContext).isAppConfigured(this.getApp()).takeIf { b -> b } ?.run { Utils().run { when (NotificationParser(this@with).isAd()) { true -> { this.mute() isMuted = true } false -> { isMuted = false this.unmute(); ) } } } } } }
next important step is granting the app, the permission to listen to notifications. (if all apps can listen to incoming notifications, this would be a huge security flaw!)
- grant permission
- since this app is openSource, everyone can see the source code before granting permission
bascially the following one line has to be used. (note: using string to support older devices)
startActivity(Intent("enabled_notification_listeners"))
in the android manifest, add the following to the
<application>
tag,telling the system to use the file
NotificationListenerService
as the notification listener service.<service android:name=".NotificationListener" android:label="@string/notification_listener_name" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:exported="false"> <intent-filter> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter> </service>
Conclusion
That is all is required to get the app to work, there are other things like UI, checking app installed or not, maintaining the app running in the background, hiding the app from recents, restarting the service, preference…etc. Since the app is so lightweight (around 150KB)…
I could write about every part of the app in this, but this app is open source….its already explained. It was so refreshing and fun work on this… Kotlin is fun and I am glad to have learned it(atleast some bit).