Skip to content

Commit 66dbe22

Browse files
authored
Merge pull request #156 from aghontpi/release/v0.6.2
2 parents b7276ac + 8e1c244 commit 66dbe22

File tree

15 files changed

+357
-115
lines changed

15 files changed

+357
-115
lines changed

.github/workflows/android.yml

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,50 @@ on:
88

99
jobs:
1010
build:
11-
1211
runs-on: ubuntu-latest
1312

1413
steps:
15-
- uses: actions/checkout@v3
16-
- name: set up JDK 11
17-
uses: actions/setup-java@v3
18-
with:
19-
java-version: '11'
20-
distribution: 'temurin'
21-
cache: gradle
22-
23-
- name: Decode keystore
24-
env:
25-
DEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}
26-
run: |
27-
echo "$DEBUG_KEYSTORE" | base64 --decode > debug.keystore
28-
29-
- name: Grant execute permission for gradlew
30-
run: chmod +x gradlew
31-
- name: Build with Gradle
32-
env:
33-
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
34-
KEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
35-
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
36-
37-
run: ./gradlew assembleDebug -Pandroid.injected.signing.store.file=$(pwd)/debug.keystore -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD
38-
39-
- name: Upload debug build
40-
uses: actions/upload-artifact@v3
41-
with:
42-
name: ad-silence-debug-build.apk (build artifact)
43-
path: app/build/outputs/apk/debug/app-debug.apk
14+
- uses: actions/checkout@v3
15+
16+
- name: Set up JDK 17
17+
uses: actions/setup-java@v3
18+
with:
19+
java-version: '17'
20+
distribution: 'temurin'
21+
cache: gradle
22+
23+
- name: Decode keystore
24+
env:
25+
DEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}
26+
run: |
27+
echo "$DEBUG_KEYSTORE" | base64 --decode > debug.keystore
28+
29+
- name: Grant execute permission for gradlew
30+
run: chmod +x gradlew
31+
32+
- name: Build with Gradle
33+
env:
34+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
35+
KEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
36+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
37+
run: |
38+
./gradlew assembleDebug \
39+
-Pandroid.injected.signing.store.file=$(pwd)/debug.keystore \
40+
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
41+
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \
42+
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
43+
44+
- name: Upload Debug APK
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: ad-silence-debug-build-apk
48+
path: app/build/outputs/apk/debug/app-debug.apk
49+
50+
- name: Comment Debug APK on PR
51+
if: github.event_name == 'pull_request'
52+
uses: peter-evans/create-or-update-comment@v3
53+
with:
54+
issue-number: ${{ github.event.pull_request.number }}
55+
body: |
56+
A debug APK build is available for this pull request.
57+
You can download it from [this workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) under the **Artifacts** section.

