mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-06-02 11:26:18 +02:00
refactor
This commit is contained in:
parent
6f742c718a
commit
14527a2638
|
@ -1,19 +1,15 @@
|
|||
package app.revanced.integrations.youtube.patches;
|
||||
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.*;
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.integrations.shared.settings.BaseSettings;
|
||||
import app.revanced.integrations.shared.settings.EnumSetting;
|
||||
import app.revanced.integrations.shared.settings.Setting;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.youtube.shared.NavigationBar;
|
||||
import app.revanced.integrations.youtube.shared.PlayerType;
|
||||
|
||||
import org.chromium.net.UrlRequest;
|
||||
import org.chromium.net.UrlResponseInfo;
|
||||
import org.chromium.net.impl.CronetUrlRequest;
|
||||
|
@ -26,13 +22,12 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_HOME;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_LIBRARY;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_PLAYER;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_SEARCH;
|
||||
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_SUBSCRIPTIONS;
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.shared.settings.Setting;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.youtube.shared.NavigationBar;
|
||||
import app.revanced.integrations.youtube.shared.PlayerType;
|
||||
|
||||
/**
|
||||
* Alternative YouTube thumbnails.
|
||||
|
@ -134,11 +129,6 @@ public final class AlternativeThumbnailsPatch {
|
|||
*/
|
||||
private static volatile long timeToResumeDeArrowAPICalls;
|
||||
|
||||
/**
|
||||
* Used only for debug logging.
|
||||
*/
|
||||
private static volatile EnumSetting<ThumbnailOption> currentOptionSetting;
|
||||
|
||||
static {
|
||||
dearrowApiUri = validateSettings();
|
||||
final int port = dearrowApiUri.getPort();
|
||||
|
@ -162,26 +152,38 @@ public final class AlternativeThumbnailsPatch {
|
|||
return apiUri;
|
||||
}
|
||||
|
||||
private static EnumSetting<ThumbnailOption> optionSettingForCurrentNavigation() {
|
||||
private static ThumbnailOption optionSettingForCurrentNavigation() {
|
||||
// Must check player type first, as search bar can be active behind the player.
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||
return ALT_THUMBNAIL_PLAYER;
|
||||
return ALT_THUMBNAIL_PLAYER.get();
|
||||
}
|
||||
|
||||
// Must check second, as search can be from any tab.
|
||||
if (NavigationBar.isSearchBarActive()) {
|
||||
return ALT_THUMBNAIL_SEARCH;
|
||||
return ALT_THUMBNAIL_SEARCH.get();
|
||||
}
|
||||
|
||||
NavigationButton navButtonSelected = NavigationButton.getSelectedNavigationButton();
|
||||
if (navButtonSelected == NavigationButton.HOME) {
|
||||
return ALT_THUMBNAIL_HOME;
|
||||
// Avoid checking which navigation button is selected, if all other settings are the same.
|
||||
ThumbnailOption homeOption = ALT_THUMBNAIL_HOME.get();
|
||||
ThumbnailOption subscriptionsOption = ALT_THUMBNAIL_SUBSCRIPTIONS.get();
|
||||
ThumbnailOption libraryOption = ALT_THUMBNAIL_LIBRARY.get();
|
||||
if ((homeOption == subscriptionsOption) && (homeOption == libraryOption)) {
|
||||
return homeOption; // All are the same option.
|
||||
}
|
||||
if (navButtonSelected == NavigationButton.SUBSCRIPTIONS || navButtonSelected == NavigationButton.NOTIFICATIONS) {
|
||||
return ALT_THUMBNAIL_SUBSCRIPTIONS;
|
||||
|
||||
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
||||
if (selectedNavButton == null) {
|
||||
// Unknown tab, treat as the home tab;
|
||||
return homeOption;
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.HOME) {
|
||||
return homeOption;
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS || selectedNavButton == NavigationButton.NOTIFICATIONS) {
|
||||
return subscriptionsOption;
|
||||
}
|
||||
// A library tab variant is active.
|
||||
return ALT_THUMBNAIL_LIBRARY;
|
||||
return libraryOption;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,14 +261,7 @@ public final class AlternativeThumbnailsPatch {
|
|||
*/
|
||||
public static String overrideImageURL(String originalUrl) {
|
||||
try {
|
||||
EnumSetting<ThumbnailOption> optionSetting = optionSettingForCurrentNavigation();
|
||||
ThumbnailOption option = optionSetting.get();
|
||||
if (BaseSettings.DEBUG.get()) {
|
||||
if (currentOptionSetting != optionSetting) {
|
||||
currentOptionSetting = optionSetting;
|
||||
Logger.printDebug(() -> "Changed to setting: " + optionSetting.key);
|
||||
}
|
||||
}
|
||||
ThumbnailOption option = optionSettingForCurrentNavigation();
|
||||
|
||||
if (option == ThumbnailOption.ORIGINAL) {
|
||||
return originalUrl;
|
||||
|
|
|
@ -123,12 +123,22 @@ final class KeywordContentFilter extends Filter {
|
|||
return Settings.HIDE_KEYWORD_CONTENT_SEARCH.get();
|
||||
}
|
||||
|
||||
NavigationButton navButtonSelected = NavigationButton.getSelectedNavigationButton();
|
||||
if (navButtonSelected == NavigationButton.HOME) {
|
||||
return Settings.HIDE_KEYWORD_CONTENT_HOME.get();
|
||||
// Avoid checking navigation button status if all other settings are off.
|
||||
final boolean hideHome = Settings.HIDE_KEYWORD_CONTENT_HOME.get();
|
||||
final boolean hideSubscriptions = Settings.HIDE_SUBSCRIPTIONS_BUTTON.get();
|
||||
if (!hideHome && !hideSubscriptions) {
|
||||
return false;
|
||||
}
|
||||
if (navButtonSelected == NavigationButton.SUBSCRIPTIONS) {
|
||||
return Settings.HIDE_SUBSCRIPTIONS_BUTTON.get();
|
||||
|
||||
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
||||
if (selectedNavButton == null) {
|
||||
return hideHome; // Unknown tab, treat the same as home.
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.HOME) {
|
||||
return hideHome;
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
|
||||
return hideSubscriptions;
|
||||
}
|
||||
// User is in the Library or Notifications tab.
|
||||
return false;
|
||||
|
|
|
@ -368,14 +368,18 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
}
|
||||
|
||||
private static boolean hideShelves() {
|
||||
// If the player is opened while library is selected,
|
||||
// then still filter any recommendations below the player.
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
||||
// Or if the search is active while library is selected, then also filter.
|
||||
|| NavigationBar.isSearchBarActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check navigation button last.
|
||||
// Only filter if the library tab is not selected.
|
||||
// This check is important as the shelf layout is used for the library tab playlists.
|
||||
NavigationButton selectedButton = NavigationButton.getSelectedNavigationButton();
|
||||
return (selectedButton != null && !selectedButton.isLibraryOrYouTab())
|
||||
// But if the player is opened while library is selected,
|
||||
// then still filter any recommendations below the player.
|
||||
|| PlayerType.getCurrent().isMaximizedOrFullscreen()
|
||||
// Or if the search is active while library is selected, then also filter.
|
||||
|| NavigationBar.isSearchBarActive();
|
||||
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
||||
return selectedNavButton != null && !selectedNavButton.isLibraryOrYouTab();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,12 +231,22 @@ public final class ShortsFilter extends Filter {
|
|||
return Settings.HIDE_SHORTS_SEARCH.get();
|
||||
}
|
||||
|
||||
NavigationButton navButtonSelected = NavigationButton.getSelectedNavigationButton();
|
||||
if (navButtonSelected == NavigationButton.HOME) {
|
||||
return Settings.HIDE_SHORTS_HOME.get();
|
||||
// Avoid checking navigation button status if all other settings are off.
|
||||
final boolean hideHome = Settings.HIDE_SHORTS_HOME.get();
|
||||
final boolean hideSubscriptions = Settings.HIDE_SHORTS_SUBSCRIPTIONS.get();
|
||||
if (!hideHome && !hideSubscriptions) {
|
||||
return false;
|
||||
}
|
||||
if (navButtonSelected == NavigationButton.SUBSCRIPTIONS) {
|
||||
return Settings.HIDE_SHORTS_SUBSCRIPTIONS.get();
|
||||
|
||||
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
||||
if (selectedNavButton == null) {
|
||||
return hideHome; // Unknown tab, treat the same as home.
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.HOME) {
|
||||
return hideHome;
|
||||
}
|
||||
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
|
||||
return hideSubscriptions;
|
||||
}
|
||||
// User must be in the library tab. Don't hide the history or any playlists here.
|
||||
return false;
|
||||
|
|
|
@ -19,55 +19,12 @@ import app.revanced.integrations.youtube.settings.Settings;
|
|||
@SuppressWarnings("unused")
|
||||
public final class NavigationBar {
|
||||
|
||||
//
|
||||
// Search bar
|
||||
//
|
||||
|
||||
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
* When using the back button and the navigation button changes, the button is updated
|
||||
* a few milliseconds after litho starts creating the view. To fix this, any thread
|
||||
* calling for the current navigation button waits until this latch is released.
|
||||
*
|
||||
* The latch is also initial set, because on app startup litho can start before the navigation bar is initialized.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile CountDownLatch navButtonLatch;
|
||||
|
||||
static {
|
||||
createNavButtonLatch();
|
||||
}
|
||||
|
||||
private static void createNavButtonLatch() {
|
||||
navButtonLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
private static void releaseNavButtonLatch() {
|
||||
CountDownLatch latch = navButtonLatch;
|
||||
if (latch != null) {
|
||||
latch.countDown();
|
||||
}
|
||||
navButtonLatch = null;
|
||||
}
|
||||
|
||||
private static boolean waitForLatchIfNeed() {
|
||||
CountDownLatch latch = navButtonLatch;
|
||||
if (latch == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.printDebug(() -> "Waiting for navbar button latch");
|
||||
if (latch.await(1000, TimeUnit.MILLISECONDS)) {
|
||||
Logger.printDebug(() -> "Waiting complete");
|
||||
return true;
|
||||
}
|
||||
Logger.printDebug(() -> "Get navigation button wait timed out");
|
||||
navButtonLatch = null;
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.printException(() -> "Wait interrupted", ex); // Will never happen.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
|
@ -85,6 +42,86 @@ public final class NavigationBar {
|
|||
return searchbarResults != null && searchbarResults.getParent() != null;
|
||||
}
|
||||
|
||||
//
|
||||
// Navigation bar buttons
|
||||
//
|
||||
|
||||
/**
|
||||
* How long to wait for the set nav button latch to be released. Maximum wait time must
|
||||
* be as small as possible while still allowing enough time for the nav bar to update.
|
||||
*
|
||||
* YT calls it's back button handlers out of order, and litho starts
|
||||
* filtering the previous screen before the navigation bar is updated.
|
||||
*
|
||||
* Fixing this situation and not needlessly wait requires somehow detecting if a back button
|
||||
* key-press will cause a tab change. Typically after pressing the back button, the time
|
||||
* between the first litho event and the when the nav button is updated is about 10-20ms.
|
||||
*
|
||||
* Using 50-100ms here should be enough time and not noticeable, since YT typically takes
|
||||
* 100-200ms (or more) just to update the view anyways.
|
||||
*
|
||||
* This issue can also be avoided on a patch by patch basis, by avoiding calls to
|
||||
* {@link NavigationButton#getSelectedNavigationButton()} unless absolutely necessary.
|
||||
*/
|
||||
private static final long LATCH_AWAIT_TIMEOUT_MILLISECONDS = 50;
|
||||
|
||||
/**
|
||||
* Used as a workaround to fix the issue of YT calling back button handlers out of order.
|
||||
* Used to hold calls to {@link NavigationButton#getSelectedNavigationButton()}
|
||||
* until the current navigation button can be determined.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile CountDownLatch navButtonLatch;
|
||||
|
||||
static {
|
||||
// On app startup litho can start before the navigation bar is initialized.
|
||||
// Force it to wait until the nav bar is updated.
|
||||
createNavButtonLatch();
|
||||
}
|
||||
|
||||
private static void createNavButtonLatch() {
|
||||
navButtonLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
private static void releaseNavButtonLatch() {
|
||||
CountDownLatch latch = navButtonLatch;
|
||||
if (latch != null) {
|
||||
navButtonLatch = null;
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitForNavButtonLatchIfNeed() {
|
||||
CountDownLatch latch = navButtonLatch;
|
||||
if (latch == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils.isCurrentlyOnMainThread()) {
|
||||
// The latch is released from the main thread, and waiting from the main thread will always timeout.
|
||||
// This situation has only been observed when navigating out of a submenu and not changing tabs.
|
||||
// and for those cases the nav bar did not change anyways so it's safe to return.
|
||||
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. Using last known navbar button status.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.printDebug(() -> "Waiting for navbar button latch");
|
||||
if (latch.await(LATCH_AWAIT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS)) {
|
||||
Logger.printDebug(() -> "Latch waiting complete");
|
||||
return;
|
||||
}
|
||||
|
||||
// Timeout occurred, and a normal event when pressing the physical back button
|
||||
// does not change navigation tabs.
|
||||
releaseNavButtonLatch(); // Prevent other threads from waiting for no reason.
|
||||
Logger.printDebug(() -> "Latch wait timed out");
|
||||
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.printException(() -> "Wait interrupted", ex); // Will never happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Last YT navigation enum loaded. Not necessarily the active navigation tab.
|
||||
*/
|
||||
|
@ -108,9 +145,9 @@ public final class NavigationBar {
|
|||
String lastEnumName = lastYTNavigationEnumName;
|
||||
|
||||
for (NavigationButton button : NavigationButton.values()) {
|
||||
if (button.ytEnumName.equals(lastEnumName)) {;
|
||||
if (button.ytEnumName.equals(lastEnumName)) {
|
||||
Logger.printDebug(() -> "navigationTabLoaded: " + lastEnumName);
|
||||
button.imageViewRef = new WeakReference<>(navigationButtonGroup);
|
||||
button.buttonLayoutRef = new WeakReference<>(navigationButtonGroup);
|
||||
navigationTabCreatedCallback(button, navigationButtonGroup);
|
||||
return;
|
||||
}
|
||||
|
@ -149,17 +186,15 @@ public final class NavigationBar {
|
|||
public static void navigationTabSelected(View navButtonImageView, boolean isSelected) {
|
||||
try {
|
||||
for (NavigationButton button : NavigationButton.values()) {
|
||||
View buttonView = button.imageViewRef.get();
|
||||
View buttonView = button.buttonLayoutRef.get();
|
||||
|
||||
if (buttonView == navButtonImageView) {
|
||||
if (isSelected) {
|
||||
if (NavigationButton.selectedNavigationButton != button) {
|
||||
Logger.printDebug(() -> "Changed to navigation button: " + button);
|
||||
NavigationButton.selectedNavigationButton = button;
|
||||
}
|
||||
|
||||
NavigationButton.selectedNavigationButton = button;
|
||||
// Wake up any threads waiting to return the currently selected nav button.
|
||||
releaseNavButtonLatch();
|
||||
|
||||
Logger.printDebug(() -> "Changed to navigation button: " + button);
|
||||
} else if (NavigationButton.selectedNavigationButton == button) {
|
||||
NavigationButton.selectedNavigationButton = null;
|
||||
Logger.printDebug(() -> "Navigated away from button: " + button);
|
||||
|
@ -241,23 +276,24 @@ public final class NavigationBar {
|
|||
*
|
||||
* All code calling this method should handle a null return value.
|
||||
*
|
||||
* <b>Due to issues with how YT processes hardware back button events,
|
||||
* this patch uses workarounds that can cause this method to take up to 50ms.</b>
|
||||
*
|
||||
* @return The active navigation tab.
|
||||
* If the user is in the upload video UI, this returns tab currently selected
|
||||
* on screen (whatever tab the user was on before tapping the upload nav button).
|
||||
*/
|
||||
@Nullable
|
||||
public static NavigationButton getSelectedNavigationButton() {
|
||||
if (waitForLatchIfNeed()) {
|
||||
return selectedNavigationButton;
|
||||
}
|
||||
return null; // Latch wait timed out, and it's unclear which tab is selected.
|
||||
waitForNavButtonLatchIfNeed();
|
||||
return selectedNavigationButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* YouTube enum name for this tab.
|
||||
*/
|
||||
private final String ytEnumName;
|
||||
private volatile WeakReference<View> imageViewRef = new WeakReference<>(null);
|
||||
private volatile WeakReference<View> buttonLayoutRef = new WeakReference<>(null);
|
||||
|
||||
NavigationButton(String ytEnumName) {
|
||||
this.ytEnumName = ytEnumName;
|
||||
|
|
Loading…
Reference in New Issue
Block a user