186 lines
6.6 KiB
Java
186 lines
6.6 KiB
Java
package app.revanced.integrations.youtube.shared;
|
|
|
|
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
|
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.ImageView;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
|
|
import app.revanced.integrations.shared.Logger;
|
|
import app.revanced.integrations.shared.Utils;
|
|
import app.revanced.integrations.youtube.settings.Settings;
|
|
|
|
@SuppressWarnings("unused")
|
|
public final class NavigationBar {
|
|
|
|
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
|
|
|
|
/**
|
|
* Injection point.
|
|
*/
|
|
public static void searchBarResultsViewLoaded(View searchbarResults) {
|
|
searchBarResultsRef = new WeakReference<>(searchbarResults);
|
|
}
|
|
|
|
/**
|
|
* @return If the search bar is on screen. This includes if the player
|
|
* is on screen and the search results are behind the player (and not visible).
|
|
* Detecting the search is covered by the player can be done by checking {@link PlayerType#isMaximizedOrFullscreen()}.
|
|
*/
|
|
public static boolean isSearchBarActive() {
|
|
View searchbarResults = searchBarResultsRef.get();
|
|
return searchbarResults != null && searchbarResults.getParent() != null;
|
|
}
|
|
|
|
/**
|
|
* Last YT navigation enum loaded. Not necessarily the active navigation tab.
|
|
*/
|
|
@Nullable
|
|
private static volatile String lastYTNavigationEnumName;
|
|
|
|
/**
|
|
* Injection point.
|
|
*/
|
|
public static void setLastAppNavigationEnum(@Nullable Enum<?> ytNavigationEnumName) {
|
|
if (ytNavigationEnumName != null) {
|
|
lastYTNavigationEnumName = ytNavigationEnumName.name();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Injection point.
|
|
*/
|
|
public static void navigationTabLoaded(final View navigationButtonGroup) {
|
|
try {
|
|
String lastEnumName = lastYTNavigationEnumName;
|
|
for (NavigationButton button : NavigationButton.values()) {
|
|
if (button.ytEnumName.equals(lastEnumName)) {
|
|
ImageView imageView = Utils.getChildView((ViewGroup) navigationButtonGroup,
|
|
true, view -> view instanceof ImageView);
|
|
|
|
if (imageView != null) {
|
|
Logger.printDebug(() -> "navigationTabLoaded: " + lastEnumName);
|
|
|
|
button.imageViewRef = new WeakReference<>(imageView);
|
|
navigationTabCreatedCallback(button, navigationButtonGroup);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// Log the unknown tab as exception level, only if debug is enabled.
|
|
// This is because unknown tabs do no harm, and it's only relevant to developers.
|
|
if (Settings.DEBUG.get()) {
|
|
Logger.printException(() -> "Unknown tab: " + lastEnumName
|
|
+ " view: " + navigationButtonGroup.getClass());
|
|
}
|
|
} catch (Exception ex) {
|
|
Logger.printException(() -> "navigationTabLoaded failure", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Injection point.
|
|
*
|
|
* Unique hook just for the 'Create' and 'You' tab.
|
|
*/
|
|
public static void navigationImageResourceTabLoaded(View view) {
|
|
// 'You' tab has no YT enum name and the enum hook is not called for it.
|
|
// Compare the last enum to figure out which tab this actually is.
|
|
if (CREATE.ytEnumName.equals(lastYTNavigationEnumName)) {
|
|
navigationTabLoaded(view);
|
|
} else {
|
|
lastYTNavigationEnumName = NavigationButton.LIBRARY_YOU.ytEnumName;
|
|
navigationTabLoaded(view);
|
|
}
|
|
}
|
|
|
|
/** @noinspection EmptyMethod*/
|
|
private static void navigationTabCreatedCallback(NavigationButton button, View tabView) {
|
|
// Code is added during patching.
|
|
}
|
|
|
|
public enum NavigationButton {
|
|
HOME("PIVOT_HOME"),
|
|
SHORTS("TAB_SHORTS"),
|
|
/**
|
|
* Create new video tab.
|
|
*
|
|
* {@link #isSelected()} always returns false, even if the create video UI is on screen.
|
|
*/
|
|
CREATE("CREATION_TAB_LARGE"),
|
|
SUBSCRIPTIONS("PIVOT_SUBSCRIPTIONS"),
|
|
/**
|
|
* Notifications tab. Only present when
|
|
* {@link Settings#SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON} is active.
|
|
*/
|
|
NOTIFICATIONS("TAB_ACTIVITY"),
|
|
/**
|
|
* Library tab when the user is not logged in.
|
|
*/
|
|
LIBRARY_LOGGED_OUT("ACCOUNT_CIRCLE"),
|
|
/**
|
|
* User is logged in with incognito mode enabled.
|
|
*/
|
|
LIBRARY_INCOGNITO("INCOGNITO_CIRCLE"),
|
|
/**
|
|
* Old library tab (pre 'You' layout), only present when version spoofing.
|
|
*/
|
|
LIBRARY_OLD_UI("VIDEO_LIBRARY_WHITE"),
|
|
/**
|
|
* 'You' library tab that is sometimes momentarily loaded.
|
|
* When this is loaded, {@link #LIBRARY_YOU} is also present.
|
|
*
|
|
* This might be a temporary tab while the user profile photo is loading,
|
|
* but its exact purpose is not entirely clear.
|
|
*/
|
|
LIBRARY_PIVOT_UNKNOWN("PIVOT_LIBRARY"),
|
|
/**
|
|
* Modern library tab with 'You' layout.
|
|
*/
|
|
// The hooked YT code does not use an enum, and a dummy name is used here.
|
|
LIBRARY_YOU("YOU_LIBRARY_DUMMY_PLACEHOLDER_NAME");
|
|
|
|
/**
|
|
* @return The active navigation tab.
|
|
* If the user is in the create new video UI, this returns NULL.
|
|
*/
|
|
@Nullable
|
|
public static NavigationButton getSelectedNavigationButton() {
|
|
for (NavigationButton button : values()) {
|
|
if (button.isSelected()) return button;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return If the currently selected tab is a 'You' or library type.
|
|
* Covers all known app states including incognito mode and version spoofing.
|
|
*/
|
|
public static boolean libraryOrYouTabIsSelected() {
|
|
return LIBRARY_YOU.isSelected() || LIBRARY_PIVOT_UNKNOWN.isSelected()
|
|
|| LIBRARY_OLD_UI.isSelected() || LIBRARY_INCOGNITO.isSelected()
|
|
|| LIBRARY_LOGGED_OUT.isSelected();
|
|
}
|
|
|
|
/**
|
|
* YouTube enum name for this tab.
|
|
*/
|
|
private final String ytEnumName;
|
|
private volatile WeakReference<ImageView> imageViewRef = new WeakReference<>(null);
|
|
|
|
NavigationButton(String ytEnumName) {
|
|
this.ytEnumName = ytEnumName;
|
|
}
|
|
|
|
public boolean isSelected() {
|
|
ImageView view = imageViewRef.get();
|
|
return view != null && view.isSelected();
|
|
}
|
|
}
|
|
}
|