diff --git a/CHANGELOG.md b/CHANGELOG.md index 767a834..c610ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog / Ad-Free + +### v3.2/42 (2023-12-08) +- Improve Accuradio Detector to support bigContentView, tickerView and contentView (#93, thanks to @unseenlarks) +- Use generic reflection approach UserDefinedText Detector to search for user defined ad texts + ### v3.1/41 - Fix bug with local music plugin: folder selection diff --git a/app/build.gradle b/app/build.gradle index d2ca3b9..f9b1011 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "ch.abertschi.adfree" minSdkVersion 23 targetSdkVersion 27 - versionCode 41 - versionName "3.1" + versionCode 42 + versionName "3.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/app/src/main/java/ch/abertschi/adfree/detector/AccuradioDetector.kt b/app/src/main/java/ch/abertschi/adfree/detector/AccuradioDetector.kt index bc3a01f..ff30b6b 100644 --- a/app/src/main/java/ch/abertschi/adfree/detector/AccuradioDetector.kt +++ b/app/src/main/java/ch/abertschi/adfree/detector/AccuradioDetector.kt @@ -55,10 +55,9 @@ class AccuradioDetector : AdDetectable, AnkoLogger, AbstractNotificationDetector } } - override fun flagAsAdvertisement(payload: AdPayload): Boolean { + + private fun inspectContentViews(contentView: RemoteViews?): Boolean { try { - val contentView = payload.statusbarNotification?.notification?.contentView - info(payload) if (contentView != null) { val actions = extractObject(contentView, "mActions") as List<*>? if (actions != null) { @@ -88,4 +87,18 @@ class AccuradioDetector : AdDetectable, AnkoLogger, AbstractNotificationDetector } return false } + + override fun flagAsAdvertisement(payload: AdPayload): Boolean { + // XXX: Support old deprecated fields + val contentView = payload.statusbarNotification.notification?.contentView + val bigView = payload.statusbarNotification.notification?.bigContentView + val tickerView = payload.statusbarNotification.notification?.tickerView + + for (v in listOf(contentView, bigView, tickerView)) { + if (inspectContentViews(v)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/app/src/main/java/ch/abertschi/adfree/detector/UserDefinedTextDetector.kt b/app/src/main/java/ch/abertschi/adfree/detector/UserDefinedTextDetector.kt index e7f9e13..1d180e9 100644 --- a/app/src/main/java/ch/abertschi/adfree/detector/UserDefinedTextDetector.kt +++ b/app/src/main/java/ch/abertschi/adfree/detector/UserDefinedTextDetector.kt @@ -4,6 +4,7 @@ import android.app.Notification import android.os.Bundle import ch.abertschi.adfree.model.TextRepository import ch.abertschi.adfree.model.TextRepositoryData +import com.thoughtworks.xstream.XStream import org.jetbrains.anko.AnkoLogger import org.jetbrains.anko.warn import java.util.* @@ -38,8 +39,9 @@ class UserDefinedTextDetector(private val repo: TextRepository) : AdDetectable, } } - override fun flagAsAdvertisement(payload: AdPayload): Boolean { - val extras = payload.statusbarNotification?.notification?.extras + // Use a fixed approach and search for predefined fields + private fun flagAsAdvertisementFixed(payload: AdPayload): Boolean { + val extras = payload.statusbarNotification.notification?.extras val title = extractString(extras, Notification.EXTRA_TITLE) val subTitle = extractString(extras, Notification.EXTRA_SUB_TEXT) @@ -59,6 +61,27 @@ class UserDefinedTextDetector(private val repo: TextRepository) : AdDetectable, return false; } + private fun flagAsAdvertisementDynamic(payload: AdPayload): Boolean { + /* + * XXX: This implementation is inefficient but simple. + * Will a reflection approach be better? + */ + val str = XStream().toXML(payload)!!.toLowerCase() + for (entry in payload.matchedTextDetectorEntries) { + for (entryLine in entry.content) { + if (entryLine.trim().isEmpty()) { + continue + } + if (str.contains(entryLine.trim().toLowerCase())) { + return true + } + } + } + return false + } + override fun flagAsAdvertisement(payload: AdPayload) = + flagAsAdvertisementFixed(payload) || flagAsAdvertisementDynamic(payload) + override fun getMeta(): AdDetectorMeta = AdDetectorMeta( "User defined text", "flag a notification based on the presence of text", false, diff --git a/app/src/main/java/ch/abertschi/adfree/model/TextRepository.kt b/app/src/main/java/ch/abertschi/adfree/model/TextRepository.kt index e753e6e..5957cb1 100644 --- a/app/src/main/java/ch/abertschi/adfree/model/TextRepository.kt +++ b/app/src/main/java/ch/abertschi/adfree/model/TextRepository.kt @@ -39,6 +39,7 @@ class TextRepository : AnkoLogger { private val context: Context private val ID_KEY: String = "k_" private val ID_KEYS: String = "keys" + private val ID_USE_REFLECTION_FOR_MATCH = "_use_reflection" private var dataEntries: ArrayList @@ -56,6 +57,8 @@ class TextRepository : AnkoLogger { return sharedPreferences.getStringSet(ID_KEYS, HashSet()) } + + private fun getEntryByFormattedKey(key: String): TextRepositoryData? { var dataStr: String = sharedPreferences.getString(key, null) ?: return null return TextRepositoryData.deserialzeFromString(dataStr) @@ -72,6 +75,18 @@ class TextRepository : AnkoLogger { return entries } + /* + * Boolean to indicate if a generic reflection based + * approach should be used to find a matching text entry in all fields of the payload + */ + fun useReflectionForMatch(): Boolean { + return sharedPreferences.getBoolean(ID_USE_REFLECTION_FOR_MATCH, false) + } + + fun setReflectionForMatch(useIt: Boolean) { + sharedPreferences.edit().putBoolean(ID_USE_REFLECTION_FOR_MATCH, useIt).apply() + } + fun getAllEntries(): ArrayList { return ArrayList(dataEntries) } diff --git a/app/src/main/java/ch/abertschi/adfree/presenter/SettingsPresenter.kt b/app/src/main/java/ch/abertschi/adfree/presenter/SettingsPresenter.kt index d658c19..566dfb8 100644 --- a/app/src/main/java/ch/abertschi/adfree/presenter/SettingsPresenter.kt +++ b/app/src/main/java/ch/abertschi/adfree/presenter/SettingsPresenter.kt @@ -11,12 +11,9 @@ import ch.abertschi.adfree.ad.AdObservable import ch.abertschi.adfree.plugin.AdPlugin import ch.abertschi.adfree.plugin.PluginHandler import ch.abertschi.adfree.view.setting.SettingsView -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers import org.jetbrains.anko.AnkoLogger import org.jetbrains.anko.collections.forEachWithIndex -import java.util.concurrent.TimeUnit + /** diff --git a/metadata/en-US/changelogs/42.txt b/metadata/en-US/changelogs/42.txt new file mode 100644 index 0000000..3430405 --- /dev/null +++ b/metadata/en-US/changelogs/42.txt @@ -0,0 +1,3 @@ +### v3.2/42 (2023-12-08) +- Improve Accuradio Detector to support bigContentView, tickerView and contentView (#93, thanks to @unseenlarks) +- Use generic reflection approach UserDefinedText Detector to search for user defined ad texts diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt index 56e3862..be30be5 100644 --- a/metadata/en-US/full_description.txt +++ b/metadata/en-US/full_description.txt @@ -1,4 +1,4 @@ -ad-free is an ad audio blocker for Android. +Ad-free is an ad audio blocker for Android. This app is a research project attempting to show flaws in the way how audio advertisement is shown on Android. It is a proof-of-concept of a diff --git a/metadata/en-US/short_description.txt b/metadata/en-US/short_description.txt index b6e1b67..b28d208 100644 --- a/metadata/en-US/short_description.txt +++ b/metadata/en-US/short_description.txt @@ -1 +1 @@ -ad-free is a modularized audio ad blocker for Spotify and more on Android. +Ad-free is a modularized audio ad blocker for Spotify and more on Android.