mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-06-02 19:36:30 +02:00
Merge remote-tracking branch 'origin/feat/save_brightness' into feat/save_brightness
This commit is contained in:
commit
ffd2acc6da
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,3 +1,43 @@
|
|||
# [1.8.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v1.8.0-dev.4...v1.8.0-dev.5) (2024-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Do not clip compact text when not using English ([eeaeb49](https://github.com/ReVanced/revanced-integrations/commit/eeaeb49f2a562d2690dae184153c303a5b1c4368))
|
||||
|
||||
# [1.8.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.8.0-dev.3...v1.8.0-dev.4) (2024-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide Shorts components:** Correctly hide Shorts if navigation tab is changed using device back button ([#611](https://github.com/ReVanced/revanced-integrations/issues/611)) ([ffc3437](https://github.com/ReVanced/revanced-integrations/commit/ffc3437843c24af255d2a0dda9930d2843cac4b6))
|
||||
|
||||
# [1.8.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.8.0-dev.2...v1.8.0-dev.3) (2024-04-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** Do not show a toast if migrating old unknown settings ([f2e15a2](https://github.com/ReVanced/revanced-integrations/commit/f2e15a2e1ff59ae7780cfbd366e5165f4e2b191d))
|
||||
|
||||
# [1.8.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.8.0-dev.1...v1.8.0-dev.2) (2024-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide playlist shelf in library ([c5d38a7](https://github.com/ReVanced/revanced-integrations/commit/c5d38a7e0791ebb8fe59397fff959cc94e0a7aed))
|
||||
|
||||
# [1.8.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v1.7.1-dev.4...v1.8.0-dev.1) (2024-04-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - GmsCore support:** Prompt to disable battery optimizations, if not done already ([#601](https://github.com/ReVanced/revanced-integrations/issues/601)) ([c5c9de5](https://github.com/ReVanced/revanced-integrations/commit/c5c9de500d8f1268799e55c31c446bfe8336f79a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide layout components:** Add option to hide horizontal shelves ([#598](https://github.com/ReVanced/revanced-integrations/issues/598)) ([fedace0](https://github.com/ReVanced/revanced-integrations/commit/fedace02fd5c443ef37dcf77253438b041f4c3f9))
|
||||
|
||||
## [1.7.1-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.7.1-dev.3...v1.7.1-dev.4) (2024-04-04)
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
package app.revanced.integrations.shared;
|
||||
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
|
@ -45,26 +49,19 @@ public class GmsCoreSupport {
|
|||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void showToastOrDialog(Context context, String toastMessageKey, String dialogMessageKey, String link) {
|
||||
if (!(context instanceof Activity)) {
|
||||
// Context is for the application and cannot show a dialog using it.
|
||||
Utils.showToastLong(str(toastMessageKey));
|
||||
open(link);
|
||||
return;
|
||||
}
|
||||
|
||||
private static void showBatteryOptimizationDialog(Activity context,
|
||||
String dialogMessageRef,
|
||||
String positiveButtonStringRef,
|
||||
DialogInterface.OnClickListener onPositiveClickListener) {
|
||||
// Use a delay to allow the activity to finish initializing.
|
||||
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
new AlertDialog.Builder(context)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setTitle(str("gms_core_dialog_title"))
|
||||
.setMessage(str(dialogMessageKey))
|
||||
.setPositiveButton(str("gms_core_dialog_ok_button_text"), (dialog, id) -> {
|
||||
open(link);
|
||||
})
|
||||
// Manually allow using the back button to dismiss the dialog with the back button,
|
||||
// if troubleshooting and somehow the GmsCore verification checks always fail.
|
||||
.setMessage(str(dialogMessageRef))
|
||||
.setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener)
|
||||
// Allow using back button to skip the action, just in case the check can never be satisfied.
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
}, 100);
|
||||
|
@ -74,47 +71,62 @@ public class GmsCoreSupport {
|
|||
* Injection point.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static void checkGmsCore(Context context) {
|
||||
public static void checkGmsCore(Activity context) {
|
||||
try {
|
||||
// Verify GmsCore is installed.
|
||||
try {
|
||||
PackageManager manager = context.getPackageManager();
|
||||
manager.getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
Logger.printDebug(() -> "GmsCore was not found");
|
||||
Logger.printInfo(() -> "GmsCore was not found");
|
||||
// Cannot show a dialog and must show a toast,
|
||||
// because on some installations the app crashes before the dialog can display.
|
||||
// because on some installations the app crashes before a dialog can be displayed.
|
||||
Utils.showToastLong(str("gms_core_toast_not_installed_message"));
|
||||
open(getGmsCoreDownload());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if GmsCore is whitelisted from battery optimizations.
|
||||
var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (!powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME)) {
|
||||
Logger.printDebug(() -> "GmsCore is not whitelisted from battery optimizations");
|
||||
showToastOrDialog(context,
|
||||
"gms_core_toast_not_whitelisted_message",
|
||||
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
||||
DONT_KILL_MY_APP_LINK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if GmsCore is running in the background.
|
||||
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
||||
if (client == null) {
|
||||
Logger.printDebug(() -> "GmsCore is not running in the background");
|
||||
showToastOrDialog(context,
|
||||
"gms_core_toast_not_whitelisted_message",
|
||||
Logger.printInfo(() -> "GmsCore is not running in the background");
|
||||
|
||||
showBatteryOptimizationDialog(context,
|
||||
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
||||
DONT_KILL_MY_APP_LINK);
|
||||
"gms_core_dialog_open_website_text",
|
||||
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if GmsCore is whitelisted from battery optimizations.
|
||||
if (batteryOptimizationsEnabled(context)) {
|
||||
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
|
||||
showBatteryOptimizationDialog(context,
|
||||
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
||||
"gms_core_dialog_continue_text",
|
||||
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "checkGmsCore failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife") // Permission is part of GmsCore
|
||||
private static void openGmsCoreDisableBatteryOptimizationsIntent(Activity activity) {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||
intent.setData(Uri.fromParts("package", GMS_CORE_PACKAGE_NAME, null));
|
||||
activity.startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If GmsCore is not whitelisted from battery optimizations.
|
||||
*/
|
||||
private static boolean batteryOptimizationsEnabled(Context context) {
|
||||
var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
private static String getGmsCoreDownload() {
|
||||
final var vendorGroupId = getGmsCoreVendorGroupId();
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
|
|
|
@ -224,6 +224,7 @@ public abstract class Setting<T> {
|
|||
if (!oldPrefs.preferences.contains(settingKey)) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
|
||||
Object newValue = setting.get();
|
||||
final Object migratedValue;
|
||||
if (setting instanceof BooleanSetting) {
|
||||
|
@ -238,13 +239,17 @@ public abstract class Setting<T> {
|
|||
migratedValue = oldPrefs.getString(settingKey, (String) newValue);
|
||||
} else {
|
||||
Logger.printException(() -> "Unknown setting: " + setting);
|
||||
// Remove otherwise it'll show a toast on every launch
|
||||
oldPrefs.preferences.edit().remove(settingKey).apply();
|
||||
return;
|
||||
}
|
||||
|
||||
oldPrefs.preferences.edit().remove(settingKey).apply(); // Remove the old setting.
|
||||
if (migratedValue.equals(newValue)) {
|
||||
Logger.printDebug(() -> "Value does not need migrating: " + settingKey);
|
||||
return; // Old value is already equal to the new setting value.
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Migrating old preference value into current preference: " + settingKey);
|
||||
//noinspection unchecked
|
||||
setting.save(migratedValue);
|
||||
|
|
|
@ -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,23 +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();
|
||||
}
|
||||
if (NavigationButton.HOME.isSelected()) {
|
||||
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 (NavigationButton.SUBSCRIPTIONS.isSelected() || NavigationButton.NOTIFICATIONS.isSelected()) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,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;
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package app.revanced.integrations.youtube.patches;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import app.revanced.integrations.youtube.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class HideBreakingNewsPatch {
|
||||
|
||||
/**
|
||||
* When spoofing to app versions 17.31.00 and older, the watch history preview bar uses
|
||||
* the same layout components as the breaking news shelf.
|
||||
*
|
||||
* Breaking news does not appear to be present in these older versions anyways.
|
||||
*/
|
||||
private static final boolean isSpoofingOldVersionWithHorizontalCardListWatchHistory =
|
||||
SpoofAppVersionPatch.isSpoofingToLessThan("18.01.00");
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideBreakingNews(View view) {
|
||||
if (!Settings.HIDE_BREAKING_NEWS.get()
|
||||
|| isSpoofingOldVersionWithHorizontalCardListWatchHistory) return;
|
||||
Utils.hideViewByLayoutParams(view);
|
||||
}
|
||||
}
|
|
@ -176,12 +176,6 @@ public class ReturnYouTubeDislikePatch {
|
|||
textView.removeTextChangedListener(oldUiTextWatcher);
|
||||
textView.addTextChangedListener(oldUiTextWatcher);
|
||||
|
||||
/**
|
||||
* If the patch is changed to include the dislikes button as a parameter to this method,
|
||||
* then if the button is already selected the dislikes could be adjusted using
|
||||
* {@link ReturnYouTubeDislike#setUserVote(Vote)}
|
||||
*/
|
||||
|
||||
updateOldUIDislikesTextView();
|
||||
|
||||
} catch (Exception ex) {
|
||||
|
@ -314,19 +308,25 @@ public class ReturnYouTubeDislikePatch {
|
|||
*/
|
||||
public static float onRollingNumberMeasured(String text, float measuredTextWidth) {
|
||||
try {
|
||||
if (Settings.RYD_ENABLED.get() && !Settings.RYD_COMPACT_LAYOUT.get()) {
|
||||
if (Settings.RYD_ENABLED.get()) {
|
||||
if (ReturnYouTubeDislike.isPreviouslyCreatedSegmentedSpan(text)) {
|
||||
// +1 pixel is needed for some foreign languages that measure
|
||||
// the text different from what is used for layout (Greek in particular).
|
||||
// Probably a bug in Android, but who knows.
|
||||
// Single line mode is also used as an additional fix for this issue.
|
||||
return measuredTextWidth + ReturnYouTubeDislike.leftSeparatorBounds.right
|
||||
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels + 1;
|
||||
if (Settings.RYD_COMPACT_LAYOUT.get()) {
|
||||
return measuredTextWidth + 1;
|
||||
}
|
||||
|
||||
return measuredTextWidth + 1
|
||||
+ ReturnYouTubeDislike.leftSeparatorBounds.right
|
||||
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onRollingNumberMeasured failure", ex);
|
||||
}
|
||||
|
||||
return measuredTextWidth;
|
||||
}
|
||||
|
||||
|
@ -344,10 +344,12 @@ public class ReturnYouTubeDislikePatch {
|
|||
} else {
|
||||
view.setCompoundDrawables(separator, null, null, null);
|
||||
}
|
||||
|
||||
// Disliking can cause the span to grow in size, which is ok and is laid out correctly,
|
||||
// but if the user then removes their dislike the layout will not adjust to the new shorter width.
|
||||
// Use a center alignment to take up any extra space.
|
||||
view.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
|
||||
|
||||
// Single line mode does not clip words if the span is larger than the view bounds.
|
||||
// The styled span applied to the view should always have the same bounds,
|
||||
// but use this feature just in case the measurements are somehow off by a few pixels.
|
||||
|
|
|
@ -112,37 +112,35 @@ final class KeywordContentFilter extends Filter {
|
|||
|
||||
private volatile ByteTrieSearch bufferSearch;
|
||||
|
||||
private static void logNavigationState(String state) {
|
||||
// Enable locally to debug filtering. Default off to reduce log spam.
|
||||
final boolean LOG_NAVIGATION_STATE = false;
|
||||
// noinspection ConstantValue
|
||||
if (LOG_NAVIGATION_STATE) {
|
||||
Logger.printDebug(() -> "Navigation state: " + state);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hideKeywordSettingIsActive() {
|
||||
// Must check player type first, as search bar can be active behind the player.
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||
// For now, consider the under video results the same as the home feed.
|
||||
logNavigationState("Player active");
|
||||
return Settings.HIDE_KEYWORD_CONTENT_HOME.get();
|
||||
}
|
||||
// Must check second, as search can be from any tab.
|
||||
if (NavigationBar.isSearchBarActive()) {
|
||||
logNavigationState("Search");
|
||||
return Settings.HIDE_KEYWORD_CONTENT_SEARCH.get();
|
||||
}
|
||||
if (NavigationButton.HOME.isSelected()) {
|
||||
logNavigationState("Home tab");
|
||||
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 (NavigationButton.SUBSCRIPTIONS.isSelected()) {
|
||||
logNavigationState("Subscription tab");
|
||||
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.
|
||||
logNavigationState("Ignored tab");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package app.revanced.integrations.youtube.patches.components;
|
||||
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.youtube.StringTrieSearch;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.youtube.shared.NavigationBar;
|
||||
import app.revanced.integrations.youtube.shared.PlayerType;
|
||||
|
||||
|
@ -35,6 +37,7 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
private final StringFilterGroup compactChannelBarInner;
|
||||
private final StringFilterGroup compactChannelBarInnerButton;
|
||||
private final ByteArrayFilterGroup joinMembershipButton;
|
||||
private final StringFilterGroup horizontalShelves;
|
||||
|
||||
static {
|
||||
mixPlaylistsExceptions.addPatterns(
|
||||
|
@ -43,7 +46,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public LayoutComponentsFilter() {
|
||||
exceptions.addPatterns(
|
||||
|
@ -237,6 +239,12 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
"endorsement_header_footer"
|
||||
);
|
||||
|
||||
horizontalShelves = new StringFilterGroup(
|
||||
Settings.HIDE_HORIZONTAL_SHELVES,
|
||||
"horizontal_video_shelf.eml",
|
||||
"horizontal_shelf.eml"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
expandableMetadata,
|
||||
inFeedSurvey,
|
||||
|
@ -263,7 +271,8 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
timedReactions,
|
||||
imageShelf,
|
||||
channelMemberShelf,
|
||||
forYouShelf
|
||||
forYouShelf,
|
||||
horizontalShelves
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -274,12 +283,15 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
{
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
if (exceptions.matches(path)) return false; // Exceptions are not filtered.
|
||||
|
||||
|
@ -298,6 +310,14 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
// TODO: This also hides the feed Shorts shelf header
|
||||
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
|
||||
|
||||
if (matchedGroup == horizontalShelves) {
|
||||
if (contentIndex == 0 && hideShelves()) {
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
|
@ -346,4 +366,20 @@ public final class LayoutComponentsFilter extends Filter {
|
|||
Utils.hideViewByLayoutParams(view);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hideShelves() {
|
||||
// If the player is opened while library is selected,
|
||||
// then 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 selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
||||
return selectedNavButton != null && !selectedNavButton.isLibraryOrYouTab();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package app.revanced.integrations.youtube.patches.components;
|
||||
|
||||
import static app.revanced.integrations.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
|
@ -224,16 +225,30 @@ public final class ShortsFilter extends Filter {
|
|||
// For now, consider the under video results the same as the home feed.
|
||||
return Settings.HIDE_SHORTS_HOME.get();
|
||||
}
|
||||
|
||||
// Must check second, as search can be from any tab.
|
||||
if (NavigationBar.isSearchBarActive()) {
|
||||
return Settings.HIDE_SHORTS_SEARCH.get();
|
||||
}
|
||||
if (NavigationBar.NavigationButton.HOME.isSelected()) {
|
||||
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 (NavigationBar.NavigationButton.SUBSCRIPTIONS.isSelected()) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ public class Settings extends BaseSettings {
|
|||
public static final BooleanSetting HIDE_SELF_SPONSOR = new BooleanSetting("revanced_hide_self_sponsor_ads", TRUE);
|
||||
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_hide_video_ads", TRUE, true);
|
||||
public static final BooleanSetting HIDE_WEB_SEARCH_RESULTS = new BooleanSetting("revanced_hide_web_search_results", TRUE);
|
||||
|
||||
// Layout
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SUBSCRIPTIONS = new EnumSetting<>("revanced_alt_thumbnail_subscription", ThumbnailOption.ORIGINAL);
|
||||
|
@ -76,7 +77,7 @@ public class Settings extends BaseSettings {
|
|||
public static final BooleanSetting HIDE_ALBUM_CARDS = new BooleanSetting("revanced_hide_album_cards", FALSE, true);
|
||||
public static final BooleanSetting HIDE_ARTIST_CARDS = new BooleanSetting("revanced_hide_artist_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_BREAKING_NEWS = new BooleanSetting("revanced_hide_breaking_news", TRUE, true);
|
||||
public static final BooleanSetting HIDE_HORIZONTAL_SHELVES = new BooleanSetting("revanced_hide_horizontal_shelves", TRUE);
|
||||
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
||||
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_CHANNEL_BAR = new BooleanSetting("revanced_hide_channel_bar", FALSE);
|
||||
|
|
|
@ -79,10 +79,9 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
|||
shortsPreference = new SwitchPreference(context);
|
||||
shortsPreference.setChecked(Settings.RYD_SHORTS.get());
|
||||
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
|
||||
String shortsSummary = str("revanced_ryd_shorts_summary_on",
|
||||
ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
? ""
|
||||
: "\n\n" + str("revanced_ryd_shorts_summary_disclaimer"));
|
||||
String shortsSummary = ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
? str("revanced_ryd_shorts_summary_on")
|
||||
: str("revanced_ryd_shorts_summary_on_disclaimer");
|
||||
shortsPreference.setSummaryOn(shortsSummary);
|
||||
shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off"));
|
||||
shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
|
|
|
@ -100,10 +100,10 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||
privateUserId.setEnabled(enabled);
|
||||
|
||||
// If the user has a private user id, then include a subtext that mentions not to share it.
|
||||
String exportSummarySubText = SponsorBlockSettings.userHasSBPrivateId()
|
||||
String importExportSummary = SponsorBlockSettings.userHasSBPrivateId()
|
||||
? str("revanced_sb_settings_ie_sum_warning")
|
||||
: "";
|
||||
importExport.setSummary(str("revanced_sb_settings_ie_sum", exportSummarySubText));
|
||||
: str("revanced_sb_settings_ie_sum");
|
||||
importExport.setSummary(importExportSummary);
|
||||
|
||||
apiUrl.setEnabled(enabled);
|
||||
importExport.setEnabled(enabled);
|
||||
|
|
|
@ -2,21 +2,29 @@ package app.revanced.integrations.youtube.shared;
|
|||
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.shared.settings.BaseSettings;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class NavigationBar {
|
||||
|
||||
//
|
||||
// Search bar
|
||||
//
|
||||
|
||||
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
|
@ -36,11 +44,101 @@ public final class NavigationBar {
|
|||
return searchbarResults != null && searchbarResults.getParent() != null;
|
||||
}
|
||||
|
||||
//
|
||||
// Navigation bar buttons
|
||||
//
|
||||
|
||||
/**
|
||||
* Last YT navigation enum loaded. Not necessarily the active navigation tab.
|
||||
* 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 before the navigation bar is updated.
|
||||
*
|
||||
* Fixing this situation and not needlessly waiting 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
|
||||
* 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) 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 = 75;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Only used when the hardware back button is pressed.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile String lastYTNavigationEnumName;
|
||||
private static volatile CountDownLatch navButtonLatch;
|
||||
|
||||
/**
|
||||
* Map of nav button layout views to Enum type.
|
||||
* No synchronization is needed, and this is always accessed from the main thread.
|
||||
*/
|
||||
private static final Map<View, NavigationButton> viewToButtonMap = new WeakHashMap<>();
|
||||
|
||||
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 waitForNavButtonLatchIfNeeded() {
|
||||
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 that use case the nav bar does not change so it's safe to return here.
|
||||
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. Using last known navbar button status.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Logger.printDebug(() -> "Latch wait started");
|
||||
if (latch.await(LATCH_AWAIT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS)) {
|
||||
// Back button changed the navigation tab.
|
||||
Logger.printDebug(() -> "Latch wait 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(() -> "Latch wait interrupted failure", ex); // Will never happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Last YT navigation enum loaded. Not necessarily the active navigation tab.
|
||||
* Always accessed from the main thread.
|
||||
*/
|
||||
@Nullable
|
||||
private static String lastYTNavigationEnumName;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
|
@ -57,21 +155,16 @@ public final class NavigationBar {
|
|||
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;
|
||||
}
|
||||
Logger.printDebug(() -> "navigationTabLoaded: " + lastEnumName);
|
||||
viewToButtonMap.put(navigationButtonGroup, button);
|
||||
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()) {
|
||||
|
@ -99,6 +192,46 @@ public final class NavigationBar {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void navigationTabSelected(View navButtonImageView, boolean isSelected) {
|
||||
try {
|
||||
NavigationButton button = viewToButtonMap.get(navButtonImageView);
|
||||
|
||||
if (button == null) { // An unknown tab was selected.
|
||||
// Show a toast only if debug mode is enabled.
|
||||
if (BaseSettings.DEBUG.get()) {
|
||||
Logger.printException(() -> "Unknown navigation view selected: " + navButtonImageView);
|
||||
}
|
||||
|
||||
NavigationButton.selectedNavigationButton = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
NavigationButton.selectedNavigationButton = button;
|
||||
Logger.printDebug(() -> "Changed to navigation button: " + button);
|
||||
|
||||
// Release any threads waiting for the selected nav button.
|
||||
releaseNavButtonLatch();
|
||||
} else if (NavigationButton.selectedNavigationButton == button) {
|
||||
NavigationButton.selectedNavigationButton = null;
|
||||
Logger.printDebug(() -> "Navigated away from button: " + button);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "navigationTabSelected failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void onBackPressed(Activity activity) {
|
||||
Logger.printDebug(() -> "Back button pressed");
|
||||
createNavButtonLatch();
|
||||
}
|
||||
|
||||
/** @noinspection EmptyMethod*/
|
||||
private static void navigationTabCreatedCallback(NavigationButton button, View tabView) {
|
||||
// Code is added during patching.
|
||||
|
@ -109,8 +242,7 @@ public final class NavigationBar {
|
|||
SHORTS("TAB_SHORTS"),
|
||||
/**
|
||||
* Create new video tab.
|
||||
*
|
||||
* {@link #isSelected()} always returns false, even if the create video UI is on screen.
|
||||
* This tab will never be in a selected state, even if the create video UI is on screen.
|
||||
*/
|
||||
CREATE("CREATION_TAB_LARGE"),
|
||||
SUBSCRIPTIONS("PIVOT_SUBSCRIPTIONS"),
|
||||
|
@ -145,41 +277,43 @@ public final class NavigationBar {
|
|||
// The hooked YT code does not use an enum, and a dummy name is used here.
|
||||
LIBRARY_YOU("YOU_LIBRARY_DUMMY_PLACEHOLDER_NAME");
|
||||
|
||||
@Nullable
|
||||
private static volatile NavigationButton selectedNavigationButton;
|
||||
|
||||
/**
|
||||
* This will return null only if the currently selected tab is unknown.
|
||||
* This scenario will only happen if the UI has different tabs due to an A/B user test
|
||||
* or YT abruptly changes the navigation layout for some other reason.
|
||||
*
|
||||
* All code calling this method should handle a null return value.
|
||||
*
|
||||
* <b>Due to issues with how YT processes physical back button events,
|
||||
* this patch uses workarounds that can cause this method to take up to 75ms
|
||||
* if the device back button was recently pressed.</b>
|
||||
*
|
||||
* @return The active navigation tab.
|
||||
* If the user is in the create new video UI, this returns NULL.
|
||||
* If the user is in the upload video UI, this returns tab that is still visually
|
||||
* selected on screen (whatever tab the user was on before tapping the upload button).
|
||||
*/
|
||||
@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();
|
||||
waitForNavButtonLatchIfNeeded();
|
||||
return selectedNavigationButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
public boolean isLibraryOrYouTab() {
|
||||
return this == LIBRARY_YOU || this == LIBRARY_PIVOT_UNKNOWN
|
||||
|| this == LIBRARY_OLD_UI || this == LIBRARY_INCOGNITO
|
||||
|| this == LIBRARY_LOGGED_OUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
android.useAndroidX = true
|
||||
version = 1.7.1-dev.4
|
||||
version = 1.8.0-dev.5
|
||||
|
|
Loading…
Reference in New Issue
Block a user