Skip to content
This repository has been archived by the owner on Oct 26, 2024. It is now read-only.

Commit

Permalink
feat(YouTube - Miniplayer): Rename Tablet mini player and allow sel…
Browse files Browse the repository at this point in the history
…ecting the style of the in-app miniplayer (#649)
  • Loading branch information
LisoUseInAIKyrios authored Jun 7, 2024
1 parent 7a7b2db commit f483af6
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 52 deletions.
54 changes: 46 additions & 8 deletions app/src/main/java/app/revanced/integrations/shared/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,32 @@ public static String getAppVersionName() {
return versionName;
}


/**
* 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 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;
}

/**
Expand All @@ -110,20 +124,42 @@ public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View vi
* @param view The view to hide.
*/
public static void hideViewUnderCondition(BooleanSetting condition, View view) {
if (!condition.get()) return;
if (hideViewUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
}
}

Logger.printDebug(() -> "Hiding view with setting: " + condition);
/**
* 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;
}

view.setVisibility(View.GONE);
public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting condition, View view) {
if (hideViewByRemovingFromParentUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
}
}

public static void removeViewFromParentUnderConditions(BooleanSetting setting, View view) {
if (setting.get()) {
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;
}

/**
Expand Down Expand Up @@ -236,6 +272,8 @@ public static <T extends View> T getChildView(@NonNull ViewGroup viewGroup, bool
@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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

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
Expand Down Expand Up @@ -98,4 +100,18 @@ public void save(@NonNull T newValue) {
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;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
@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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit f483af6

Please sign in to comment.