Skip to content

Commit

Permalink
feat(nu.nl): add ad removal patch
Browse files Browse the repository at this point in the history
  • Loading branch information
jabbink committed Jan 29, 2025
1 parent 525bad2 commit 93ec15d
Show file tree
Hide file tree
Showing 15 changed files with 282 additions and 0 deletions.
3 changes: 3 additions & 0 deletions extensions/nunl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
compileOnly(project(":extensions:nunl:stub"))
}
1 change: 1 addition & 0 deletions extensions/nunl/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package app.revanced.extension.nunl;

import nl.nu.performance.api.client.interfaces.Block;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unused")
public class ScreenMapperPatch {
private static final String[] blockedHeaderBlocks = {
"Aanbiedingen (Adverteerders)",
"Aangeboden door NUshop"
};

// "Rubrieken" menu links to ads
private static final String[] blockedLinkBlocks = {
"Van onze adverteerders"
};

public static void filterAds(List<Block> blocks) {
ArrayList<Block> cleanedList = new ArrayList<>();

boolean skipFullHeader = false;
boolean skipUntilDivider = false;

int index = 0;
while (index < blocks.size()) {
Block currentBlock = blocks.get(index);

// because of pagination, we might not see the Divider in front of it
// just remove it as is and leave potential extra spacing visible on the screen
if (currentBlock instanceof DpgBannerBlock) {
index++;
continue;
}

if (index + 1 < blocks.size()) {
// filter Divider -> DpgMediaBanner -> Divider
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof DpgBannerBlock) {
index += 2;
continue;
}

// filter Divider -> LinkBlock (... -> LinkBlock -> LinkBlock-> LinkBlock -> Divider)
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof LinkBlock linkBlock) {
Link link = linkBlock.getLink();
if (link != null && link.getTitle() != null) {
for (String blockedLinkBlock : blockedLinkBlocks) {
if (blockedLinkBlock.equals(link.getTitle().getText())) {
skipUntilDivider = true;
break;
}
}
if (skipUntilDivider) {
index++;
continue;
}
}
}
}

if (currentBlock instanceof DividerBlock) {
skipUntilDivider = false;
}

// filter HeaderBlock with known ads until next HeaderBlock
if (currentBlock instanceof HeaderBlock headerBlock) {
StyledText headerText = headerBlock.component20();
if (headerText != null) {
skipFullHeader = false;
for (String blockedHeaderBlock : blockedHeaderBlocks) {
if (blockedHeaderBlock.equals(headerText.getText())) {
skipFullHeader = true;
break;
}
}
if (skipFullHeader) {
index++;
continue;
}
}
}

if (!skipFullHeader && !skipUntilDivider) {
cleanedList.add(currentBlock);
}
index++;
}

// replace list in-place to not deal with moving the result to the correct register in smali
blocks.clear();
blocks.addAll(cleanedList);
}
}
17 changes: 17 additions & 0 deletions extensions/nunl/stub/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
}

android {
namespace = "app.revanced.extension"
compileSdk = 33

defaultConfig {
minSdk = 24
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
1 change: 1 addition & 0 deletions extensions/nunl/stub/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package nl.nu.performance.api.client.interfaces;

public class Block {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;

import nl.nu.performance.api.client.interfaces.Block;

public class DividerBlock extends Block {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;

import nl.nu.performance.api.client.interfaces.Block;

public class DpgBannerBlock extends Block {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;

import nl.nu.performance.api.client.interfaces.Block;

public class HeaderBlock extends Block {
// returns title
public final StyledText component20() {
throw new UnsupportedOperationException("Stub");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;

public class Link {
public final StyledText getTitle() {
throw new UnsupportedOperationException("Stub");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;

import android.os.Parcelable;
import nl.nu.performance.api.client.interfaces.Block;

public abstract class LinkBlock extends Block implements Parcelable {
public final Link getLink() {
throw new UnsupportedOperationException("Stub");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;

public class StyledText {
public final String getText() {
throw new UnsupportedOperationException("Stub");
}
}
4 changes: 4 additions & 0 deletions patches/api/patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/nunl/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package app.revanced.patches.nunl.ads

import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode

internal val xandrInitFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("V")
custom { methodDef, classDef ->
classDef.type == "Lcom/appnexus/opensdk/XandrAd;" && methodDef.name == "init" && methodDef.parameters.size == 4
}
}

internal val jwCreateAdvertisementFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
custom { methodDef, classDef ->
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising"
}
}

internal val screenMapperFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Lnl/nu/android/bff/domain/models/screen/ScreenEntity;")
parameters("Lnl/nu/performance/api/client/objects/Screen;")

opcodes(
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.CHECK_CAST
)

custom { methodDef, classDef ->
classDef.type == "Lnl/nu/android/bff/data/mappers/ScreenMapper;" && methodDef.name == "map"
}
}

internal val nextPageRepositoryImplFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("Lnl/nu/android/bff/domain/models/Page;")
parameters("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;")

opcodes(
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.CHECK_CAST
)

custom { methodDef, classDef ->
classDef.type == "Lnl/nu/android/bff/data/repositories/NextPageRepositoryImpl;" && methodDef.name == "mapToPage"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package app.revanced.patches.nunl.ads

import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x

@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads",
use = true
) {
extendWith("extensions/nunl.rve")

compatibleWith("nl.sanomamedia.android.nu"("11.0.0"))

execute {
// prevent Xandr from being initialized showing sponsored "articles" in the lists
xandrInitFingerprint.method.addInstructions(
0,
"""
return-void
""",
)

// prevent video pre-roll ads
jwCreateAdvertisementFingerprint.method.addInstructions(
0,
"""
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
invoke-direct {v0}, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
invoke-virtual {v0}, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
move-result-object v0
return-object v0
""",
)

// filter injected content from API calls out of lists
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
val startIndex = it.patternMatch!!.startIndex
it.method.apply {
val moveInstruction = getInstruction<Instruction11x>(startIndex)

val listRegister = moveInstruction.registerA

addInstructions(
startIndex + 1,
"""
invoke-static {v$listRegister}, Lapp/revanced/extension/nunl/ScreenMapperPatch;->filterAds(Ljava/util/List;)V
""",
)
}
}
}
}

0 comments on commit 93ec15d

Please sign in to comment.