Changelog/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [v0.6.2] - (Feb 28, 2025)
9+
10+
**_Stable - requires android 5 or higher_**
11+
12+
## Added
13+
14+
- Add certificate comparison for faster checks ([159ba1c](https://github.com/aghontpi/ad-silence/commit/159ba1c))
15+
- Add fastlane configuration ([159ba1c](https://github.com/aghontpi/ad-silence/commit/159ba1c))
16+
- Support for Android 14 (SDK 34) ([d72783f](https://github.com/aghontpi/ad-silence/commit/d72783f))
17+
- Support for Android 15 (SDK 35) ([f399fb3](https://github.com/aghontpi/ad-silence/commit/f399fb3))
18+
19+
## Fixes
20+
21+
- Fix Accuradio detection not working ([4c5a6c4](https://github.com/aghontpi/ad-silence/commit/4c5a6c4))
22+
23+
## Updates
24+
25+
- Update Gradle to 8.12 ([f5943a8](https://github.com/aghontpi/ad-silence/commit/f5943a8))
26+
- Update Android build tools to 8.8.2 ([4486918](https://github.com/aghontpi/ad-silence/commit/4486918))
27+
- Minor CI & CD improvements ([53b659d](https://github.com/aghontpi/ad-silence/commit/53b659d))
28+
29+
**You can get it on Google Playstore & F-Droid**
30+
31+
<p align="left">
32+
<a href='https://play.google.com/store/apps/details?id=bluepie.ad_silence&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'>
33+
<img alt='Get it on Google Play' height="72px" src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/>
34+
</a>
35+
<a href='https://f-droid.org/packages/bluepie.ad_silence/'>
36+
<img alt='Get it on F-Droid' height="72px" src='https://fdroid.gitlab.io/artwork/badge/get-it-on.png'/>
37+
</a>
38+
</p>
39+
840

941
## [v0.6.1] - (Jan 26, 2024)
1042

app/build.gradle

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ plugins {
44
}
55

66
android {
7-
compileSdk 33
7+
compileSdk 35
88

99
defaultConfig {
1010
applicationId "bluepie.ad_silence"
1111
minSdk 21
12-
targetSdk 33
13-
versionCode 49
14-
versionName "0.6.1-prod"
12+
targetSdk 35
13+
versionCode 51
14+
versionName "0.6.2-prod"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}
@@ -32,18 +32,18 @@ android {
3232
}
3333
}
3434
compileOptions {
35-
sourceCompatibility JavaVersion.VERSION_1_8
36-
targetCompatibility JavaVersion.VERSION_1_8
35+
sourceCompatibility JavaVersion.VERSION_17
36+
targetCompatibility JavaVersion.VERSION_17
3737
}
3838
kotlinOptions {
39-
jvmTarget = '1.8'
39+
jvmTarget = '17'
4040
}
41+
42+
namespace 'bluepie.ad_silence'
4143

42-
lintOptions {
44+
lint {
4345
checkDependencies true
4446
}
45-
46-
namespace 'bluepie.ad_silence'
4747
}
4848

4949
dependencies {

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
44
<!--android 13 and above-->
55
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
6+
<!-- New permission for specialUse foreground service -->
7+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
8+
69
<queries>
710
<!--android 11 and above-->
811
<package android:name="com.slipstream.accuradio"/>
@@ -13,13 +16,15 @@
1316
<package android:name="com.slacker.radio"/>
1417
<package android:name="com.soundcloud.android"/>
1518
</queries>
19+
1620
<application
1721
android:allowBackup="true"
1822
android:icon="@mipmap/ic_launcher"
1923
android:label="@string/app_name"
2024
android:roundIcon="@mipmap/ic_launcher_round"
2125
android:supportsRtl="true"
2226
android:theme="@style/Theme.Silence">
27+
2328
<activity
2429
android:name=".AdSilenceActivity"
2530
android:excludeFromRecents="true"
@@ -31,13 +36,15 @@
3136
</activity>
3237
<service android:name=".NotificationListener"
3338
android:label="@string/notification_listener_name"
39+
android:foregroundServiceType="specialUse"
3440
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
3541
android:exported="false">
42+
<property
43+
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
44+
android:value="mute_audio_ads_monitor" />
3645
<intent-filter>
3746
<action android:name="android.service.notification.NotificationListenerService" />
3847
</intent-filter>
3948
</service>
4049
</application>
41-
42-
43-
</manifest>
50+
</manifest>

app/src/main/java/bluepie/ad_silence/NotificationParser.kt

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ package bluepie.ad_silence
33
import android.app.Notification
44
import android.content.Context
55
import android.util.Log
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.FrameLayout
9+
import android.widget.RemoteViews
10+
import android.widget.TextView
611
import bluepie.ad_silence.triggers.spotifyTrigger
712
import java.util.*
813

@@ -79,63 +84,100 @@ class NotificationParser(override var appNotification: AppNotification) :
7984
return notificationInfo
8085
}
8186

82-
private fun parseAccuradioNotification(): Boolean {
83-
Log.v(
84-
TAG,
85-
"detected ${appNotification.context.getString(R.string.accuradio)} -> ${
86-
appNotification.context.getString(R.string.accuradio_pkg_name)
87-
}"
88-
)
89-
val notification = appNotification.notification
90-
try {
91-
var fields: ArrayList<*>? = null
9287

93-
var contentView = notification.contentView;
88+
private fun extractTextFromRemoteViews(remoteViews: RemoteViews): String {
89+
return try {
90+
// Inflate the RemoteViews into a temporary container.
91+
val parent = FrameLayout(appNotification.context)
92+
val view = remoteViews.apply(appNotification.context, parent)
93+
// Traverse the inflated view tree to extract all text.
94+
extractTextFromView(view)
95+
} catch (e: Exception) {
96+
Log.e(TAG, "RemoteViews inflation failed", e)
97+
""
98+
}
99+
}
94100

95-
if (contentView == null) {
96-
contentView = notification.bigContentView;
101+
private fun extractTextFromView(view: View): String {
102+
val sb = StringBuilder()
103+
if (view is TextView) {
104+
sb.append(view.text)
105+
}
106+
if (view is ViewGroup) {
107+
for (i in 0 until view.childCount) {
108+
sb.append(" ").append(extractTextFromView(view.getChildAt(i)))
97109
}
98-
if (contentView == null) {
99-
contentView = notification.headsUpContentView;
110+
}
111+
return sb.toString().trim()
112+
}
113+
114+
private fun extractTextViaReflection(remoteViews: RemoteViews): String {
115+
var fields: ArrayList<*>? = null
116+
remoteViews.javaClass.declaredFields.forEach { field ->
117+
if (field.name == "mActions") {
118+
field.isAccessible = true
119+
fields = field.get(remoteViews) as? ArrayList<*>
100120
}
121+
}
122+
val info = LinkedList<String>()
123+
fields?.forEach { action ->
124+
action?.javaClass?.declaredFields
125+
?.firstOrNull { it.name == "value" }
126+
?.apply { isAccessible = true }
127+
?.let { field ->
128+
when (val v = field.get(action)) {
129+
is String -> info.push(v)
130+
}
131+
}
132+
}
133+
return info.joinToString(" ")
134+
}
101135

102136

103-
for (field in contentView.javaClass.declaredFields) {
104-
if (field.name == "mActions") {
105-
field.isAccessible = true
106-
fields = field.get(contentView) as ArrayList<*>
137+
private fun parseAccuradioNotification(): Boolean {
138+
Log.v(
139+
TAG,
140+
"detected ${appNotification.context.getString(R.string.accuradio)} -> " +
141+
appNotification.context.getString(R.string.accuradio_pkg_name)
142+
)
143+
val notification = appNotification.notification
144+
try {
145+
var notificationText = ""
146+
// remote views
147+
val remoteViews = notification.contentView
148+
?: notification.bigContentView
149+
?: notification.headsUpContentView
150+
151+
// Trying to inflate the RemoteViews
152+
if (remoteViews != null) {
153+
try {
154+
notificationText = extractTextFromRemoteViews(remoteViews)
155+
} catch (e: Exception) {
156+
Log.e(TAG, "RemoteViews inflation failed", e)
157+
notificationText = ""
107158
}
108-
109159
}
110-
val info = LinkedList<String>()
111-
fields?.toArray()?.forEach { it ->
112-
if (it != null) {
113-
val fieldFilter = it.javaClass.declaredFields.filter { it.name == "value" }
114-
if (fieldFilter.size == 1) {
115-
val field = fieldFilter.get(0)
116-
field.isAccessible = true
117-
118-
// necessary fields
119-
when (val v = field.get(it)) {
120-
is String -> info.push(v)
121-
}
122-
}
123-
}
160+
161+
// fallback to reflection.
162+
if (notificationText.isBlank() && remoteViews != null) {
163+
notificationText = extractTextViaReflection(remoteViews)
124164
}
125165

126-
notificationInfo = info
166+
notificationInfo = LinkedList<String>().apply { push(notificationText) }
167+
168+
// Check for ad strings.
127169
var isAd = false
128170
for (adString in appNotification.adString()) {
129-
if (info.any { it.contains(adString) }) {
171+
if (notificationText.contains(adString)) {
130172
Log.v(TAG, "detection in Accuradio: $adString")
131173
isAd = true
132174
break
133175
}
134176
}
135177
return isAd
136-
} catch (nullPointerException: NullPointerException) {
137-
Log.v(TAG, "Ad-silence exception in parsing accuradio")
138-
return false;
178+
} catch (e: Exception) {
179+
Log.v(TAG, "Ad-silence exception in parsing accuradio", e)
180+
return false
139181
}
140182
}
141183

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ buildscript {
55
mavenCentral()
66
}
77
dependencies {
8-
classpath 'com.android.tools.build:gradle:7.4.2'
8+
classpath 'com.android.tools.build:gradle:8.8.2'
99
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
1010

1111
// NOTE: Do not place your application dependencies here; they belong
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fix Accuradio detection not working
2+
- Compile target sdk for android 14 & 15

fastlane/metadata/android/en-US/full_description.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<b>Features:</b>
22

33
- Remove ads in Accuradio, Spotify, Spotify lite, Tidal, Soundcloud, Pandora & LiveOne.
4-
- Lightweight (~ 150KB)
4+
- Lightweight (~ 120KB)
55
- Minimal UI
66
- Configure which apps to remove ads
77
- No bloat/external dependencies
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Silence ads in Accuradio, Spotify & Tidal. Lightweight & minimal(under 150kb).
1+
Silence ads in Accuradio, Spotify & Tidal. Lightweight & minimal(under 120kb).

gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ kotlin.code.style=official
1515
org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" -Dfile.encoding\=UTF-8
1616
android.useAndroidX=true
1717
android.enableJetifier=true
18+
android.defaults.buildfeatures.buildconfig=true
19+
android.nonTransitiveRClass=false
20+
android.nonFinalResIds=false

0 commit comments

Comments
 (0)