diff --git a/CHANGELOG.md b/CHANGELOG.md index b85e438ac..a7b15246d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [5.44.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.3...v5.44.0-dev.1) (2025-10-22) + + +### Features + +* **Samsung Radio:** Add `Disable device checks` patch ([#6145](https://github.com/ReVanced/revanced-patches/issues/6145)) ([de97562](https://github.com/ReVanced/revanced-patches/commit/de97562c5ddc8ec707761c1e04e74c4e18f9c158)) + +## [5.43.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.2...v5.43.2-dev.3) (2025-10-19) + + +### Bug Fixes + +* **YouTube - Hide layout components:** Hide new kind of community post ([#6146](https://github.com/ReVanced/revanced-patches/issues/6146)) ([cfd244b](https://github.com/ReVanced/revanced-patches/commit/cfd244b4088daacd2788ec38357ac521e4b296d5)) + ## [5.43.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.1...v5.43.2-dev.2) (2025-10-17) diff --git a/extensions/samsung/radio/build.gradle.kts b/extensions/samsung/radio/build.gradle.kts new file mode 100644 index 000000000..0eadeef26 --- /dev/null +++ b/extensions/samsung/radio/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compileOnly(project(":extensions:shared:library")) + compileOnly(project(":extensions:samsung:radio:stub")) +} diff --git a/extensions/samsung/radio/src/main/AndroidManifest.xml b/extensions/samsung/radio/src/main/AndroidManifest.xml new file mode 100644 index 000000000..9b65eb06c --- /dev/null +++ b/extensions/samsung/radio/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java new file mode 100644 index 000000000..72c5addc4 --- /dev/null +++ b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java @@ -0,0 +1,24 @@ +package app.revanced.extension.samsung.radio.misc.fix.crash; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@SuppressWarnings("unused") +public final class FixCrashPatch { + /** + * Injection point. + *

