chore: Merge branch dev to main (#650)

This commit is contained in:
oSumAtrIX 2024-06-23 14:11:58 +02:00 committed by GitHub
commit fac49c7c10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 675 additions and 116 deletions

View File

@ -1,3 +1,73 @@
# [1.11.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.6...v1.11.0-dev.7) (2024-06-17)
### Bug Fixes
* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#654](https://github.com/ReVanced/revanced-integrations/issues/654)) ([f82dfce](https://github.com/ReVanced/revanced-integrations/commit/f82dfce887bbeccbfdb8e8f8d3d84db6ce28539a))
# [1.11.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.5...v1.11.0-dev.6) (2024-06-17)
### Features
* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#656](https://github.com/ReVanced/revanced-integrations/issues/656)) ([064d8e9](https://github.com/ReVanced/revanced-integrations/commit/064d8e99a96167282f63725d33502f251632dcdb))
# [1.11.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.4...v1.11.0-dev.5) (2024-06-09)
### Features
* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#653](https://github.com/ReVanced/revanced-integrations/issues/653)) ([240e805](https://github.com/ReVanced/revanced-integrations/commit/240e805489e3603df026a7a3cf78b939461f5f9e))
# [1.11.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.3...v1.11.0-dev.4) (2024-06-09)
### Bug Fixes
* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#652](https://github.com/ReVanced/revanced-integrations/issues/652)) ([58f8172](https://github.com/ReVanced/revanced-integrations/commit/58f8172b2d30fecd31d541f7470abf61b7e303c7))
# [1.11.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.2...v1.11.0-dev.3) (2024-06-08)
### Features
* **Boost For Reddit:** Add `Fix /s/ links` patch ([#631](https://github.com/ReVanced/revanced-integrations/issues/631)) ([0c9ad35](https://github.com/ReVanced/revanced-integrations/commit/0c9ad35fc9024efe33c97b51714225c5d5226f59))
# [1.11.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.11.0-dev.1...v1.11.0-dev.2) (2024-06-08)
### Bug Fixes
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#651](https://github.com/ReVanced/revanced-integrations/issues/651)) ([84c50c0](https://github.com/ReVanced/revanced-integrations/commit/84c50c080c2a8c096b7709164cbaec5466a8492c))
# [1.11.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v1.10.1-dev.3...v1.11.0-dev.1) (2024-06-07)
### Features
* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#649](https://github.com/ReVanced/revanced-integrations/issues/649)) ([f483af6](https://github.com/ReVanced/revanced-integrations/commit/f483af6d3a2c1003cd566b4bf36fa4a257d1d6d3))
## [1.10.1-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.10.1-dev.2...v1.10.1-dev.3) (2024-06-07)
### Bug Fixes
* **YouTube - Spoof client:** Correctly play as background audio only with Android VR ([9adbc66](https://github.com/ReVanced/revanced-integrations/commit/9adbc66197e8342d3321dc77d151c648be65595b))
## [1.10.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.10.1-dev.1...v1.10.1-dev.2) (2024-06-06)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Do not replace view count with dislikes ([5f79196](https://github.com/ReVanced/revanced-integrations/commit/5f7919669250e95fc47c1a2705b352b53aa226f6))
## [1.10.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v1.10.0...v1.10.1-dev.1) (2024-06-06)
### Bug Fixes
* **YouTube - Spoof client:** Correctly play some live streams when using Android VR ([f74fb17](https://github.com/ReVanced/revanced-integrations/commit/f74fb17a127b1ed8094db23a04e228d8abd05440))
# [1.10.0](https://github.com/ReVanced/revanced-integrations/compare/v1.9.2...v1.10.0) (2024-06-02)

View File

@ -0,0 +1,24 @@
package app.revanced.integrations.boostforreddit;
import com.rubenmayayo.reddit.ui.activities.WebViewActivity;
import app.revanced.integrations.shared.fixes.slink.BaseFixSLinksPatch;
/** @noinspection unused*/
public class FixSLinksPatch extends BaseFixSLinksPatch {
static {
INSTANCE = new FixSLinksPatch();
}
private FixSLinksPatch() {
webViewActivityClass = WebViewActivity.class;
}
public static boolean patchResolveSLink(String link) {
return INSTANCE.resolveSLink(link);
}
public static void patchSetAccessToken(String accessToken) {
INSTANCE.setAccessToken(accessToken);
}
}

View File

@ -89,6 +89,7 @@ public class Utils {
return versionName;
}
/**
* Hide a view by setting its layout height and width to 1dp.
*
@ -96,11 +97,24 @@ public class Utils {
* @param view The view to hide.
*/
public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View view) {
if (!condition.get()) return;
if (hideViewBy0dpUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
}
}
Logger.printDebug(() -> "Hiding view with setting: " + condition);
/**
* Hide a view by setting its layout height and width to 1dp.
*
* @param condition The setting to check for hiding the view.
* @param view The view to hide.
*/
public static boolean hideViewBy0dpUnderCondition(boolean condition, View view) {
if (condition) {
hideViewByLayoutParams(view);
return true;
}
hideViewByLayoutParams(view);
return false;
}
/**
@ -110,20 +124,42 @@ public class Utils {
* @param view The view to hide.
*/
public static void hideViewUnderCondition(BooleanSetting condition, View view) {
if (!condition.get()) return;
Logger.printDebug(() -> "Hiding view with setting: " + condition);
view.setVisibility(View.GONE);
if (hideViewUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
}
}
public static void removeViewFromParentUnderConditions(BooleanSetting setting, View view) {
if (setting.get()) {
/**
* Hide a view by setting its visibility to GONE.
*
* @param condition The setting to check for hiding the view.
* @param view The view to hide.
*/
public static boolean hideViewUnderCondition(boolean condition, View view) {
if (condition) {
view.setVisibility(View.GONE);
return true;
}
return false;
}
public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting condition, View view) {
if (hideViewByRemovingFromParentUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
}
}
public static boolean hideViewByRemovingFromParentUnderCondition(boolean setting, View view) {
if (setting) {
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(view);
return true;
}
}
return false;
}
/**
@ -236,6 +272,8 @@ public class Utils {
@NonNull MatchFilter<View> filter) {
for (int i = 0, childCount = viewGroup.getChildCount(); i < childCount; i++) {
View childAt = viewGroup.getChildAt(i);
Logger.printDebug(() -> "View id: " + childAt.getId() + " tag: " + childAt.getTag());
if (filter.matches(childAt)) {
//noinspection unchecked
return (T) childAt;

View File

@ -0,0 +1,208 @@
package app.revanced.integrations.shared.fixes.slink;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Objects;
import static app.revanced.integrations.shared.Utils.getContext;
/**
* Base class to implement /s/ link resolution in 3rd party Reddit apps.
* <br>
* <br>
* Usage:
* <br>
* <br>
* An implementation of this class must have two static methods that are called by the app:
* <ul>
* <li>public static boolean patchResolveSLink(String link)</li>
* <li>public static void patchSetAccessToken(String accessToken)</li>
* </ul>
* The static methods must call the instance methods of the base class.
* <br>
* The singleton pattern can be used to access the instance of the class:
* <pre>
* {@code
* {
* INSTANCE = new FixSLinksPatch();
* }
* }
* </pre>
* Set the app's web view activity class as a fallback to open /s/ links if the resolution fails:
* <pre>
* {@code
* private FixSLinksPatch() {
* webViewActivityClass = WebViewActivity.class;
* }
* }
* </pre>
* Hook the app's navigation handler to call this method before doing any of its own resolution:
* <pre>
* {@code
* public static boolean patchResolveSLink(Context context, String link) {
* return INSTANCE.resolveSLink(context, link);
* }
* }
* </pre>
* If this method returns true, the app should early return and not do any of its own resolution.
* <br>
* <br>
* Hook the app's access token so that this class can use it to resolve /s/ links:
* <pre>
* {@code
* public static void patchSetAccessToken(String accessToken) {
* INSTANCE.setAccessToken(access_token);
* }
* }
* </pre>
*/
public abstract class BaseFixSLinksPatch {
/**
* The class of the activity used to open links in a web view if resolving them fails.
*/
protected Class<? extends Activity> webViewActivityClass;
/**
* The access token used to resolve the /s/ link.
*/
protected String accessToken;
/**
* The URL that was trying to be resolved before the access token was set.
* If this is not null, the URL will be resolved right after the access token is set.
*/
protected String pendingUrl;
/**
* The singleton instance of the class.
*/
protected static BaseFixSLinksPatch INSTANCE;
public boolean resolveSLink(String link) {
switch (resolveLink(link)) {
case ACCESS_TOKEN_START: {
pendingUrl = link;
return true;
}
case DO_NOTHING:
return true;
default:
return false;
}
}
private ResolveResult resolveLink(String link) {
Context context = getContext();
if (link.matches(".*reddit\\.com/r/[^/]+/s/[^/]+")) {
// A link ends with #bypass if it failed to resolve below.
// resolveLink is called with the same link again but this time with #bypass
// so that the link is opened in the app browser instead of trying to resolve it again.
if (link.endsWith("#bypass")) {
openInAppBrowser(context, link);
return ResolveResult.DO_NOTHING;
}
Logger.printDebug(() -> "Resolving " + link);
if (accessToken == null) {
// This is not optimal.
// However, an accessToken is necessary to make an authenticated request to Reddit.
// in case Reddit has banned the IP - e.g. VPN.
Intent startIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
context.startActivity(startIntent);
return ResolveResult.ACCESS_TOKEN_START;
}
Utils.runOnBackgroundThread(() -> {
String bypassLink = link + "#bypass";
String finalLocation = bypassLink;
try {
HttpURLConnection connection = getHttpURLConnection(link, accessToken);
connection.connect();
String location = connection.getHeaderField("location");
connection.disconnect();
Objects.requireNonNull(location, "Location is null");
finalLocation = location;
Logger.printDebug(() -> "Resolved " + link + " to " + location);
} catch (SocketTimeoutException e) {
Logger.printException(() -> "Timeout when trying to resolve " + link, e);
finalLocation = bypassLink;
} catch (Exception e) {
Logger.printException(() -> "Failed to resolve " + link, e);
finalLocation = bypassLink;
} finally {
Intent startIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(finalLocation));
startIntent.setPackage(context.getPackageName());
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startIntent);
}
});
return ResolveResult.DO_NOTHING;
}
return ResolveResult.CONTINUE;
}
public void setAccessToken(String accessToken) {
Logger.printDebug(() -> "Setting access token");
this.accessToken = accessToken;
// In case a link was trying to be resolved before access token was set.
// The link is resolved now, after the access token is set.
if (pendingUrl != null) {
String link = pendingUrl;
pendingUrl = null;
Logger.printDebug(() -> "Opening pending URL");
resolveLink(link);
}
}
private void openInAppBrowser(Context context, String link) {
Intent intent = new Intent(context, webViewActivityClass);
intent.putExtra("url", link);
context.startActivity(intent);
}
@NonNull
private HttpURLConnection getHttpURLConnection(String link, String accessToken) throws IOException {
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("HEAD");
connection.setConnectTimeout(2000);
connection.setReadTimeout(2000);
if (accessToken != null) {
Logger.printDebug(() -> "Setting access token to make /s/ request");
connection.setRequestProperty("Authorization", "Bearer " + accessToken);
} else {
Logger.printDebug(() -> "Not setting access token to make /s/ request, because it is null");
}
return connection;
}
}

View File

@ -0,0 +1,10 @@
package app.revanced.integrations.shared.fixes.slink;
public enum ResolveResult {
// Let app handle rest of stuff
CONTINUE,
// Start app, to make it cache its access_token
ACCESS_TOKEN_START,
// Don't do anything - we started resolving
DO_NOTHING
}

View File

@ -2,13 +2,15 @@ package app.revanced.integrations.shared.settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.shared.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Locale;
import java.util.Objects;
import app.revanced.integrations.shared.Logger;
/**
* If an Enum value is removed or changed, any saved or imported data using the
* non-existent value will be reverted to the default value
@ -98,4 +100,18 @@ public class EnumSetting<T extends Enum<?>> extends Setting<T> {
public T get() {
return value;
}
/**
* Availability based on if this setting is currently set to any of the provided types.
*/
@SafeVarargs
public final Setting.Availability availability(@NonNull T... types) {
return () -> {
T currentEnumType = get();
for (T enumType : types) {
if (currentEnumType == enumType) return true;
}
return false;
};
}
}

View File

@ -1,42 +1,24 @@
package app.revanced.integrations.syncforreddit;
import android.os.StrictMode;
import app.revanced.integrations.shared.Logger;
import com.laurencedawson.reddit_sync.ui.activities.WebViewActivity;
import java.net.HttpURLConnection;
import java.net.URL;
import app.revanced.integrations.shared.fixes.slink.BaseFixSLinksPatch;
public final class FixSLinksPatch {
public static String resolveSLink(String link) {
if (link.matches(".*reddit\\.com/r/[^/]+/s/[^/]+")) {
Logger.printInfo(() -> "Resolving " + link);
try {
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("HEAD");
/** @noinspection unused*/
public class FixSLinksPatch extends BaseFixSLinksPatch {
static {
INSTANCE = new FixSLinksPatch();
}
// Disable strict mode in order to allow network access on the main thread.
// This is not ideal, but it's the easiest solution for now.
final var currentPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
private FixSLinksPatch() {
webViewActivityClass = WebViewActivity.class;
}
connection.connect();
String location = connection.getHeaderField("location");
connection.disconnect();
public static boolean patchResolveSLink(String link) {
return INSTANCE.resolveSLink(link);
}
// Restore the original strict mode policy.
StrictMode.setThreadPolicy(currentPolicy);
Logger.printInfo(() -> "Resolved " + link + " -> " + location);
return location;
} catch (Exception e) {
Logger.printException(() -> "Failed to resolve " + link, e);
}
}
return link;
public static void patchSetAccessToken(String accessToken) {
INSTANCE.setAccessToken(accessToken);
}
}

View File

@ -6,7 +6,7 @@ import app.revanced.integrations.tiktok.settings.Settings;
@SuppressWarnings("unused")
public class SpoofSimPatch {
private static final Boolean ENABLED = Settings.SIM_SPOOF.get();
private static final boolean ENABLED = Settings.SIM_SPOOF.get();
public static String getCountryIso(String value) {
if (ENABLED) {

View File

@ -3,7 +3,7 @@ package app.revanced.integrations.youtube.patches;
import app.revanced.integrations.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class MinimizedPlaybackPatch {
public class BackgroundPlaybackPatch {
/**
* Injection point.
@ -35,7 +35,7 @@ public class MinimizedPlaybackPatch {
/**
* Injection point.
*/
public static boolean overrideMinimizedPlaybackAvailable() {
public static boolean overrideBackgroundPlaybackAvailable() {
// This could be done entirely in the patch,
// but having a unique method to search for makes manually inspecting the patched apk much easier.
return true;

View File

@ -1,22 +1,33 @@
package app.revanced.integrations.youtube.patches;
import static app.revanced.integrations.shared.StringRef.str;
import android.widget.ImageView;
import app.revanced.integrations.youtube.settings.Settings;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public class CustomPlayerOverlayOpacityPatch {
public static void changeOpacity(ImageView imageView) {
private static final int PLAYER_OVERLAY_OPACITY_LEVEL;
static {
int opacity = Settings.PLAYER_OVERLAY_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong("Player overlay opacity must be between 0-100");
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue;
}
imageView.setImageAlpha((opacity * 255) / 100);
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;
}
/**
* Injection point.
*/
public static void changeOpacity(ImageView imageView) {
imageView.setImageAlpha(PLAYER_OVERLAY_OPACITY_LEVEL);
}
}

View File

@ -1,10 +0,0 @@
package app.revanced.integrations.youtube.patches;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class EnableTabletLayoutPatch {
public static boolean enableTabletLayout() {
return Settings.TABLET_LAYOUT.get();
}
}

View File

@ -5,7 +5,7 @@ import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public class HideAutoplayButtonPatch {
private static final Boolean HIDE_AUTOPLAY_BUTTON_ENABLED = Settings.HIDE_AUTOPLAY_BUTTON.get();
private static final boolean HIDE_AUTOPLAY_BUTTON_ENABLED = Settings.HIDE_AUTOPLAY_BUTTON.get();
/**
* Injection point.

View File

@ -0,0 +1,166 @@
package app.revanced.integrations.youtube.patches;
import static app.revanced.integrations.shared.StringRef.str;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.*;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class MiniplayerPatch {
/**
* Mini player type. Null fields indicates to use the original un-patched value.
*/
public enum MiniplayerType {
/** Unmodified type, and same as un-patched. */
ORIGINAL(null, null),
PHONE(false, null),
TABLET(true, null),
MODERN_1(null, 1),
MODERN_2(null, 2),
MODERN_3(null, 3);
/**
* Legacy tablet hook value.
*/
@Nullable
final Boolean legacyTabletOverride;
/**
* Modern player type used by YT.
*/
@Nullable
final Integer modernPlayerType;
MiniplayerType(@Nullable Boolean legacyTabletOverride, @Nullable Integer modernPlayerType) {
this.legacyTabletOverride = legacyTabletOverride;
this.modernPlayerType = modernPlayerType;
}
public boolean isModern() {
return modernPlayerType != null;
}
}
/**
* Modern subtitle overlay for {@link MiniplayerType#MODERN_2}.
* Resource is not present in older targets, and this field will be zero.
*/
private static final int MODERN_OVERLAY_SUBTITLE_TEXT
= Utils.getResourceIdentifier("modern_miniplayer_subtitle_text", "id");
private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get();
private static final boolean HIDE_EXPAND_CLOSE_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get();
private static final boolean HIDE_SUBTEXT_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
private static final boolean HIDE_REWIND_FORWARD_ENABLED =
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
private static final int OPACITY_LEVEL;
static {
int opacity = Settings.MINIPLAYER_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;
}
/**
* Injection point.
*/
public static boolean getLegacyTabletMiniplayerOverride(boolean original) {
Boolean isTablet = CURRENT_TYPE.legacyTabletOverride;
return isTablet == null
? original
: isTablet;
}
/**
* Injection point.
*/
public static boolean getModernMiniplayerOverride(boolean original) {
return CURRENT_TYPE == ORIGINAL
? original
: CURRENT_TYPE.isModern();
}
/**
* Injection point.
*/
public static int getModernMiniplayerOverrideType(int original) {
Integer modernValue = CURRENT_TYPE.modernPlayerType;
return modernValue == null
? original
: modernValue;
}
/**
* Injection point.
*/
public static void adjustMiniplayerOpacity(ImageView view) {
if (CURRENT_TYPE == MODERN_1) {
view.setImageAlpha(OPACITY_LEVEL);
}
}
/**
* Injection point.
*/
public static void hideMiniplayerExpandClose(ImageView view) {
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_EXPAND_CLOSE_ENABLED, view);
}
/**
* Injection point.
*/
public static void hideMiniplayerRewindForward(ImageView view) {
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_REWIND_FORWARD_ENABLED, view);
}
/**
* Injection point.
*/
public static void hideMiniplayerSubTexts(View view) {
// Different subviews are passed in, but only TextView and layouts are of interest here.
final boolean hideView = HIDE_SUBTEXT_ENABLED && (view instanceof TextView || view instanceof LinearLayout);
Utils.hideViewByRemovingFromParentUnderCondition(hideView, view);
}
/**
* Injection point.
*/
public static void playerOverlayGroupCreated(View group) {
// Modern 2 has an half broken subtitle that is always present.
// Always hide it to make the miniplayer mostly usable.
if (CURRENT_TYPE == MODERN_2 && MODERN_OVERLAY_SUBTITLE_TEXT != 0) {
if (group instanceof ViewGroup) {
View subtitleText = Utils.getChildView((ViewGroup) group, true,
view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT);
if (subtitleText != null) {
subtitleText.setVisibility(View.GONE);
Logger.printDebug(() -> "Modern overlay subtitle view set to hidden");
}
}
}
}
}

View File

@ -23,7 +23,7 @@ public final class NavigationButtonsPatch {
}
};
private static final Boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
/**

View File

@ -221,6 +221,10 @@ public class ReturnYouTubeDislikePatch {
String conversionContextString = conversionContext.toString();
if (isRollingNumber && !conversionContextString.contains("video_action_bar.eml|")) {
return original;
}
final CharSequence replacement;
if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) {
// Regular video.
@ -289,6 +293,7 @@ public class ReturnYouTubeDislikePatch {
@NonNull String original) {
try {
CharSequence replacement = onLithoTextLoaded(conversionContext, original, true);
String replacementString = replacement.toString();
if (!replacementString.equals(original)) {
rollingNumberSpan = replacement;

View File

@ -0,0 +1,16 @@
package app.revanced.integrations.youtube.patches;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class TabletLayoutPatch {
private static final boolean TABLET_LAYOUT_ENABLED = Settings.TABLET_LAYOUT.get();
/**
* Injection point.
*/
public static boolean getTabletLayoutEnabled() {
return TABLET_LAYOUT_ENABLED;
}
}

View File

@ -1,13 +0,0 @@
package app.revanced.integrations.youtube.patches;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public class TabletMiniPlayerOverridePatch {
public static boolean getTabletMiniPlayerOverride(boolean original) {
if (Settings.USE_TABLET_MINIPLAYER.get())
return true;
return original;
}
}

View File

@ -14,21 +14,37 @@ final class CommentsFilter extends Filter {
private final ByteArrayFilterGroup emojiPickerBufferGroup;
public CommentsFilter() {
var commentsByMembers = new StringFilterGroup(
Settings.HIDE_COMMENTS_BY_MEMBERS_HEADER,
"sponsorships_comments_header.eml",
"sponsorships_comments_footer.eml"
);
var comments = new StringFilterGroup(
Settings.HIDE_COMMENTS_SECTION,
"video_metadata_carousel",
"_comments"
);
var createAShort = new StringFilterGroup(
Settings.HIDE_COMMENTS_CREATE_A_SHORT_BUTTON,
"composer_short_creation_button.eml"
);
var previewComment = new StringFilterGroup(
Settings.HIDE_PREVIEW_COMMENT,
Settings.HIDE_COMMENTS_PREVIEW_COMMENT,
"|carousel_item",
"comments_entry_point_teaser",
"comments_entry_point_simplebox"
);
var thanksButton = new StringFilterGroup(
Settings.HIDE_COMMENTS_THANKS_BUTTON,
"super_thanks_button.eml"
);
commentComposer = new StringFilterGroup(
Settings.HIDE_COMMENT_TIMESTAMP_AND_EMOJI_BUTTONS,
Settings.HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS,
"comment_composer.eml"
);
@ -38,8 +54,11 @@ final class CommentsFilter extends Filter {
);
addPathCallbacks(
commentsByMembers,
comments,
createAShort,
previewComment,
thanksButton,
commentComposer
);
}

View File

@ -18,8 +18,15 @@ final class DescriptionComponentsFilter extends Filter {
"metadata"
);
final StringFilterGroup chapterSection = new StringFilterGroup(
Settings.HIDE_CHAPTERS,
final StringFilterGroup attributesSection = new StringFilterGroup(
Settings.HIDE_ATTRIBUTES_SECTION,
"gaming_section",
"music_section",
"video_attributes_section"
);
final StringFilterGroup chaptersSection = new StringFilterGroup(
Settings.HIDE_CHAPTERS_SECTION,
"macro_markers_carousel"
);
@ -28,17 +35,6 @@ final class DescriptionComponentsFilter extends Filter {
"infocards_section"
);
final StringFilterGroup gameSection = new StringFilterGroup(
Settings.HIDE_GAME_SECTION,
"gaming_section"
);
final StringFilterGroup musicSection = new StringFilterGroup(
Settings.HIDE_MUSIC_SECTION,
"music_section",
"video_attributes_section"
);
final StringFilterGroup podcastSection = new StringFilterGroup(
Settings.HIDE_PODCAST_SECTION,
"playlist_section"
@ -50,10 +46,9 @@ final class DescriptionComponentsFilter extends Filter {
);
addPathCallbacks(
chapterSection,
attributesSection,
chaptersSection,
infoCardsSection,
gameSection,
musicSection,
podcastSection,
transcriptSection
);

View File

@ -1,7 +1,6 @@
package app.revanced.integrations.youtube.patches.components;
import static app.revanced.integrations.shared.Utils.hideViewUnderCondition;
import static app.revanced.integrations.shared.Utils.removeViewFromParentUnderConditions;
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
@ -326,11 +325,11 @@ public final class ShortsFilter extends Filter {
// the button was (only relevant for dislikes button).
//
// Instead remove the view from the parent.
removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
}
public static void hideDislikeButton(final View dislikeButtonView) {
removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
}
public static void hideShortsCommentsButton(final View commentsButtonView) {

View File

@ -128,9 +128,8 @@ public class SpoofClientPatch {
}
private enum ClientType {
// https://dumps.tadiphone.dev/dumps/oculus/monterey/-/blob/vr_monterey-user-7.1.1-NGI77B-256550.6810.0-release-keys/system/system/build.prop
// version 1.37 is not the latest, but it works with livestream audio only playback.
ANDROID_VR(28, "Quest", "1.37"),
// https://dumps.tadiphone.dev/dumps/oculus/eureka
ANDROID_VR(28, "Quest 3", "1.56.21"),
// 11,4 = iPhone XS Max.
// 16,2 = iPhone 15 Pro Max.
// Since the 15 supports AV1 hardware decoding, only spoof that device if this

View File

@ -1,5 +1,17 @@
package app.revanced.integrations.youtube.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.integrations.shared.settings.Setting.*;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_1;
import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
import static app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.settings.*;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
@ -10,15 +22,7 @@ import app.revanced.integrations.youtube.patches.AlternativeThumbnailsPatch.Thum
import app.revanced.integrations.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static app.revanced.integrations.shared.settings.Setting.*;
import static app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour.*;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
@SuppressWarnings("deprecation")
public class Settings extends BaseSettings {
// Video
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
@ -129,6 +133,13 @@ public class Settings extends BaseSettings {
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
// Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true);
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
// External downloader
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
@ -136,15 +147,17 @@ public class Settings extends BaseSettings {
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
// Comments
public static final BooleanSetting HIDE_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_preview_comment", FALSE);
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
public static final BooleanSetting HIDE_COMMENT_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comment_timestamp_and_emoji_buttons", TRUE);
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
// Description
public static final BooleanSetting HIDE_CHAPTERS = new BooleanSetting("revanced_hide_chapters", TRUE);
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
public static final BooleanSetting HIDE_INFO_CARDS_SECTION = new BooleanSetting("revanced_hide_info_cards_section", TRUE);
public static final BooleanSetting HIDE_GAME_SECTION = new BooleanSetting("revanced_hide_game_section", TRUE);
public static final BooleanSetting HIDE_MUSIC_SECTION = new BooleanSetting("revanced_hide_music_section", TRUE);
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
@ -175,7 +188,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.33.42", true, parent(SPOOF_APP_VERSION));
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
public static final BooleanSetting USE_TABLET_MINIPLAYER = new BooleanSetting("revanced_tablet_miniplayer", FALSE, true);
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
"revanced_remove_viewer_discretion_dialog_user_dialog_message");

View File

@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
android.useAndroidX = true
version = 1.10.0
version = 1.11.0-dev.7

View File

@ -0,0 +1,6 @@
package com.laurencedawson.reddit_sync.ui.activities;
import android.app.Activity;
public class WebViewActivity extends Activity {
}

View File

@ -0,0 +1,6 @@
package com.rubenmayayo.reddit.ui.activities;
import android.app.Activity;
public class WebViewActivity extends Activity {
}