+ * Add the required permissions to the request list to avoid crashes on API 34+. + **/ + public static final String[] fixPermissionRequestList(String[] perms) { + List permsList = new ArrayList<>(Arrays.asList(perms)); + if (permsList.contains("android.permission.POST_NOTIFICATIONS")) { + permsList.addAll(Arrays.asList("android.permission.RECORD_AUDIO", "android.permission.READ_PHONE_STATE", "android.permission.FOREGROUND_SERVICE_MICROPHONE")); + } + if (permsList.contains("android.permission.RECORD_AUDIO")) { + permsList.add("android.permission.FOREGROUND_SERVICE_MICROPHONE"); + } + return permsList.toArray(new String[0]); + } +} diff --git a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java new file mode 100644 index 000000000..19b6c3e82 --- /dev/null +++ b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java @@ -0,0 +1,19 @@ +package app.revanced.extension.samsung.radio.restrictions.device; + +import android.os.SemSystemProperties; + +import java.util.Arrays; + +@SuppressWarnings("unused") +public final class BypassDeviceChecksPatch { + + /** + * Injection point. + *

+ * Check if the device has the required hardware + **/ + public static final boolean checkIfDeviceIsIncompatible(String[] deviceList) { + String currentDevice = SemSystemProperties.getSalesCode(); + return Arrays.asList(deviceList).contains(currentDevice); + } +} diff --git a/extensions/samsung/radio/stub/build.gradle.kts b/extensions/samsung/radio/stub/build.gradle.kts new file mode 100644 index 000000000..b4bee8809 --- /dev/null +++ b/extensions/samsung/radio/stub/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(libs.plugins.android.library) +} + +android { + namespace = "app.revanced.extension" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} diff --git a/extensions/samsung/radio/stub/src/main/AndroidManifest.xml b/extensions/samsung/radio/stub/src/main/AndroidManifest.xml new file mode 100644 index 000000000..15e7c2ae6 --- /dev/null +++ b/extensions/samsung/radio/stub/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java b/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java new file mode 100644 index 000000000..33a4b4400 --- /dev/null +++ b/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java @@ -0,0 +1,7 @@ +package android.os; + +public class SemSystemProperties { + public static String getSalesCode() { + throw new UnsupportedOperationException("Stub"); + } +} \ No newline at end of file diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java index a9fbe8f7f..d9185ba35 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java @@ -88,7 +88,8 @@ public final class LayoutComponentsFilter extends Filter { "post_shelf_slim.e", "videos_post_responsive_root.e", "text_post_responsive_root.e", - "poll_post_responsive_root.e" + "poll_post_responsive_root.e", + "shared_post_root.e" ); final var subscribersCommunityGuidelines = new StringFilterGroup( diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ProgressBarDrawable.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ProgressBarDrawable.java deleted file mode 100644 index 93d53c9a5..000000000 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ProgressBarDrawable.java +++ /dev/null @@ -1,51 +0,0 @@ -package app.revanced.extension.youtube.patches.theme; - -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.drawable.Drawable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import app.revanced.extension.youtube.patches.HideSeekbarPatch; -import app.revanced.extension.youtube.settings.Settings; - -/** - * Used by {@link SeekbarColorPatch} change the color of the seekbar. - * and {@link HideSeekbarPatch} to hide the seekbar of the feed and watch history. - */ -@SuppressWarnings("unused") -public class ProgressBarDrawable extends Drawable { - - private final Paint paint = new Paint(); - { - paint.setColor(SeekbarColorPatch.getSeekbarColor()); - } - - @Override - public void draw(@NonNull Canvas canvas) { - if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) { - return; - } - - canvas.drawRect(getBounds(), paint); - } - - @Override - public void setAlpha(int alpha) { - paint.setAlpha(alpha); - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - paint.setColorFilter(colorFilter); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - -} diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index de70304bb..65f31d813 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -4,9 +4,7 @@ import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.Utils.clamp; import static app.revanced.extension.youtube.patches.theme.ThemePatch.SplashScreenAnimationStyle; -import android.content.res.Resources; import android.graphics.Color; -import android.graphics.drawable.AnimatedVectorDrawable; import com.airbnb.lottie.LottieAnimationView; @@ -15,7 +13,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.Locale; import java.util.Scanner; import app.revanced.extension.shared.Logger; @@ -105,27 +102,6 @@ public final class SeekbarColorPatch { return customSeekbarColor; } - private static int colorChannelTo3Bits(int channel8Bits) { - final float channel3Bits = channel8Bits * 7 / 255f; - - // If a color channel is near zero, then allow rounding up so values between - // 0x12 and 0x23 will show as 0x24. But always round down when the channel is - // near full saturation, otherwise rounding to nearest will cause all values - // between 0xEC and 0xFE to always show as full saturation (0xFF). - return channel3Bits < 6 - ? Math.round(channel3Bits) - : (int) channel3Bits; - } - - @SuppressWarnings("SameParameterValue") - private static String get9BitStyleIdentifier(int color24Bit) { - final int r3 = colorChannelTo3Bits(Color.red(color24Bit)); - final int g3 = colorChannelTo3Bits(Color.green(color24Bit)); - final int b3 = colorChannelTo3Bits(Color.blue(color24Bit)); - - return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b3); - } - /** * injection point. */ @@ -136,36 +112,6 @@ public final class SeekbarColorPatch { return original; // false = drawable style, true = lottie style. } - /** - * Injection point. - * Old drawable style launch screen. - */ - public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) { - // Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable - // without using any styles, but a color filter cannot selectively change the seekbar - // while keeping the red YT logo untouched. - // Even if the seekbar color xml value is changed to a completely different color (such as green), - // a color filter still cannot be selectively applied when the drawable has more than 1 color. - try { - // Must set the color even if custom seekbar is off, - // because the xml color was replaced with a themed value. - String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor); - Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle); - - final int styleIdentifierDefault = Utils.getResourceIdentifierOrThrow( - ResourceType.STYLE, - seekbarStyle - ); - - Resources.Theme theme = Utils.getContext().getResources().newTheme(); - theme.applyStyle(styleIdentifierDefault, true); - - vectorDrawable.applyTheme(theme); - } catch (Exception ex) { - Logger.printException(() -> "setSplashAnimationDrawableTheme failure", ex); - } - } - /** * Injection point. * Modern Lottie style animation. diff --git a/gradle.properties b/gradle.properties index 973dda541..a5b833989 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official -version = 5.43.2-dev.2 +version = 5.44.0-dev.1 diff --git a/patches/api/patches.api b/patches/api/patches.api index 919a9d487..82c7b524f 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -714,6 +714,14 @@ public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQuer public static final fun getSanitizeUrlQueryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatchKt { + public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatchKt { + public static final fun getBypassDeviceChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatchKt { public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt new file mode 100644 index 000000000..35641f2e7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.samsung.radio.misc.fix.crash + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.util.asSequence +import org.w3c.dom.Element + +@Suppress("unused") +internal val addManifestPermissionsPatch = resourcePatch { + + val requiredPermissions = listOf( + "android.permission.READ_PHONE_STATE", + "android.permission.FOREGROUND_SERVICE_MICROPHONE", + "android.permission.RECORD_AUDIO", + ) + + execute { + document("AndroidManifest.xml").use { document -> + document.getElementsByTagName("manifest").item(0).let { manifestEl -> + + // Check which permissions are missing + val existingPermissionNames = document.getElementsByTagName("uses-permission").asSequence() + .mapNotNull { (it as? Element)?.getAttribute("android:name") }.toSet() + val missingPermissions = requiredPermissions.filterNot { it in existingPermissionNames } + + // Then add them + for (permission in missingPermissions) { + val element = document.createElement("uses-permission") + element.setAttribute("android:name", permission) + manifestEl.appendChild(element) + } + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt new file mode 100644 index 000000000..cefa1ce32 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.samsung.radio.misc.fix.crash + +import app.revanced.patcher.fingerprint + +internal val permissionRequestListFingerprint by fingerprint { + strings( + "android.permission.POST_NOTIFICATIONS", + "android.permission.READ_MEDIA_AUDIO", + "android.permission.RECORD_AUDIO" + ) + custom { method, _ -> method.name == "" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt new file mode 100644 index 000000000..a076ca830 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt @@ -0,0 +1,42 @@ +@file:Suppress("unused") + +package app.revanced.patches.samsung.radio.misc.fix.crash + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.samsung.radio.restrictions.device.bypassDeviceChecksPatch +import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch;" + +val fixCrashPatch = bytecodePatch( + name = "Fix crashes", description = "Prevents the app from crashing because of missing system permissions." +) { + dependsOn(addManifestPermissionsPatch, bypassDeviceChecksPatch) + extendWith("extensions/samsung/radio.rve") + compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11")) + + execute { + permissionRequestListFingerprint.method.apply { + findInstructionIndicesReversedOrThrow(Opcode.FILLED_NEW_ARRAY).forEach { filledNewArrayIndex -> + val moveResultIndex = indexOfFirstInstruction(filledNewArrayIndex, Opcode.MOVE_RESULT_OBJECT) + if (moveResultIndex < 0) return@forEach // No move-result-object found after the filled-new-array + + // Get the register where the array is saved + val arrayRegister = getInstruction(moveResultIndex).registerA + + // Invoke the method from the extension + addInstructions( + moveResultIndex + 1, """ + invoke-static { v$arrayRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->fixPermissionRequestList([Ljava/lang/String;)[Ljava/lang/String; + move-result-object v$arrayRegister + """ + ) + } + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt new file mode 100644 index 000000000..6a1ae6551 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt @@ -0,0 +1,55 @@ +package app.revanced.patches.samsung.radio.restrictions.device + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.findFreeRegister +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch;" + +@Suppress("unused") +val bypassDeviceChecksPatch = bytecodePatch( + name = "Bypass device checks", + description = "Removes firmware and region blacklisting. " + + "This patch will still not allow the app to run on devices that do not have the required hardware.", +) { + extendWith("extensions/samsung/radio.rve") + compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11")) + + execute { + // Return false = The device is not blacklisted. + checkDeviceFingerprint.method.apply { + // Find the first string that start with "SM-", that's the list of incompatible devices. + val firstStringIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.CONST_STRING && + getReference()?.string?.startsWith("SM-") == true + } + + // Find the following filled-new-array (or filled-new-array/range) instruction. + val filledNewArrayIndex = indexOfFirstInstructionOrThrow(firstStringIndex + 1) { + opcode == Opcode.FILLED_NEW_ARRAY || opcode == Opcode.FILLED_NEW_ARRAY_RANGE + } + + val resultRegister = findFreeRegister(filledNewArrayIndex + 1) + + // Store the array there and invoke the method that we added to the class earlier. + addInstructions( + filledNewArrayIndex + 1, + """ + move-result-object v$resultRegister + invoke-static { v$resultRegister }, $EXTENSION_CLASS_DESCRIPTOR->checkIfDeviceIsIncompatible([Ljava/lang/String;)Z + move-result v$resultRegister + return v$resultRegister + """ + ) + + // Remove the instructions before our strings. + removeInstructions(0, firstStringIndex) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt new file mode 100644 index 000000000..e5278adee --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt @@ -0,0 +1,61 @@ +package app.revanced.patches.samsung.radio.restrictions.device + +import app.revanced.patcher.fingerprint +import app.revanced.patches.all.misc.transformation.IMethodCall +import app.revanced.patches.all.misc.transformation.fromMethodReference +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val checkDeviceFingerprint by fingerprint { + returns("Z") + custom { method, _ -> + /* Check for methods call to: + - Landroid/os/SemSystemProperties;->getSalesCode()Ljava/lang/String; + - Landroid/os/SemSystemProperties;->getCountryIso()Ljava/lang/String; + */ + + val impl = method.implementation ?: return@custom false + + // Track which target methods we've found + val foundMethods = mutableSetOf() + + // Scan method instructions for calls to our target methods + for (instr in impl.instructions) { + val ref = instr.getReference() ?: continue + val mc = fromMethodReference(ref) ?: continue + + if (mc == MethodCall.GetSalesCode || mc == MethodCall.GetCountryIso) { + foundMethods.add(mc) + + // If we found both methods, return success + if (foundMethods.size == 2) { + return@custom true + } + } + } + + // Only match if both methods are present + return@custom false + } +} + +// Information about method calls we want to replace +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + GetSalesCode( + "Landroid/os/SemSystemProperties;", + "getSalesCode", + arrayOf(), + "Ljava/lang/String;", + ), + GetCountryIso( + "Landroid/os/SemSystemProperties;", + "getCountryIso", + arrayOf(), + "Ljava/lang/String;", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index f0dda3446..518e22bfc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -1,14 +1,11 @@ package app.revanced.patches.youtube.layout.seekbar import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.fingerprint -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.shared.layout.theme.lithoColorHookPatch @@ -16,21 +13,15 @@ import app.revanced.patches.shared.layout.theme.lithoColorOverrideHook import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater -import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_49_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_30_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint -import app.revanced.util.copyXmlNode -import app.revanced.util.findElementByAttributeValueOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.inputStreamFromBundledResource import app.revanced.util.insertLiteralOverride import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -38,125 +29,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter -import org.w3c.dom.Element -import java.io.ByteArrayInputStream -import kotlin.use - -private const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color" - -private val seekbarColorResourcePatch = resourcePatch { - dependsOn( - resourceMappingPatch, - versionCheckPatch, - ) - - execute { - // Modify the resume playback drawable and replace the progress bar with a custom drawable. - document("res/drawable/resume_playback_progressbar_drawable.xml").use { document -> - val layerList = document.getElementsByTagName("layer-list").item(0) as Element - val progressNode = layerList.getElementsByTagName("item").item(1) as Element - if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) { - throw PatchException("Could not find progress bar") - } - val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element - val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element - val replacementNode = document.createElement( - "app.revanced.extension.youtube.patches.theme.ProgressBarDrawable", - ) - scaleNode.replaceChild(replacementNode, shapeNode) - } - - // Add attribute and styles for splash screen custom color. - // Using a style is the only way to selectively change just the seekbar fill color. - // - // Because the style colors must be hard coded for all color possibilities, - // instead of allowing 24 bit color the style is restricted to 9-bit (3 bits per color channel) - // and the style color closest to the users custom color is used for the splash screen. - arrayOf( - inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! to "res/values/attrs.xml", - ByteArrayInputStream(create9BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml" - ).forEach { (source, destination) -> - "resources".copyXmlNode( - document(source), - document(destination), - ).close() - } - - fun setSplashDrawablePathFillColor(xmlFileNames: Iterable, vararg resourceNames: String) { - xmlFileNames.forEach { xmlFileName -> - document(xmlFileName).use { document -> - val childNodes = document.childNodes - - resourceNames.forEach { elementId -> - val element = childNodes.findElementByAttributeValueOrThrow( - "android:name", - elementId - ) - - val attribute = "android:fillColor" - if (!element.hasAttribute(attribute)) { - throw PatchException("Could not find $attribute for $elementId") - } - - element.setAttribute(attribute, "?attr/$splashSeekbarColorAttributeName") - } - } - } - } - - setSplashDrawablePathFillColor( - listOf( - "res/drawable/\$startup_animation_light__0.xml", - "res/drawable/\$startup_animation_dark__0.xml" - ), - "_R_G_L_10_G_D_0_P_0" - ) - - if (!is_19_46_or_greater) { - // Resources removed in 19.46+ - setSplashDrawablePathFillColor( - listOf( - "res/drawable/\$buenos_aires_animation_light__0.xml", - "res/drawable/\$buenos_aires_animation_dark__0.xml" - ), - "_R_G_L_8_G_D_0_P_0" - ) - } - } -} - -/** - * Generate a style xml with all combinations of 9-bit colors. - */ -private fun create9BitSeekbarColorStyles(): String = StringBuilder().apply { - append("") - append("\n") - - for (red in 0..7) { - for (green in 0..7) { - for (blue in 0..7) { - val name = "${red}_${green}_${blue}" - - fun roundTo3BitHex(channel8Bits: Int) = - (channel8Bits * 255 / 7).toString(16).padStart(2, '0') - val r = roundTo3BitHex(red) - val g = roundTo3BitHex(green) - val b = roundTo3BitHex(blue) - val color = "#ff$r$g$b" - - append( - """ - - """ - ) - } - } - } - - append("") -}.toString() private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/SeekbarColorPatch;" @@ -166,7 +38,6 @@ val seekbarColorPatch = bytecodePatch( dependsOn( sharedExtensionPatch, lithoColorHookPatch, - seekbarColorResourcePatch, resourceMappingPatch, versionCheckPatch ) @@ -300,21 +171,6 @@ val seekbarColorPatch = bytecodePatch( // Hook the splash animation to set the a seekbar color. mainActivityOnCreateFingerprint.method.apply { - val drawableIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Landroid/widget/ImageView;" - && reference.name == "getDrawable" - } - val checkCastIndex = indexOfFirstInstructionOrThrow(drawableIndex, Opcode.CHECK_CAST) - val drawableRegister = getInstruction(checkCastIndex).registerA - - addInstruction( - checkCastIndex + 1, - "invoke-static { v$drawableRegister }, $EXTENSION_CLASS_DESCRIPTOR->" + - "setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V" - ) - - // Replace the Lottie animation view setAnimation(int) call. val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name @@ -328,8 +184,7 @@ val seekbarColorPatch = bytecodePatch( replaceInstruction( index, "invoke-static { v${instruction.registerC}, v${instruction.registerD} }, " + - "$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie" + - "(Lcom/airbnb/lottie/LottieAnimationView;I)V" + "$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie(Lcom/airbnb/lottie/LottieAnimationView;I)V" ) } } diff --git a/patches/src/main/resources/seekbar/values/attrs.xml b/patches/src/main/resources/seekbar/values/attrs.xml deleted file mode 100644 index 2bf349f0d..000000000 --- a/patches/src/main/resources/seekbar/values/attrs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file