feat: Use modern style settings dialogs (#5109)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
This commit is contained in:
@ -5,7 +5,7 @@ import static app.revanced.extension.shared.requests.Route.Method.GET;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@ -15,6 +15,8 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
@ -26,6 +28,7 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.requests.Requester;
|
import app.revanced.extension.shared.requests.Requester;
|
||||||
import app.revanced.extension.shared.requests.Route;
|
import app.revanced.extension.shared.requests.Route;
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class GmsCoreSupport {
|
public class GmsCoreSupport {
|
||||||
@ -78,13 +81,27 @@ public class GmsCoreSupport {
|
|||||||
// Use a delay to allow the activity to finish initializing.
|
// 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.
|
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
||||||
Utils.runOnMainThreadDelayed(() -> {
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
|
// Create the custom dialog.
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
context,
|
||||||
|
str("gms_core_dialog_title"), // Title.
|
||||||
|
str(dialogMessageRef), // Message.
|
||||||
|
null, // No EditText.
|
||||||
|
str(positiveButtonTextRef), // OK button text.
|
||||||
|
() -> onPositiveClickListener.onClick(null, 0), // Convert DialogInterface.OnClickListener to Runnable.
|
||||||
|
null, // No Cancel button action.
|
||||||
|
null, // No Neutral button text.
|
||||||
|
null, // No Neutral button action.
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
|
||||||
// Do not set cancelable to false, to allow using back button to skip the action,
|
// Do not set cancelable to false, to allow using back button to skip the action,
|
||||||
// just in case the battery change can never be satisfied.
|
// just in case the battery change can never be satisfied.
|
||||||
var dialog = new AlertDialog.Builder(context)
|
dialog.setCancelable(true);
|
||||||
.setTitle(str("gms_core_dialog_title"))
|
|
||||||
.setMessage(str(dialogMessageRef))
|
// Show the dialog
|
||||||
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
|
|
||||||
.create();
|
|
||||||
Utils.showDialog(context, dialog);
|
Utils.showDialog(context, dialog);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,15 +3,21 @@ package app.revanced.extension.shared.checks;
|
|||||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
import static app.revanced.extension.shared.Utils.DialogFragmentOnStartAction;
|
import static app.revanced.extension.shared.Utils.DialogFragmentOnStartAction;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -86,38 +92,59 @@ abstract class Check {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Utils.runOnMainThreadDelayed(() -> {
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
AlertDialog alert = new AlertDialog.Builder(activity)
|
// Create the custom dialog.
|
||||||
.setCancelable(false)
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
activity,
|
||||||
.setTitle(str("revanced_check_environment_failed_title"))
|
str("revanced_check_environment_failed_title"), // Title.
|
||||||
.setMessage(message)
|
message, // Message.
|
||||||
.setPositiveButton(
|
null, // No EditText.
|
||||||
" ",
|
str("revanced_check_environment_dialog_open_official_source_button"), // OK button text.
|
||||||
(dialog, which) -> {
|
() -> {
|
||||||
final var intent = new Intent(Intent.ACTION_VIEW, GOOD_SOURCE);
|
// Action for the OK (website) button.
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
final var intent = new Intent(Intent.ACTION_VIEW, GOOD_SOURCE);
|
||||||
activity.startActivity(intent);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
|
||||||
// Shutdown to prevent the user from navigating back to this app,
|
// Shutdown to prevent the user from navigating back to this app,
|
||||||
// which is no longer showing a warning dialog.
|
// which is no longer showing a warning dialog.
|
||||||
activity.finishAffinity();
|
activity.finishAffinity();
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
},
|
||||||
).setNegativeButton(
|
null, // No cancel button.
|
||||||
" ",
|
str("revanced_check_environment_dialog_ignore_button"), // Neutral button text.
|
||||||
(dialog, which) -> {
|
() -> {
|
||||||
// Cleanup data if the user incorrectly imported a huge negative number.
|
// Neutral button action.
|
||||||
final int current = Math.max(0, BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get());
|
// Cleanup data if the user incorrectly imported a huge negative number.
|
||||||
BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(current + 1);
|
final int current = Math.max(0, BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get());
|
||||||
|
BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(current + 1);
|
||||||
|
},
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
dialog.dismiss();
|
// Get the dialog and main layout.
|
||||||
}
|
Dialog dialog = dialogPair.first;
|
||||||
).create();
|
LinearLayout mainLayout = dialogPair.second;
|
||||||
|
|
||||||
Utils.showDialog(activity, alert, false, new DialogFragmentOnStartAction() {
|
// Add icon to the dialog.
|
||||||
|
ImageView iconView = new ImageView(activity);
|
||||||
|
iconView.setImageResource(Utils.getResourceIdentifier("revanced_ic_dialog_alert", "drawable"));
|
||||||
|
iconView.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN);
|
||||||
|
final int dip8 = dipToPixels(8);
|
||||||
|
iconView.setPadding(0, dip8, 0, dip8);
|
||||||
|
LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
iconParams.gravity = Gravity.CENTER;
|
||||||
|
mainLayout.addView(iconView, 0); // Add icon at the top.
|
||||||
|
|
||||||
|
dialog.setCancelable(false);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
Utils.showDialog(activity, dialog, false, new DialogFragmentOnStartAction() {
|
||||||
boolean hasRun;
|
boolean hasRun;
|
||||||
@Override
|
@Override
|
||||||
public void onStart(AlertDialog dialog) {
|
public void onStart(Dialog dialog) {
|
||||||
// Only run this once, otherwise if the user changes to a different app
|
// Only run this once, otherwise if the user changes to a different app
|
||||||
// then changes back, this handler will run again and disable the buttons.
|
// then changes back, this handler will run again and disable the buttons.
|
||||||
if (hasRun) {
|
if (hasRun) {
|
||||||
@ -125,19 +152,43 @@ abstract class Check {
|
|||||||
}
|
}
|
||||||
hasRun = true;
|
hasRun = true;
|
||||||
|
|
||||||
var openWebsiteButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
// Get the button container to access buttons.
|
||||||
|
LinearLayout buttonContainer = (LinearLayout) mainLayout.getChildAt(mainLayout.getChildCount() - 1);
|
||||||
|
|
||||||
|
Button openWebsiteButton;
|
||||||
|
Button ignoreButton;
|
||||||
|
|
||||||
|
// Check if buttons are in a single-row layout (buttonContainer has one child: rowContainer).
|
||||||
|
if (buttonContainer.getChildCount() == 1 && buttonContainer.getChildAt(0) instanceof LinearLayout) {
|
||||||
|
LinearLayout rowContainer = (LinearLayout) buttonContainer.getChildAt(0);
|
||||||
|
// Neutral button is the first child (index 0).
|
||||||
|
ignoreButton = (Button) rowContainer.getChildAt(0);
|
||||||
|
// OK button is the last child.
|
||||||
|
openWebsiteButton = (Button) rowContainer.getChildAt(rowContainer.getChildCount() - 1);
|
||||||
|
} else {
|
||||||
|
// Multi-row layout: buttons are in separate containers, ordered OK, Cancel, Neutral.
|
||||||
|
LinearLayout okContainer =
|
||||||
|
(LinearLayout) buttonContainer.getChildAt(0); // OK is first.
|
||||||
|
openWebsiteButton = (Button) okContainer.getChildAt(0);
|
||||||
|
LinearLayout neutralContainer =
|
||||||
|
(LinearLayout)buttonContainer.getChildAt(buttonContainer.getChildCount() - 1); // Neutral is last.
|
||||||
|
ignoreButton = (Button) neutralContainer.getChildAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially set buttons to INVISIBLE and disabled.
|
||||||
|
openWebsiteButton.setVisibility(View.INVISIBLE);
|
||||||
openWebsiteButton.setEnabled(false);
|
openWebsiteButton.setEnabled(false);
|
||||||
|
ignoreButton.setVisibility(View.INVISIBLE);
|
||||||
|
ignoreButton.setEnabled(false);
|
||||||
|
|
||||||
var dismissButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
// Start the countdown for showing and enabling buttons.
|
||||||
dismissButton.setEnabled(false);
|
getCountdownRunnable(ignoreButton, openWebsiteButton).run();
|
||||||
|
|
||||||
getCountdownRunnable(dismissButton, openWebsiteButton).run();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 1000); // Use a delay, so this dialog is shown on top of any other startup dialogs.
|
}, 1000); // Use a delay, so this dialog is shown on top of any other startup dialogs.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Runnable getCountdownRunnable(Button dismissButton, Button openWebsiteButton) {
|
private static Runnable getCountdownRunnable(Button ignoreButton, Button openWebsiteButton) {
|
||||||
return new Runnable() {
|
return new Runnable() {
|
||||||
private int secondsRemaining = SECONDS_BEFORE_SHOWING_IGNORE_BUTTON;
|
private int secondsRemaining = SECONDS_BEFORE_SHOWING_IGNORE_BUTTON;
|
||||||
|
|
||||||
@ -146,17 +197,15 @@ abstract class Check {
|
|||||||
Utils.verifyOnMainThread();
|
Utils.verifyOnMainThread();
|
||||||
|
|
||||||
if (secondsRemaining > 0) {
|
if (secondsRemaining > 0) {
|
||||||
if (secondsRemaining - SECONDS_BEFORE_SHOWING_WEBSITE_BUTTON == 0) {
|
if (secondsRemaining - SECONDS_BEFORE_SHOWING_WEBSITE_BUTTON <= 0) {
|
||||||
openWebsiteButton.setText(str("revanced_check_environment_dialog_open_official_source_button"));
|
openWebsiteButton.setVisibility(View.VISIBLE);
|
||||||
openWebsiteButton.setEnabled(true);
|
openWebsiteButton.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
secondsRemaining--;
|
secondsRemaining--;
|
||||||
|
|
||||||
Utils.runOnMainThreadDelayed(this, 1000);
|
Utils.runOnMainThreadDelayed(this, 1000);
|
||||||
} else {
|
} else {
|
||||||
dismissButton.setText(str("revanced_check_environment_dialog_ignore_button"));
|
ignoreButton.setVisibility(View.VISIBLE);
|
||||||
dismissButton.setEnabled(true);
|
ignoreButton.setEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -52,7 +52,7 @@ public class Route {
|
|||||||
|
|
||||||
private int countMatches(CharSequence seq, char c) {
|
private int countMatches(CharSequence seq, char c) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = 0; i < seq.length(); i++) {
|
for (int i = 0, length = seq.length(); i < length; i++) {
|
||||||
if (seq.charAt(i) == c)
|
if (seq.charAt(i) == c)
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,20 @@ package app.revanced.extension.shared.settings.preference;
|
|||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.*;
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceGroup;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.preference.PreferenceScreen;
|
||||||
|
import android.preference.SwitchPreference;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -44,7 +52,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
* Set by subclasses if Strings cannot be added as a resource.
|
* Set by subclasses if Strings cannot be added as a resource.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle;
|
protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle, restartDialogMessage;
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||||
try {
|
try {
|
||||||
@ -76,7 +84,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
updatingPreference = true;
|
updatingPreference = true;
|
||||||
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
|
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
|
||||||
// Updating here can can cause a recursive call back into this same method.
|
// Updating here can cause a recursive call back into this same method.
|
||||||
updatePreference(pref, setting, true, settingImportInProgress);
|
updatePreference(pref, setting, true, settingImportInProgress);
|
||||||
// Update any other preference availability that may now be different.
|
// Update any other preference availability that may now be different.
|
||||||
updateUIAvailability();
|
updateUIAvailability();
|
||||||
@ -116,11 +124,14 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
showingUserDialogMessage = true;
|
showingUserDialogMessage = true;
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setTitle(confirmDialogTitle)
|
context,
|
||||||
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
|
confirmDialogTitle, // Title.
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
|
Objects.requireNonNull(setting.userDialogMessage).toString(), // No message.
|
||||||
// User confirmed, save to the Setting.
|
null, // No EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action. User confirmed, save to the Setting.
|
||||||
updatePreference(pref, setting, true, false);
|
updatePreference(pref, setting, true, false);
|
||||||
|
|
||||||
// Update availability of other preferences that may be changed.
|
// Update availability of other preferences that may be changed.
|
||||||
@ -129,23 +140,27 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
if (setting.rebootApp) {
|
if (setting.rebootApp) {
|
||||||
showRestartDialog(context);
|
showRestartDialog(context);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.setNegativeButton(android.R.string.cancel, (dialog, id) -> {
|
() -> {
|
||||||
// Restore whatever the setting was before the change.
|
// Cancel button action. Restore whatever the setting was before the change.
|
||||||
updatePreference(pref, setting, true, true);
|
updatePreference(pref, setting, true, true);
|
||||||
})
|
},
|
||||||
.setOnDismissListener(dialog -> {
|
null, // No Neutral button.
|
||||||
showingUserDialogMessage = false;
|
null, // No Neutral button action.
|
||||||
})
|
true // Dismiss dialog when onNeutralClick.
|
||||||
.setCancelable(false)
|
);
|
||||||
.show();
|
|
||||||
|
dialogPair.first.setOnDismissListener(d -> showingUserDialogMessage = false);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates all Preferences values and their availability using the current values in {@link Setting}.
|
* Updates all Preferences values and their availability using the current values in {@link Setting}.
|
||||||
*/
|
*/
|
||||||
protected void updateUIToSettingValues() {
|
protected void updateUIToSettingValues() {
|
||||||
updatePreferenceScreen(getPreferenceScreen(), true,true);
|
updatePreferenceScreen(getPreferenceScreen(), true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -280,17 +295,27 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
if (restartDialogTitle == null) {
|
if (restartDialogTitle == null) {
|
||||||
restartDialogTitle = str("revanced_settings_restart_title");
|
restartDialogTitle = str("revanced_settings_restart_title");
|
||||||
}
|
}
|
||||||
|
if (restartDialogMessage == null) {
|
||||||
|
restartDialogMessage = str("revanced_settings_restart_dialog_message");
|
||||||
|
}
|
||||||
if (restartDialogButtonText == null) {
|
if (restartDialogButtonText == null) {
|
||||||
restartDialogButtonText = str("revanced_settings_restart");
|
restartDialogButtonText = str("revanced_settings_restart");
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(context,
|
||||||
.setMessage(restartDialogTitle)
|
restartDialogTitle, // Title.
|
||||||
.setPositiveButton(restartDialogButtonText, (dialog, id)
|
restartDialogMessage, // Message.
|
||||||
-> Utils.restartApp(context))
|
null, // No EditText.
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
restartDialogButtonText, // OK button text.
|
||||||
.setCancelable(false)
|
() -> Utils.restartApp(context), // OK button action.
|
||||||
.show();
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
null, // No Neutral button text.
|
||||||
|
null, // No Neutral button action.
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ResourceType")
|
@SuppressLint("ResourceType")
|
||||||
|
@ -2,8 +2,9 @@ package app.revanced.extension.shared.settings.preference;
|
|||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
@ -18,6 +19,7 @@ import android.text.TextWatcher;
|
|||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -182,7 +184,7 @@ public class ColorPickerPreference extends EditTextPreference {
|
|||||||
* @throws IllegalArgumentException If the color string is invalid.
|
* @throws IllegalArgumentException If the color string is invalid.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void setText(String colorString) {
|
public final void setText(String colorString) {
|
||||||
try {
|
try {
|
||||||
Logger.printDebug(() -> "setText: " + colorString);
|
Logger.printDebug(() -> "setText: " + colorString);
|
||||||
super.setText(colorString);
|
super.setText(colorString);
|
||||||
@ -216,86 +218,6 @@ public class ColorPickerPreference extends EditTextPreference {
|
|||||||
widgetColorDot.setAlpha(isEnabled() ? 1.0f : DISABLED_ALPHA);
|
widgetColorDot.setAlpha(isEnabled() ? 1.0f : DISABLED_ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a layout with a color preview and EditText for hex color input.
|
|
||||||
*
|
|
||||||
* @param context The context for creating the layout.
|
|
||||||
* @return A LinearLayout containing the color preview and EditText.
|
|
||||||
*/
|
|
||||||
private LinearLayout createDialogLayout(Context context) {
|
|
||||||
LinearLayout layout = new LinearLayout(context);
|
|
||||||
layout.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
layout.setPadding(70, 0, 70, 0);
|
|
||||||
|
|
||||||
// Inflate color picker.
|
|
||||||
View colorPicker = LayoutInflater.from(context).inflate(
|
|
||||||
getResourceIdentifier("revanced_color_picker", "layout"), null);
|
|
||||||
dialogColorPickerView = colorPicker.findViewById(
|
|
||||||
getResourceIdentifier("color_picker_view", "id"));
|
|
||||||
dialogColorPickerView.setColor(currentColor);
|
|
||||||
layout.addView(colorPicker);
|
|
||||||
|
|
||||||
// Horizontal layout for preview and EditText.
|
|
||||||
LinearLayout inputLayout = new LinearLayout(context);
|
|
||||||
inputLayout.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
inputLayout.setPadding(0, 20, 0, 0);
|
|
||||||
|
|
||||||
dialogColorPreview = new TextView(context);
|
|
||||||
inputLayout.addView(dialogColorPreview);
|
|
||||||
updateColorPreview();
|
|
||||||
|
|
||||||
EditText editText = getEditText();
|
|
||||||
ViewParent parent = editText.getParent();
|
|
||||||
if (parent instanceof ViewGroup parentViewGroup) {
|
|
||||||
parentViewGroup.removeView(editText);
|
|
||||||
}
|
|
||||||
editText.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
));
|
|
||||||
String currentColorString = getColorString(currentColor);
|
|
||||||
editText.setText(currentColorString);
|
|
||||||
editText.setSelection(currentColorString.length());
|
|
||||||
editText.setTypeface(Typeface.MONOSPACE);
|
|
||||||
colorTextWatcher = createColorTextWatcher(dialogColorPickerView);
|
|
||||||
editText.addTextChangedListener(colorTextWatcher);
|
|
||||||
inputLayout.addView(editText);
|
|
||||||
|
|
||||||
// Add a dummy view to take up remaining horizontal space,
|
|
||||||
// otherwise it will show an oversize underlined text view.
|
|
||||||
View paddingView = new View(context);
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
0,
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
1f
|
|
||||||
);
|
|
||||||
paddingView.setLayoutParams(params);
|
|
||||||
inputLayout.addView(paddingView);
|
|
||||||
|
|
||||||
layout.addView(inputLayout);
|
|
||||||
|
|
||||||
// Set up color picker listener with debouncing.
|
|
||||||
// Add listener last to prevent callbacks from set calls above.
|
|
||||||
dialogColorPickerView.setOnColorChangedListener(color -> {
|
|
||||||
// Check if it actually changed, since this callback
|
|
||||||
// can be caused by updates in afterTextChanged().
|
|
||||||
if (currentColor == color) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String updatedColorString = getColorString(color);
|
|
||||||
Logger.printDebug(() -> "onColorChanged: " + updatedColorString);
|
|
||||||
currentColor = color;
|
|
||||||
editText.setText(updatedColorString);
|
|
||||||
editText.setSelection(updatedColorString.length());
|
|
||||||
|
|
||||||
updateColorPreview();
|
|
||||||
updateWidgetColorDot();
|
|
||||||
});
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the color preview TextView with a colored dot.
|
* Updates the color preview TextView with a colored dot.
|
||||||
*/
|
*/
|
||||||
@ -360,65 +282,142 @@ public class ColorPickerPreference extends EditTextPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares the dialog builder with a custom view and reset button.
|
* Creates a Dialog with a color preview and EditText for hex color input.
|
||||||
*
|
|
||||||
* @param builder The AlertDialog.Builder to configure.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
|
||||||
Utils.setEditTextDialogTheme(builder);
|
|
||||||
LinearLayout dialogLayout = createDialogLayout(builder.getContext());
|
|
||||||
builder.setView(dialogLayout);
|
|
||||||
final int originalColor = currentColor;
|
|
||||||
|
|
||||||
builder.setNeutralButton(str("revanced_settings_reset_color"), null);
|
|
||||||
|
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
|
||||||
try {
|
|
||||||
String colorString = getEditText().getText().toString();
|
|
||||||
|
|
||||||
if (colorString.length() != COLOR_STRING_LENGTH) {
|
|
||||||
Utils.showToastShort(str("revanced_settings_color_invalid"));
|
|
||||||
setText(getColorString(originalColor));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setText(colorString);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// Should never happen due to a bad color string,
|
|
||||||
// since the text is validated and fixed while the user types.
|
|
||||||
Logger.printException(() -> "setPositiveButton failure", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
|
||||||
try {
|
|
||||||
// Restore the original color.
|
|
||||||
setText(getColorString(originalColor));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "setNegativeButton failure", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void showDialog(Bundle state) {
|
protected void showDialog(Bundle state) {
|
||||||
super.showDialog(state);
|
Context context = getContext();
|
||||||
|
|
||||||
AlertDialog dialog = (AlertDialog) getDialog();
|
// Inflate color picker view.
|
||||||
dialog.setCanceledOnTouchOutside(false);
|
View colorPicker = LayoutInflater.from(context).inflate(
|
||||||
|
getResourceIdentifier("revanced_color_picker", "layout"), null);
|
||||||
|
dialogColorPickerView = colorPicker.findViewById(
|
||||||
|
getResourceIdentifier("revanced_color_picker_view", "id"));
|
||||||
|
dialogColorPickerView.setColor(currentColor);
|
||||||
|
|
||||||
// Do not close dialog when reset is pressed.
|
// Horizontal layout for preview and EditText.
|
||||||
Button button = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
|
LinearLayout inputLayout = new LinearLayout(context);
|
||||||
button.setOnClickListener(view -> {
|
inputLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
try {
|
inputLayout.setPadding(0, 0, 0, dipToPixels(10));
|
||||||
final int defaultColor = Color.parseColor(colorSetting.defaultValue) & 0x00FFFFFF;
|
|
||||||
// Setting view color causes listener callback into this class.
|
dialogColorPreview = new TextView(context);
|
||||||
dialogColorPickerView.setColor(defaultColor);
|
LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(
|
||||||
} catch (Exception ex) {
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
Logger.printException(() -> "setOnClickListener failure", ex);
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
previewParams.setMargins(dipToPixels(15), 0, dipToPixels(10), 0); // text dot has its own indents so 15, instead 16.
|
||||||
|
dialogColorPreview.setLayoutParams(previewParams);
|
||||||
|
inputLayout.addView(dialogColorPreview);
|
||||||
|
updateColorPreview();
|
||||||
|
|
||||||
|
EditText editText = getEditText();
|
||||||
|
ViewParent parent = editText.getParent();
|
||||||
|
if (parent instanceof ViewGroup parentViewGroup) {
|
||||||
|
parentViewGroup.removeView(editText);
|
||||||
|
}
|
||||||
|
editText.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
));
|
||||||
|
String currentColorString = getColorString(currentColor);
|
||||||
|
editText.setText(currentColorString);
|
||||||
|
editText.setSelection(currentColorString.length());
|
||||||
|
editText.setTypeface(Typeface.MONOSPACE);
|
||||||
|
colorTextWatcher = createColorTextWatcher(dialogColorPickerView);
|
||||||
|
editText.addTextChangedListener(colorTextWatcher);
|
||||||
|
inputLayout.addView(editText);
|
||||||
|
|
||||||
|
// Add a dummy view to take up remaining horizontal space,
|
||||||
|
// otherwise it will show an oversize underlined text view.
|
||||||
|
View paddingView = new View(context);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
1f
|
||||||
|
);
|
||||||
|
paddingView.setLayoutParams(params);
|
||||||
|
inputLayout.addView(paddingView);
|
||||||
|
|
||||||
|
// Create main container for color picker and input layout.
|
||||||
|
LinearLayout container = new LinearLayout(context);
|
||||||
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
container.addView(colorPicker);
|
||||||
|
container.addView(inputLayout);
|
||||||
|
|
||||||
|
// Create custom dialog.
|
||||||
|
final int originalColor = currentColor & 0x00FFFFFF;
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
context,
|
||||||
|
getTitle() != null ? getTitle().toString() : str("revanced_settings_color_picker_title"), // Title.
|
||||||
|
null, // No message.
|
||||||
|
null, // No EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action.
|
||||||
|
try {
|
||||||
|
String colorString = editText.getText().toString();
|
||||||
|
if (colorString.length() != COLOR_STRING_LENGTH) {
|
||||||
|
Utils.showToastShort(str("revanced_settings_color_invalid"));
|
||||||
|
setText(getColorString(originalColor));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setText(colorString);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Should never happen due to a bad color string,
|
||||||
|
// since the text is validated and fixed while the user types.
|
||||||
|
Logger.printException(() -> "OK button failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {
|
||||||
|
// Cancel button action.
|
||||||
|
try {
|
||||||
|
// Restore the original color.
|
||||||
|
setText(getColorString(originalColor));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Cancel button failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
str("revanced_settings_reset_color"), // Neutral button text.
|
||||||
|
() -> {
|
||||||
|
// Neutral button action.
|
||||||
|
try {
|
||||||
|
final int defaultColor = Color.parseColor(colorSetting.defaultValue) & 0x00FFFFFF;
|
||||||
|
// Setting view color causes listener callback into this class.
|
||||||
|
dialogColorPickerView.setColor(defaultColor);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Reset button failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false // Do not dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the custom container to the dialog's main layout.
|
||||||
|
LinearLayout dialogMainLayout = dialogPair.second;
|
||||||
|
dialogMainLayout.addView(container, 1);
|
||||||
|
|
||||||
|
// Set up color picker listener with debouncing.
|
||||||
|
// Add listener last to prevent callbacks from set calls above.
|
||||||
|
dialogColorPickerView.setOnColorChangedListener(color -> {
|
||||||
|
// Check if it actually changed, since this callback
|
||||||
|
// can be caused by updates in afterTextChanged().
|
||||||
|
if (currentColor == color) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String updatedColorString = getColorString(color);
|
||||||
|
Logger.printDebug(() -> "onColorChanged: " + updatedColorString);
|
||||||
|
currentColor = color;
|
||||||
|
editText.setText(updatedColorString);
|
||||||
|
editText.setSelection(updatedColorString.length());
|
||||||
|
|
||||||
|
updateColorPreview();
|
||||||
|
updateWidgetColorDot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Configure and show the dialog.
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,8 +29,8 @@ import app.revanced.extension.shared.Utils;
|
|||||||
* <p>
|
* <p>
|
||||||
* This view displays two main components for color selection:
|
* This view displays two main components for color selection:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><b>Hue Bar:</b> A vertical bar on the right that allows the user to select the hue component of the color.
|
* <li><b>Hue Bar:</b> A horizontal bar at the bottom that allows the user to select the hue component of the color.
|
||||||
* <li><b>Saturation-Value Selector:</b> A rectangular area that allows the user to select the saturation and value (brightness)
|
* <li><b>Saturation-Value Selector:</b> A rectangular area above the hue bar that allows the user to select the saturation and value (brightness)
|
||||||
* components of the color based on the selected hue.
|
* components of the color based on the selected hue.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -63,12 +63,12 @@ public class ColorPickerView extends View {
|
|||||||
|
|
||||||
private static final float MARGIN_BETWEEN_AREAS = dipToPixels(24);
|
private static final float MARGIN_BETWEEN_AREAS = dipToPixels(24);
|
||||||
private static final float VIEW_PADDING = dipToPixels(16);
|
private static final float VIEW_PADDING = dipToPixels(16);
|
||||||
private static final float HUE_BAR_WIDTH = dipToPixels(12);
|
private static final float HUE_BAR_HEIGHT = dipToPixels(12);
|
||||||
private static final float HUE_CORNER_RADIUS = dipToPixels(6);
|
private static final float HUE_CORNER_RADIUS = dipToPixels(6);
|
||||||
private static final float SELECTOR_RADIUS = dipToPixels(12);
|
private static final float SELECTOR_RADIUS = dipToPixels(12);
|
||||||
private static final float SELECTOR_STROKE_WIDTH = 8;
|
private static final float SELECTOR_STROKE_WIDTH = 8;
|
||||||
/**
|
/**
|
||||||
* Hue fill radius. Use slightly smaller radius for the selector handle fill,
|
* Hue fill radius. Use slightly smaller radius for the selector handle fill,
|
||||||
* otherwise the anti-aliasing causes the fill color to bleed past the selector outline.
|
* otherwise the anti-aliasing causes the fill color to bleed past the selector outline.
|
||||||
*/
|
*/
|
||||||
private static final float SELECTOR_FILL_RADIUS = SELECTOR_RADIUS - SELECTOR_STROKE_WIDTH / 2;
|
private static final float SELECTOR_FILL_RADIUS = SELECTOR_RADIUS - SELECTOR_STROKE_WIDTH / 2;
|
||||||
@ -144,17 +144,17 @@ public class ColorPickerView extends View {
|
|||||||
final float DESIRED_ASPECT_RATIO = 0.8f; // height = width * 0.8
|
final float DESIRED_ASPECT_RATIO = 0.8f; // height = width * 0.8
|
||||||
|
|
||||||
final int minWidth = Utils.dipToPixels(250);
|
final int minWidth = Utils.dipToPixels(250);
|
||||||
final int minHeight = (int) (minWidth * DESIRED_ASPECT_RATIO);
|
final int minHeight = (int) (minWidth * DESIRED_ASPECT_RATIO) + (int) (HUE_BAR_HEIGHT + MARGIN_BETWEEN_AREAS);
|
||||||
|
|
||||||
int width = resolveSize(minWidth, widthMeasureSpec);
|
int width = resolveSize(minWidth, widthMeasureSpec);
|
||||||
int height = resolveSize(minHeight, heightMeasureSpec);
|
int height = resolveSize(minHeight, heightMeasureSpec);
|
||||||
|
|
||||||
// Ensure minimum dimensions for usability
|
// Ensure minimum dimensions for usability.
|
||||||
width = Math.max(width, minWidth);
|
width = Math.max(width, minWidth);
|
||||||
height = Math.max(height, minHeight);
|
height = Math.max(height, minHeight);
|
||||||
|
|
||||||
// Adjust height to maintain desired aspect ratio if possible
|
// Adjust height to maintain desired aspect ratio if possible.
|
||||||
final int desiredHeight = (int) (width * DESIRED_ASPECT_RATIO);
|
final int desiredHeight = (int) (width * DESIRED_ASPECT_RATIO) + (int) (HUE_BAR_HEIGHT + MARGIN_BETWEEN_AREAS);
|
||||||
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
|
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
|
||||||
height = desiredHeight;
|
height = desiredHeight;
|
||||||
}
|
}
|
||||||
@ -171,22 +171,22 @@ public class ColorPickerView extends View {
|
|||||||
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
||||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||||
|
|
||||||
// Calculate bounds with hue bar on the right
|
// Calculate bounds with hue bar at the bottom.
|
||||||
final float effectiveWidth = width - (2 * VIEW_PADDING);
|
final float effectiveWidth = width - (2 * VIEW_PADDING);
|
||||||
final float selectorWidth = effectiveWidth - HUE_BAR_WIDTH - MARGIN_BETWEEN_AREAS;
|
final float effectiveHeight = height - (2 * VIEW_PADDING) - HUE_BAR_HEIGHT - MARGIN_BETWEEN_AREAS;
|
||||||
|
|
||||||
// Adjust rectangles to account for padding and density-independent dimensions
|
// Adjust rectangles to account for padding and density-independent dimensions.
|
||||||
saturationValueRect.set(
|
saturationValueRect.set(
|
||||||
VIEW_PADDING,
|
VIEW_PADDING,
|
||||||
VIEW_PADDING,
|
VIEW_PADDING,
|
||||||
VIEW_PADDING + selectorWidth,
|
VIEW_PADDING + effectiveWidth,
|
||||||
height - VIEW_PADDING
|
VIEW_PADDING + effectiveHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
hueRect.set(
|
hueRect.set(
|
||||||
width - VIEW_PADDING - HUE_BAR_WIDTH,
|
|
||||||
VIEW_PADDING,
|
VIEW_PADDING,
|
||||||
width - VIEW_PADDING,
|
height - VIEW_PADDING - HUE_BAR_HEIGHT,
|
||||||
|
VIEW_PADDING + effectiveWidth,
|
||||||
height - VIEW_PADDING
|
height - VIEW_PADDING
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ public class ColorPickerView extends View {
|
|||||||
private void updateHueShader() {
|
private void updateHueShader() {
|
||||||
LinearGradient hueShader = new LinearGradient(
|
LinearGradient hueShader = new LinearGradient(
|
||||||
hueRect.left, hueRect.top,
|
hueRect.left, hueRect.top,
|
||||||
hueRect.left, hueRect.bottom,
|
hueRect.right, hueRect.top,
|
||||||
HUE_COLORS,
|
HUE_COLORS,
|
||||||
null,
|
null,
|
||||||
Shader.TileMode.CLAMP
|
Shader.TileMode.CLAMP
|
||||||
@ -263,8 +263,8 @@ public class ColorPickerView extends View {
|
|||||||
// Draw the hue bar.
|
// Draw the hue bar.
|
||||||
canvas.drawRoundRect(hueRect, HUE_CORNER_RADIUS, HUE_CORNER_RADIUS, huePaint);
|
canvas.drawRoundRect(hueRect, HUE_CORNER_RADIUS, HUE_CORNER_RADIUS, huePaint);
|
||||||
|
|
||||||
final float hueSelectorX = hueRect.centerX();
|
final float hueSelectorX = hueRect.left + (hue / 360f) * hueRect.width();
|
||||||
final float hueSelectorY = hueRect.top + (hue / 360f) * hueRect.height();
|
final float hueSelectorY = hueRect.centerY();
|
||||||
|
|
||||||
final float satSelectorX = saturationValueRect.left + saturation * saturationValueRect.width();
|
final float satSelectorX = saturationValueRect.left + saturation * saturationValueRect.width();
|
||||||
final float satSelectorY = saturationValueRect.top + (1 - value) * saturationValueRect.height();
|
final float satSelectorY = saturationValueRect.top + (1 - value) * saturationValueRect.height();
|
||||||
@ -316,17 +316,17 @@ public class ColorPickerView extends View {
|
|||||||
|
|
||||||
// Define touch expansion for the hue bar.
|
// Define touch expansion for the hue bar.
|
||||||
RectF expandedHueRect = new RectF(
|
RectF expandedHueRect = new RectF(
|
||||||
hueRect.left - TOUCH_EXPANSION,
|
hueRect.left,
|
||||||
hueRect.top,
|
hueRect.top - TOUCH_EXPANSION,
|
||||||
hueRect.right + TOUCH_EXPANSION,
|
hueRect.right,
|
||||||
hueRect.bottom
|
hueRect.bottom + TOUCH_EXPANSION
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
// Calculate current handle positions.
|
// Calculate current handle positions.
|
||||||
final float hueSelectorX = hueRect.centerX();
|
final float hueSelectorX = hueRect.left + (hue / 360f) * hueRect.width();
|
||||||
final float hueSelectorY = hueRect.top + (hue / 360f) * hueRect.height();
|
final float hueSelectorY = hueRect.centerY();
|
||||||
|
|
||||||
final float satSelectorX = saturationValueRect.left + saturation * saturationValueRect.width();
|
final float satSelectorX = saturationValueRect.left + saturation * saturationValueRect.width();
|
||||||
final float valSelectorY = saturationValueRect.top + (1 - value) * saturationValueRect.height();
|
final float valSelectorY = saturationValueRect.top + (1 - value) * saturationValueRect.height();
|
||||||
@ -348,14 +348,14 @@ public class ColorPickerView extends View {
|
|||||||
// Check if the touch started on a handle or within the expanded hue bar area.
|
// Check if the touch started on a handle or within the expanded hue bar area.
|
||||||
if (hueHitRect.contains(x, y)) {
|
if (hueHitRect.contains(x, y)) {
|
||||||
isDraggingHue = true;
|
isDraggingHue = true;
|
||||||
updateHueFromTouch(y);
|
updateHueFromTouch(x);
|
||||||
} else if (satValHitRect.contains(x, y)) {
|
} else if (satValHitRect.contains(x, y)) {
|
||||||
isDraggingSaturation = true;
|
isDraggingSaturation = true;
|
||||||
updateSaturationValueFromTouch(x, y);
|
updateSaturationValueFromTouch(x, y);
|
||||||
} else if (expandedHueRect.contains(x, y)) {
|
} else if (expandedHueRect.contains(x, y)) {
|
||||||
// Handle touch within the expanded hue bar area.
|
// Handle touch within the expanded hue bar area.
|
||||||
isDraggingHue = true;
|
isDraggingHue = true;
|
||||||
updateHueFromTouch(y);
|
updateHueFromTouch(x);
|
||||||
} else if (saturationValueRect.contains(x, y)) {
|
} else if (saturationValueRect.contains(x, y)) {
|
||||||
isDraggingSaturation = true;
|
isDraggingSaturation = true;
|
||||||
updateSaturationValueFromTouch(x, y);
|
updateSaturationValueFromTouch(x, y);
|
||||||
@ -365,7 +365,7 @@ public class ColorPickerView extends View {
|
|||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
// Continue updating values even if touch moves outside the view.
|
// Continue updating values even if touch moves outside the view.
|
||||||
if (isDraggingHue) {
|
if (isDraggingHue) {
|
||||||
updateHueFromTouch(y);
|
updateHueFromTouch(x);
|
||||||
} else if (isDraggingSaturation) {
|
} else if (isDraggingSaturation) {
|
||||||
updateSaturationValueFromTouch(x, y);
|
updateSaturationValueFromTouch(x, y);
|
||||||
}
|
}
|
||||||
@ -387,12 +387,12 @@ public class ColorPickerView extends View {
|
|||||||
/**
|
/**
|
||||||
* Updates the hue value based on touch position, clamping to valid range.
|
* Updates the hue value based on touch position, clamping to valid range.
|
||||||
*
|
*
|
||||||
* @param y The y-coordinate of the touch position.
|
* @param x The x-coordinate of the touch position.
|
||||||
*/
|
*/
|
||||||
private void updateHueFromTouch(float y) {
|
private void updateHueFromTouch(float x) {
|
||||||
// Clamp y to the hue rectangle bounds.
|
// Clamp x to the hue rectangle bounds.
|
||||||
final float clampedY = Utils.clamp(y, hueRect.top, hueRect.bottom);
|
final float clampedX = Utils.clamp(x, hueRect.left, hueRect.right);
|
||||||
final float updatedHue = ((clampedY - hueRect.top) / hueRect.height()) * 360f;
|
final float updatedHue = ((clampedX - hueRect.left) / hueRect.width()) * 360f;
|
||||||
if (hue == updatedHue) {
|
if (hue == updatedHue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,197 @@
|
|||||||
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom ListPreference that uses a styled custom dialog with a custom checkmark indicator.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
|
public class CustomDialogListPreference extends ListPreference {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom ArrayAdapter to handle checkmark visibility.
|
||||||
|
*/
|
||||||
|
private static class ListPreferenceArrayAdapter extends ArrayAdapter<CharSequence> {
|
||||||
|
private static class SubViewDataContainer {
|
||||||
|
ImageView checkIcon;
|
||||||
|
View placeholder;
|
||||||
|
TextView itemText;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int layoutResourceId;
|
||||||
|
final CharSequence[] entryValues;
|
||||||
|
String selectedValue;
|
||||||
|
|
||||||
|
public ListPreferenceArrayAdapter(Context context, int resource, CharSequence[] entries,
|
||||||
|
CharSequence[] entryValues, String selectedValue) {
|
||||||
|
super(context, resource, entries);
|
||||||
|
this.layoutResourceId = resource;
|
||||||
|
this.entryValues = entryValues;
|
||||||
|
this.selectedValue = selectedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View view = convertView;
|
||||||
|
SubViewDataContainer holder;
|
||||||
|
|
||||||
|
if (view == null) {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||||
|
view = inflater.inflate(layoutResourceId, parent, false);
|
||||||
|
holder = new SubViewDataContainer();
|
||||||
|
holder.checkIcon = view.findViewById(Utils.getResourceIdentifier(
|
||||||
|
"revanced_check_icon", "id"));
|
||||||
|
holder.placeholder = view.findViewById(Utils.getResourceIdentifier(
|
||||||
|
"revanced_check_icon_placeholder", "id"));
|
||||||
|
holder.itemText = view.findViewById(Utils.getResourceIdentifier(
|
||||||
|
"revanced_item_text", "id"));
|
||||||
|
view.setTag(holder);
|
||||||
|
} else {
|
||||||
|
holder = (SubViewDataContainer) view.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set text.
|
||||||
|
holder.itemText.setText(getItem(position));
|
||||||
|
holder.itemText.setTextColor(Utils.getAppForegroundColor());
|
||||||
|
|
||||||
|
// Show or hide checkmark and placeholder.
|
||||||
|
String currentValue = entryValues[position].toString();
|
||||||
|
boolean isSelected = currentValue.equals(selectedValue);
|
||||||
|
holder.checkIcon.setVisibility(isSelected ? View.VISIBLE : View.GONE);
|
||||||
|
holder.checkIcon.setColorFilter(Utils.getAppForegroundColor());
|
||||||
|
holder.placeholder.setVisibility(isSelected ? View.GONE : View.VISIBLE);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedValue(String value) {
|
||||||
|
this.selectedValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomDialogListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomDialogListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomDialogListPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomDialogListPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showDialog(Bundle state) {
|
||||||
|
// Create ListView.
|
||||||
|
ListView listView = new ListView(getContext());
|
||||||
|
listView.setId(android.R.id.list);
|
||||||
|
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||||
|
|
||||||
|
// Create custom adapter for the ListView.
|
||||||
|
ListPreferenceArrayAdapter adapter = new ListPreferenceArrayAdapter(
|
||||||
|
getContext(),
|
||||||
|
Utils.getResourceIdentifier("revanced_custom_list_item_checked", "layout"),
|
||||||
|
getEntries(),
|
||||||
|
getEntryValues(),
|
||||||
|
getValue()
|
||||||
|
);
|
||||||
|
listView.setAdapter(adapter);
|
||||||
|
|
||||||
|
// Set checked item.
|
||||||
|
String currentValue = getValue();
|
||||||
|
if (currentValue != null) {
|
||||||
|
CharSequence[] entryValues = getEntryValues();
|
||||||
|
for (int i = 0, length = entryValues.length; i < length; i++) {
|
||||||
|
if (currentValue.equals(entryValues[i].toString())) {
|
||||||
|
listView.setItemChecked(i, true);
|
||||||
|
listView.setSelection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the custom dialog without OK button.
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
getContext(),
|
||||||
|
getTitle() != null ? getTitle().toString() : "",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null, // No OK button text.
|
||||||
|
null, // No OK button action.
|
||||||
|
() -> {}, // Cancel button action (just dismiss).
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
LinearLayout mainLayout = dialogPair.second;
|
||||||
|
|
||||||
|
// Measure content height before adding ListView to layout.
|
||||||
|
// Otherwise, the ListView will push the buttons off the screen.
|
||||||
|
int totalHeight = 0;
|
||||||
|
int widthSpec = View.MeasureSpec.makeMeasureSpec(
|
||||||
|
getContext().getResources().getDisplayMetrics().widthPixels,
|
||||||
|
View.MeasureSpec.AT_MOST
|
||||||
|
);
|
||||||
|
int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||||
|
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
View listItem = adapter.getView(i, null, listView);
|
||||||
|
listItem.measure(widthSpec, heightSpec);
|
||||||
|
totalHeight += listItem.getMeasuredHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap the height at maxHeight.
|
||||||
|
int maxHeight = (int) (getContext().getResources().getDisplayMetrics().heightPixels * 0.6);
|
||||||
|
int finalHeight = Math.min(totalHeight, maxHeight);
|
||||||
|
|
||||||
|
// Add ListView to the main layout with calculated height.
|
||||||
|
LinearLayout.LayoutParams listViewParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
finalHeight // Use calculated height directly.
|
||||||
|
);
|
||||||
|
final int marginHorizontal = dipToPixels(8);
|
||||||
|
listViewParams.setMargins(0, marginHorizontal, 0, marginHorizontal);
|
||||||
|
mainLayout.addView(listView, mainLayout.getChildCount() - 1, listViewParams);
|
||||||
|
|
||||||
|
// Handle item click to select value and dismiss dialog.
|
||||||
|
listView.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
String selectedValue = getEntryValues()[position].toString();
|
||||||
|
if (callChangeListener(selectedValue)) {
|
||||||
|
setValue(selectedValue);
|
||||||
|
adapter.setSelectedValue(selectedValue);
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,30 @@
|
|||||||
package app.revanced.extension.shared.settings.preference;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {
|
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {
|
||||||
@ -54,7 +65,8 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
try {
|
try {
|
||||||
// Must set text before preparing dialog, otherwise text is non selectable if this preference is later reopened.
|
// Must set text before showing dialog,
|
||||||
|
// otherwise text is non-selectable if this preference is later reopened.
|
||||||
existingSettings = Setting.exportToJson(getContext());
|
existingSettings = Setting.exportToJson(getContext());
|
||||||
getEditText().setText(existingSettings);
|
getEditText().setText(existingSettings);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@ -64,18 +76,32 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void showDialog(Bundle state) {
|
||||||
try {
|
try {
|
||||||
Utils.setEditTextDialogTheme(builder);
|
Context context = getContext();
|
||||||
|
EditText editText = getEditText();
|
||||||
|
|
||||||
// Show the user the settings in JSON format.
|
// Create a custom dialog with the EditText.
|
||||||
builder.setNeutralButton(str("revanced_settings_import_copy"), (dialog, which) -> {
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
Utils.setClipboard(getEditText().getText());
|
context,
|
||||||
}).setPositiveButton(str("revanced_settings_import"), (dialog, which) -> {
|
str("revanced_pref_import_export_title"), // Title.
|
||||||
importSettings(builder.getContext(), getEditText().getText().toString());
|
null, // No message (EditText replaces it).
|
||||||
});
|
editText, // Pass the EditText.
|
||||||
|
str("revanced_settings_import"), // OK button text.
|
||||||
|
() -> importSettings(context, editText.getText().toString()), // OK button action.
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
str("revanced_settings_import_copy"), // Neutral button (Copy) text.
|
||||||
|
() -> {
|
||||||
|
// Neutral button (Copy) action. Show the user the settings in JSON format.
|
||||||
|
Utils.setClipboard(editText.getText());
|
||||||
|
},
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
Logger.printException(() -> "showDialog failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +114,7 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
|||||||
|
|
||||||
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
|
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
|
||||||
if (rebootNeeded) {
|
if (rebootNeeded) {
|
||||||
AbstractPreferenceFragment.showRestartDialog(getContext());
|
AbstractPreferenceFragment.showRestartDialog(context);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "importSettings failure", ex);
|
Logger.printException(() -> "importSettings failure", ex);
|
||||||
@ -96,5 +122,4 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
|||||||
AbstractPreferenceFragment.settingImportInProgress = false;
|
AbstractPreferenceFragment.settingImportInProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.extension.shared.settings.preference;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@ -8,7 +9,8 @@ import android.app.Dialog;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -18,6 +20,7 @@ import android.util.AttributeSet;
|
|||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -48,28 +51,6 @@ public class ReVancedAboutPreference extends Preference {
|
|||||||
return text.replace("-", "‑"); // #8209 = non breaking hyphen.
|
return text.replace("-", "‑"); // #8209 = non breaking hyphen.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getColorHexString(int color) {
|
|
||||||
return String.format("#%06X", (0x00FFFFFF & color));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isDarkModeEnabled() {
|
|
||||||
return Utils.isDarkModeEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses can override this and provide a themed color.
|
|
||||||
*/
|
|
||||||
protected int getLightColor() {
|
|
||||||
return Color.WHITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses can override this and provide a themed color.
|
|
||||||
*/
|
|
||||||
protected int getDarkColor() {
|
|
||||||
return Color.BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apps that do not support bundling resources must override this.
|
* Apps that do not support bundling resources must override this.
|
||||||
*
|
*
|
||||||
@ -86,9 +67,8 @@ public class ReVancedAboutPreference extends Preference {
|
|||||||
builder.append("<html>");
|
builder.append("<html>");
|
||||||
builder.append("<body style=\"text-align: center; padding: 10px;\">");
|
builder.append("<body style=\"text-align: center; padding: 10px;\">");
|
||||||
|
|
||||||
final boolean isDarkMode = isDarkModeEnabled();
|
String foregroundColorHex = Utils.getColorHexString(Utils.getAppForegroundColor());
|
||||||
String backgroundColorHex = getColorHexString(isDarkMode ? getDarkColor() : getLightColor());
|
String backgroundColorHex = Utils.getColorHexString(Utils.getDialogBackgroundColor());
|
||||||
String foregroundColorHex = getColorHexString(isDarkMode ? getLightColor() : getDarkColor());
|
|
||||||
// Apply light/dark mode colors.
|
// Apply light/dark mode colors.
|
||||||
builder.append(String.format(
|
builder.append(String.format(
|
||||||
"<style> body { background-color: %s; color: %s; } a { color: %s; } </style>",
|
"<style> body { background-color: %s; color: %s; } a { color: %s; } </style>",
|
||||||
@ -220,14 +200,36 @@ class WebViewDialog extends Dialog {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
requestWindowFeature(Window.FEATURE_NO_TITLE); // Remove default title bar.
|
||||||
|
|
||||||
|
// Create main layout.
|
||||||
|
LinearLayout mainLayout = new LinearLayout(getContext());
|
||||||
|
mainLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
final int padding = dipToPixels(10);
|
||||||
|
mainLayout.setPadding(padding, padding, padding, padding);
|
||||||
|
// Set rounded rectangle background.
|
||||||
|
ShapeDrawable mainBackground = new ShapeDrawable(new RoundRectShape(
|
||||||
|
Utils.createCornerRadii(28), null, null));
|
||||||
|
mainBackground.getPaint().setColor(Utils.getDialogBackgroundColor());
|
||||||
|
mainLayout.setBackground(mainBackground);
|
||||||
|
|
||||||
|
// Create WebView.
|
||||||
WebView webView = new WebView(getContext());
|
WebView webView = new WebView(getContext());
|
||||||
webView.getSettings().setJavaScriptEnabled(true);
|
webView.getSettings().setJavaScriptEnabled(true);
|
||||||
webView.setWebViewClient(new OpenLinksExternallyWebClient());
|
webView.setWebViewClient(new OpenLinksExternallyWebClient());
|
||||||
webView.loadDataWithBaseURL(null, htmlContent, "text/html", "utf-8", null);
|
webView.loadDataWithBaseURL(null, htmlContent, "text/html", "utf-8", null);
|
||||||
|
|
||||||
setContentView(webView);
|
// Add WebView to layout.
|
||||||
|
mainLayout.addView(webView);
|
||||||
|
|
||||||
|
setContentView(mainLayout);
|
||||||
|
|
||||||
|
// Set dialog window attributes
|
||||||
|
Window window = getWindow();
|
||||||
|
if (window != null) {
|
||||||
|
Utils.setDialogWindowParameters(getContext(), window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OpenLinksExternallyWebClient extends WebViewClient {
|
private class OpenLinksExternallyWebClient extends WebViewClient {
|
||||||
@ -315,7 +317,7 @@ class AboutLinksRoutes {
|
|||||||
// Do not show an exception toast if the server is down
|
// Do not show an exception toast if the server is down
|
||||||
final int responseCode = connection.getResponseCode();
|
final int responseCode = connection.getResponseCode();
|
||||||
if (responseCode != 200) {
|
if (responseCode != 200) {
|
||||||
Logger.printDebug(() -> "Failed to get social links. Response code: " + responseCode);
|
Logger.printDebug(() -> "Failed to get social links. Response code: " + responseCode);
|
||||||
return NO_CONNECTION_STATIC_LINKS;
|
return NO_CONNECTION_STATIC_LINKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
package app.revanced.extension.shared.settings.preference;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
import android.graphics.drawable.shapes.RectShape;
|
||||||
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
|
import android.graphics.Paint.Style;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -44,41 +58,61 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
|||||||
this.setting = setting;
|
this.setting = setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
|
||||||
super.onPrepareDialogBuilder(builder);
|
|
||||||
Utils.setEditTextDialogTheme(builder);
|
|
||||||
|
|
||||||
if (setting == null) {
|
|
||||||
String key = getKey();
|
|
||||||
if (key != null) {
|
|
||||||
setting = Setting.getSettingFromPath(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setting != null) {
|
|
||||||
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void showDialog(Bundle state) {
|
protected void showDialog(Bundle state) {
|
||||||
super.showDialog(state);
|
try {
|
||||||
|
Context context = getContext();
|
||||||
|
EditText editText = getEditText();
|
||||||
|
|
||||||
// Override the button click listener to prevent dismissing the dialog.
|
// Resolve setting if not already set.
|
||||||
Button button = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_NEUTRAL);
|
if (setting == null) {
|
||||||
if (button == null) {
|
String key = getKey();
|
||||||
return;
|
if (key != null) {
|
||||||
}
|
setting = Setting.getSettingFromPath(key);
|
||||||
button.setOnClickListener(v -> {
|
}
|
||||||
try {
|
|
||||||
String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
|
|
||||||
EditText editText = getEditText();
|
|
||||||
editText.setText(defaultStringValue);
|
|
||||||
editText.setSelection(defaultStringValue.length()); // move cursor to end of text
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "reset failure", ex);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
// Set initial EditText value to the current persisted value or empty string.
|
||||||
|
String initialValue = getText() != null ? getText() : "";
|
||||||
|
editText.setText(initialValue);
|
||||||
|
editText.setSelection(initialValue.length()); // Move cursor to end.
|
||||||
|
|
||||||
|
// Create custom dialog.
|
||||||
|
String neutralButtonText = (setting != null) ? str("revanced_settings_reset") : null;
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
context,
|
||||||
|
getTitle() != null ? getTitle().toString() : "", // Title.
|
||||||
|
null, // Message is replaced by EditText.
|
||||||
|
editText, // Pass the EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action. Persist the EditText value when OK is clicked.
|
||||||
|
String newValue = editText.getText().toString();
|
||||||
|
if (callChangeListener(newValue)) {
|
||||||
|
setText(newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
neutralButtonText, // Neutral button text (Reset).
|
||||||
|
() -> {
|
||||||
|
// Neutral button action.
|
||||||
|
if (setting != null) {
|
||||||
|
try {
|
||||||
|
String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
|
||||||
|
editText.setText(defaultStringValue);
|
||||||
|
editText.setSelection(defaultStringValue.length()); // Move cursor to end of text.
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "reset failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false // Do not dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "showDialog failure", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app.revanced.extension.shared.settings.preference;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
@ -24,12 +23,14 @@ import app.revanced.extension.shared.Utils;
|
|||||||
* it needs to subclass this preference and override {@link #getFirstEntriesToPreserve}.
|
* it needs to subclass this preference and override {@link #getFirstEntriesToPreserve}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class SortedListPreference extends ListPreference {
|
public class SortedListPreference extends CustomDialogListPreference {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the current list entries.
|
* Sorts the current list entries.
|
||||||
*
|
*
|
||||||
* @param firstEntriesToPreserve The number of entries to preserve in their original position.
|
* @param firstEntriesToPreserve The number of entries to preserve in their original position,
|
||||||
|
* or a negative value to not sort and leave entries
|
||||||
|
* as they current are.
|
||||||
*/
|
*/
|
||||||
public void sortEntryAndValues(int firstEntriesToPreserve) {
|
public void sortEntryAndValues(int firstEntriesToPreserve) {
|
||||||
CharSequence[] entries = getEntries();
|
CharSequence[] entries = getEntries();
|
||||||
@ -44,6 +45,10 @@ public class SortedListPreference extends ListPreference {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (firstEntriesToPreserve < 0) {
|
||||||
|
return; // Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
List<Pair<CharSequence, CharSequence>> firstEntries = new ArrayList<>(firstEntriesToPreserve);
|
List<Pair<CharSequence, CharSequence>> firstEntries = new ArrayList<>(firstEntriesToPreserve);
|
||||||
|
|
||||||
// Android does not have a triple class like Kotlin, So instead use a nested pair.
|
// Android does not have a triple class like Kotlin, So instead use a nested pair.
|
||||||
@ -85,10 +90,6 @@ public class SortedListPreference extends ListPreference {
|
|||||||
super.setEntryValues(sortedEntryValues);
|
super.setEntryValues(sortedEntryValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getFirstEntriesToPreserve() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SortedListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public SortedListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
|
||||||
@ -112,4 +113,12 @@ public class SortedListPreference extends ListPreference {
|
|||||||
|
|
||||||
sortEntryAndValues(getFirstEntriesToPreserve());
|
sortEntryAndValues(getFirstEntriesToPreserve());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of first entries to leave exactly where they are, and do not sort them.
|
||||||
|
* A negative value indicates do not sort any entries.
|
||||||
|
*/
|
||||||
|
protected int getFirstEntriesToPreserve() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
package app.revanced.extension.youtube;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.Utils.clamp;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.style.ReplacementSpan;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.view.Window;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
|
|
||||||
public class ThemeHelper {
|
|
||||||
@Nullable
|
|
||||||
private static Integer darkThemeColor, lightThemeColor;
|
|
||||||
private static int themeValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static void setTheme(Enum<?> value) {
|
|
||||||
final int newOrdinalValue = value.ordinal();
|
|
||||||
if (themeValue != newOrdinalValue) {
|
|
||||||
themeValue = newOrdinalValue;
|
|
||||||
Logger.printDebug(() -> "Theme value: " + newOrdinalValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDarkTheme() {
|
|
||||||
return themeValue == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setActivityTheme(Activity activity) {
|
|
||||||
final var theme = isDarkTheme()
|
|
||||||
? "Theme.YouTube.Settings.Dark"
|
|
||||||
: "Theme.YouTube.Settings";
|
|
||||||
activity.setTheme(Utils.getResourceIdentifier(theme, "style"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private static String darkThemeResourceName() {
|
|
||||||
// Value is changed by Theme patch, if included.
|
|
||||||
return "@color/yt_black3";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getThemeColor(String resourceName, int defaultColor) {
|
|
||||||
try {
|
|
||||||
return Utils.getColorFromString(resourceName);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// User entered an invalid custom theme color.
|
|
||||||
// Normally this should never be reached, and no localized strings are needed.
|
|
||||||
Utils.showToastLong("Invalid custom theme color: " + resourceName);
|
|
||||||
return defaultColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The dark theme color as specified by the Theme patch (if included),
|
|
||||||
* or the dark mode background color unpatched YT uses.
|
|
||||||
*/
|
|
||||||
public static int getDarkThemeColor() {
|
|
||||||
if (darkThemeColor == null) {
|
|
||||||
darkThemeColor = getThemeColor(darkThemeResourceName(), Color.BLACK);
|
|
||||||
}
|
|
||||||
return darkThemeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private static String lightThemeResourceName() {
|
|
||||||
// Value is changed by Theme patch, if included.
|
|
||||||
return "@color/yt_white1";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The light theme color as specified by the Theme patch (if included),
|
|
||||||
* or the non dark mode background color unpatched YT uses.
|
|
||||||
*/
|
|
||||||
public static int getLightThemeColor() {
|
|
||||||
if (lightThemeColor == null) {
|
|
||||||
lightThemeColor = getThemeColor(lightThemeResourceName(), Color.WHITE);
|
|
||||||
}
|
|
||||||
return lightThemeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getBackgroundColor() {
|
|
||||||
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getForegroundColor() {
|
|
||||||
return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getDialogBackgroundColor() {
|
|
||||||
final String colorName = isDarkTheme()
|
|
||||||
? "yt_black1"
|
|
||||||
: "yt_white1";
|
|
||||||
|
|
||||||
return Utils.getColorFromString(colorName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getToolbarBackgroundColor() {
|
|
||||||
final String colorName = isDarkTheme()
|
|
||||||
? "yt_black3"
|
|
||||||
: "yt_white1";
|
|
||||||
|
|
||||||
return Utils.getColorFromString(colorName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the system navigation bar color for the activity.
|
|
||||||
* Applies the background color obtained from {@link #getBackgroundColor()} to the navigation bar.
|
|
||||||
* For Android 10 (API 29) and above, enforces navigation bar contrast to ensure visibility.
|
|
||||||
*/
|
|
||||||
public static void setNavigationBarColor(@Nullable Window window) {
|
|
||||||
if (window == null) {
|
|
||||||
Logger.printDebug(() -> "Cannot set navigation bar color, window is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setNavigationBarColor(getBackgroundColor());
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
window.setNavigationBarContrastEnforced(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjusts the brightness of a color by lightening or darkening it based on the given factor.
|
|
||||||
* <p>
|
|
||||||
* If the factor is greater than 1, the color is lightened by interpolating toward white (#FFFFFF).
|
|
||||||
* If the factor is less than or equal to 1, the color is darkened by scaling its RGB components toward black (#000000).
|
|
||||||
* The alpha channel remains unchanged.
|
|
||||||
*
|
|
||||||
* @param color The input color to adjust, in ARGB format.
|
|
||||||
* @param factor The adjustment factor. Use values > 1.0f to lighten (e.g., 1.11f for slight lightening)
|
|
||||||
* or values <= 1.0f to darken (e.g., 0.95f for slight darkening).
|
|
||||||
* @return The adjusted color in ARGB format.
|
|
||||||
*/
|
|
||||||
public static int adjustColorBrightness(int color, float factor) {
|
|
||||||
final int alpha = Color.alpha(color);
|
|
||||||
int red = Color.red(color);
|
|
||||||
int green = Color.green(color);
|
|
||||||
int blue = Color.blue(color);
|
|
||||||
|
|
||||||
if (factor > 1.0f) {
|
|
||||||
// Lighten: Interpolate toward white (255)
|
|
||||||
final float t = 1.0f - (1.0f / factor); // Interpolation parameter
|
|
||||||
red = Math.round(red + (255 - red) * t);
|
|
||||||
green = Math.round(green + (255 - green) * t);
|
|
||||||
blue = Math.round(blue + (255 - blue) * t);
|
|
||||||
} else {
|
|
||||||
// Darken or no change: Scale toward black
|
|
||||||
red = (int) (red * factor);
|
|
||||||
green = (int) (green * factor);
|
|
||||||
blue = (int) (blue * factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure values are within [0, 255]
|
|
||||||
red = clamp(red, 0, 255);
|
|
||||||
green = clamp(green, 0, 255);
|
|
||||||
blue = clamp(blue, 0, 255);
|
|
||||||
|
|
||||||
return Color.argb(alpha, red, green, blue);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,10 @@ package app.revanced.extension.youtube.patches;
|
|||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -63,18 +66,28 @@ public class CheckWatchHistoryDomainNameResolutionPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Utils.runOnMainThread(() -> {
|
Utils.runOnMainThread(() -> {
|
||||||
var alert = new android.app.AlertDialog.Builder(context)
|
try {
|
||||||
.setTitle(str("revanced_check_watch_history_domain_name_dialog_title"))
|
// Create the custom dialog.
|
||||||
.setMessage(Html.fromHtml(str("revanced_check_watch_history_domain_name_dialog_message")))
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
context,
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
str("revanced_check_watch_history_domain_name_dialog_title"), // Title.
|
||||||
dialog.dismiss();
|
Html.fromHtml(str("revanced_check_watch_history_domain_name_dialog_message")), // Message (HTML).
|
||||||
}).setNegativeButton(str("revanced_check_watch_history_domain_name_dialog_ignore"), (dialog, which) -> {
|
null, // No EditText.
|
||||||
Settings.CHECK_WATCH_HISTORY_DOMAIN_NAME.save(false);
|
null, // OK button text.
|
||||||
dialog.dismiss();
|
() -> {}, // OK button action (just dismiss).
|
||||||
}).create();
|
() -> {}, // Cancel button action (just dismiss).
|
||||||
|
str("revanced_check_watch_history_domain_name_dialog_ignore"), // Neutral button text.
|
||||||
|
() -> Settings.CHECK_WATCH_HISTORY_DOMAIN_NAME.save(false), // Neutral button action (Ignore).
|
||||||
|
true // Dismiss dialog on Neutral button click.
|
||||||
|
);
|
||||||
|
|
||||||
Utils.showDialog(context, alert, false, null);
|
// Show the dialog.
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
|
||||||
|
Utils.showDialog(context, dialog, false, null);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "checkDnsResolver dialog creation failure", ex);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "checkDnsResolver failure", ex);
|
Logger.printException(() -> "checkDnsResolver failure", ex);
|
||||||
|
@ -2,13 +2,16 @@ package app.revanced.extension.youtube.patches.announcements;
|
|||||||
|
|
||||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENTS;
|
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENTS;
|
||||||
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENT_IDS;
|
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENT_IDS;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.util.Pair;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
@ -120,25 +123,38 @@ public final class AnnouncementsPatch {
|
|||||||
final Level finalLevel = level;
|
final Level finalLevel = level;
|
||||||
|
|
||||||
Utils.runOnMainThread(() -> {
|
Utils.runOnMainThread(() -> {
|
||||||
// Show the announcement.
|
// Create the custom dialog and show the announcement.
|
||||||
var alert = new AlertDialog.Builder(context)
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setTitle(finalTitle)
|
context,
|
||||||
.setMessage(finalMessage)
|
finalTitle, // Title.
|
||||||
.setIcon(finalLevel.icon)
|
finalMessage, // Message.
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
null, // No EditText.
|
||||||
Settings.ANNOUNCEMENT_LAST_ID.save(finalId);
|
null, // OK button text.
|
||||||
dialog.dismiss();
|
() -> Settings.ANNOUNCEMENT_LAST_ID.save(finalId), // OK button action.
|
||||||
}).setNegativeButton(str("revanced_announcements_dialog_dismiss"), (dialog, which) -> {
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
dialog.dismiss();
|
str("revanced_announcements_dialog_dismiss"), // Neutral button text.
|
||||||
})
|
() -> {}, // Neutral button action (dismiss only).
|
||||||
.setCancelable(false)
|
true // Dismiss dialog when onNeutralClick.
|
||||||
.create();
|
);
|
||||||
|
|
||||||
Utils.showDialog(context, alert, false, (AlertDialog dialog) -> {
|
Dialog dialog = dialogPair.first;
|
||||||
// Make links clickable.
|
LinearLayout mainLayout = dialogPair.second;
|
||||||
((TextView) dialog.findViewById(android.R.id.message))
|
|
||||||
.setMovementMethod(LinkMovementMethod.getInstance());
|
// Set the icon for the title TextView
|
||||||
});
|
for (int i = 0, childCould = mainLayout.getChildCount(); i < childCould; i++) {
|
||||||
|
View child = mainLayout.getChildAt(i);
|
||||||
|
if (child instanceof TextView childTextView && finalTitle.equals(childTextView.getText().toString())) {
|
||||||
|
childTextView.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
finalLevel.icon, 0, 0, 0);
|
||||||
|
childTextView.setCompoundDrawablePadding(dipToPixels(8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set dialog as non-cancelable.
|
||||||
|
dialog.setCancelable(false);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
Utils.showDialog(context, dialog);
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
final var message = "Failed to get announcement";
|
final var message = "Failed to get announcement";
|
||||||
|
@ -6,7 +6,7 @@ import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQual
|
|||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abuse LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
|
* LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
|
||||||
*/
|
*/
|
||||||
public final class AdvancedVideoQualityMenuFilter extends Filter {
|
public final class AdvancedVideoQualityMenuFilter extends Filter {
|
||||||
// Must be volatile or synchronized, as litho filtering runs off main thread
|
// Must be volatile or synchronized, as litho filtering runs off main thread
|
||||||
|
@ -13,8 +13,6 @@ import app.revanced.extension.youtube.settings.Settings;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This patch contains the logic to always open the advanced video quality menu.
|
* This patch contains the logic to always open the advanced video quality menu.
|
||||||
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
|
|
||||||
* and a ListView in the old one.
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class AdvancedVideoQualityMenuPatch {
|
public final class AdvancedVideoQualityMenuPatch {
|
||||||
@ -76,7 +74,7 @@ public final class AdvancedVideoQualityMenuPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*
|
*
|
||||||
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
|
* Shorts video quality flyout.
|
||||||
*/
|
*/
|
||||||
public static void showAdvancedVideoQualityMenu(ListView listView) {
|
public static void showAdvancedVideoQualityMenu(ListView listView) {
|
||||||
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
|
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
|
||||||
@ -90,14 +88,12 @@ public final class AdvancedVideoQualityMenuPatch {
|
|||||||
final var indexOfAdvancedQualityMenuItem = 4;
|
final var indexOfAdvancedQualityMenuItem = 4;
|
||||||
if (listView.indexOfChild(child) != indexOfAdvancedQualityMenuItem) return;
|
if (listView.indexOfChild(child) != indexOfAdvancedQualityMenuItem) return;
|
||||||
|
|
||||||
Logger.printDebug(() -> "Found advanced menu item in old type of quality menu");
|
|
||||||
|
|
||||||
listView.setSoundEffectsEnabled(false);
|
listView.setSoundEffectsEnabled(false);
|
||||||
final var qualityItemMenuPosition = 4;
|
final var qualityItemMenuPosition = 4;
|
||||||
listView.performItemClick(null, qualityItemMenuPosition, 0);
|
listView.performItemClick(null, qualityItemMenuPosition, 0);
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "showOldVideoQualityMenu failure", ex);
|
Logger.printException(() -> "showAdvancedVideoQualityMenu failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,15 @@ import android.graphics.drawable.ShapeDrawable;
|
|||||||
import android.graphics.drawable.shapes.RoundRectShape;
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
import android.icu.text.NumberFormat;
|
import android.icu.text.NumberFormat;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.TranslateAnimation;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.GridLayout;
|
import android.widget.GridLayout;
|
||||||
@ -39,7 +41,6 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||||
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilterPatch;
|
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilterPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
@ -240,6 +241,9 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
// Store the dialog reference.
|
// Store the dialog reference.
|
||||||
currentDialog = new WeakReference<>(dialog);
|
currentDialog = new WeakReference<>(dialog);
|
||||||
|
|
||||||
|
// Enable dismissing the dialog when tapping outside.
|
||||||
|
dialog.setCanceledOnTouchOutside(true);
|
||||||
|
|
||||||
// Create main vertical LinearLayout for dialog content.
|
// Create main vertical LinearLayout for dialog content.
|
||||||
LinearLayout mainLayout = new LinearLayout(context);
|
LinearLayout mainLayout = new LinearLayout(context);
|
||||||
mainLayout.setOrientation(LinearLayout.VERTICAL);
|
mainLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
@ -259,15 +263,15 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
|
|
||||||
// Set rounded rectangle background for the main layout.
|
// Set rounded rectangle background for the main layout.
|
||||||
RoundRectShape roundRectShape = new RoundRectShape(
|
RoundRectShape roundRectShape = new RoundRectShape(
|
||||||
createCornerRadii(12), null, null);
|
Utils.createCornerRadii(12), null, null);
|
||||||
ShapeDrawable background = new ShapeDrawable(roundRectShape);
|
ShapeDrawable background = new ShapeDrawable(roundRectShape);
|
||||||
background.getPaint().setColor(ThemeHelper.getDialogBackgroundColor());
|
background.getPaint().setColor(Utils.getDialogBackgroundColor());
|
||||||
mainLayout.setBackground(background);
|
mainLayout.setBackground(background);
|
||||||
|
|
||||||
// Add handle bar at the top.
|
// Add handle bar at the top.
|
||||||
View handleBar = new View(context);
|
View handleBar = new View(context);
|
||||||
ShapeDrawable handleBackground = new ShapeDrawable(new RoundRectShape(
|
ShapeDrawable handleBackground = new ShapeDrawable(new RoundRectShape(
|
||||||
createCornerRadii(4), null, null));
|
Utils.createCornerRadii(4), null, null));
|
||||||
handleBackground.getPaint().setColor(getAdjustedBackgroundColor(true));
|
handleBackground.getPaint().setColor(getAdjustedBackgroundColor(true));
|
||||||
handleBar.setBackground(handleBackground);
|
handleBar.setBackground(handleBackground);
|
||||||
LinearLayout.LayoutParams handleParams = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams handleParams = new LinearLayout.LayoutParams(
|
||||||
@ -285,7 +289,7 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
float currentSpeed = VideoInformation.getPlaybackSpeed();
|
float currentSpeed = VideoInformation.getPlaybackSpeed();
|
||||||
// Initially show with only 0 minimum digits, so 1.0 shows as 1x
|
// Initially show with only 0 minimum digits, so 1.0 shows as 1x
|
||||||
currentSpeedText.setText(formatSpeedStringX(currentSpeed, 0));
|
currentSpeedText.setText(formatSpeedStringX(currentSpeed, 0));
|
||||||
currentSpeedText.setTextColor(ThemeHelper.getForegroundColor());
|
currentSpeedText.setTextColor(Utils.getAppForegroundColor());
|
||||||
currentSpeedText.setTextSize(16);
|
currentSpeedText.setTextSize(16);
|
||||||
currentSpeedText.setTypeface(Typeface.DEFAULT_BOLD);
|
currentSpeedText.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
currentSpeedText.setGravity(Gravity.CENTER);
|
currentSpeedText.setGravity(Gravity.CENTER);
|
||||||
@ -305,7 +309,8 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
// Create minus button.
|
// Create minus button.
|
||||||
Button minusButton = new Button(context, null, 0); // Disable default theme style.
|
Button minusButton = new Button(context, null, 0); // Disable default theme style.
|
||||||
minusButton.setText(""); // No text on button.
|
minusButton.setText(""); // No text on button.
|
||||||
ShapeDrawable minusBackground = new ShapeDrawable(new RoundRectShape(createCornerRadii(20), null, null));
|
ShapeDrawable minusBackground = new ShapeDrawable(new RoundRectShape(
|
||||||
|
Utils.createCornerRadii(20), null, null));
|
||||||
minusBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
minusBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
||||||
minusButton.setBackground(minusBackground);
|
minusButton.setBackground(minusBackground);
|
||||||
OutlineSymbolDrawable minusDrawable = new OutlineSymbolDrawable(false); // Minus symbol.
|
OutlineSymbolDrawable minusDrawable = new OutlineSymbolDrawable(false); // Minus symbol.
|
||||||
@ -319,9 +324,9 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
speedSlider.setMax(speedToProgressValue(customPlaybackSpeedsMax));
|
speedSlider.setMax(speedToProgressValue(customPlaybackSpeedsMax));
|
||||||
speedSlider.setProgress(speedToProgressValue(currentSpeed));
|
speedSlider.setProgress(speedToProgressValue(currentSpeed));
|
||||||
speedSlider.getProgressDrawable().setColorFilter(
|
speedSlider.getProgressDrawable().setColorFilter(
|
||||||
ThemeHelper.getForegroundColor(), PorterDuff.Mode.SRC_IN); // Theme progress bar.
|
Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN); // Theme progress bar.
|
||||||
speedSlider.getThumb().setColorFilter(
|
speedSlider.getThumb().setColorFilter(
|
||||||
ThemeHelper.getForegroundColor(), PorterDuff.Mode.SRC_IN); // Theme slider thumb.
|
Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN); // Theme slider thumb.
|
||||||
LinearLayout.LayoutParams sliderParams = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams sliderParams = new LinearLayout.LayoutParams(
|
||||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
|
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
|
||||||
sliderParams.setMargins(dip5, 0, dip5, 0); // 5dp to -/+ buttons.
|
sliderParams.setMargins(dip5, 0, dip5, 0); // 5dp to -/+ buttons.
|
||||||
@ -331,7 +336,7 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
Button plusButton = new Button(context, null, 0); // Disable default theme style.
|
Button plusButton = new Button(context, null, 0); // Disable default theme style.
|
||||||
plusButton.setText(""); // No text on button.
|
plusButton.setText(""); // No text on button.
|
||||||
ShapeDrawable plusBackground = new ShapeDrawable(new RoundRectShape(
|
ShapeDrawable plusBackground = new ShapeDrawable(new RoundRectShape(
|
||||||
createCornerRadii(20), null, null));
|
Utils.createCornerRadii(20), null, null));
|
||||||
plusBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
plusBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
||||||
plusButton.setBackground(plusBackground);
|
plusButton.setBackground(plusBackground);
|
||||||
OutlineSymbolDrawable plusDrawable = new OutlineSymbolDrawable(true); // Plus symbol.
|
OutlineSymbolDrawable plusDrawable = new OutlineSymbolDrawable(true); // Plus symbol.
|
||||||
@ -418,13 +423,13 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
// Create speed button.
|
// Create speed button.
|
||||||
Button speedButton = new Button(context, null, 0);
|
Button speedButton = new Button(context, null, 0);
|
||||||
speedButton.setText(speedFormatter.format(speed)); // Do not use 'x' speed format.
|
speedButton.setText(speedFormatter.format(speed)); // Do not use 'x' speed format.
|
||||||
speedButton.setTextColor(ThemeHelper.getForegroundColor());
|
speedButton.setTextColor(Utils.getAppForegroundColor());
|
||||||
speedButton.setTextSize(12);
|
speedButton.setTextSize(12);
|
||||||
speedButton.setAllCaps(false);
|
speedButton.setAllCaps(false);
|
||||||
speedButton.setGravity(Gravity.CENTER);
|
speedButton.setGravity(Gravity.CENTER);
|
||||||
|
|
||||||
ShapeDrawable buttonBackground = new ShapeDrawable(new RoundRectShape(
|
ShapeDrawable buttonBackground = new ShapeDrawable(new RoundRectShape(
|
||||||
createCornerRadii(20), null, null));
|
Utils.createCornerRadii(20), null, null));
|
||||||
buttonBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
buttonBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
|
||||||
speedButton.setBackground(buttonBackground);
|
speedButton.setBackground(buttonBackground);
|
||||||
speedButton.setPadding(dip5, dip5, dip5, dip5);
|
speedButton.setPadding(dip5, dip5, dip5, dip5);
|
||||||
@ -442,7 +447,7 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
TextView normalLabel = new TextView(context);
|
TextView normalLabel = new TextView(context);
|
||||||
// Use same 'Normal' string as stock YouTube.
|
// Use same 'Normal' string as stock YouTube.
|
||||||
normalLabel.setText(str("normal_playback_rate_label"));
|
normalLabel.setText(str("normal_playback_rate_label"));
|
||||||
normalLabel.setTextColor(ThemeHelper.getForegroundColor());
|
normalLabel.setTextColor(Utils.getAppForegroundColor());
|
||||||
normalLabel.setTextSize(10);
|
normalLabel.setTextSize(10);
|
||||||
normalLabel.setGravity(Gravity.CENTER);
|
normalLabel.setGravity(Gravity.CENTER);
|
||||||
|
|
||||||
@ -489,6 +494,77 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
window.setBackgroundDrawable(null); // Remove default dialog background.
|
window.setBackgroundDrawable(null); // Remove default dialog background.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply slide-in animation when showing the dialog.
|
||||||
|
final int fadeDurationFast = Utils.getResourceInteger("fade_duration_fast");
|
||||||
|
Animation slideInABottomAnimation = Utils.getResourceAnimation("slide_in_bottom");
|
||||||
|
slideInABottomAnimation.setDuration(fadeDurationFast);
|
||||||
|
mainLayout.startAnimation(slideInABottomAnimation);
|
||||||
|
|
||||||
|
// Set touch listener on mainLayout to enable drag-to-dismiss.
|
||||||
|
//noinspection ClickableViewAccessibility
|
||||||
|
mainLayout.setOnTouchListener(new View.OnTouchListener() {
|
||||||
|
/** Threshold for dismissing the dialog. */
|
||||||
|
final float dismissThreshold = dipToPixels(100); // Distance to drag to dismiss.
|
||||||
|
/** Store initial Y position of touch. */
|
||||||
|
float touchY;
|
||||||
|
/** Track current translation. */
|
||||||
|
float translationY;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
// Capture initial Y position of touch.
|
||||||
|
touchY = event.getRawY();
|
||||||
|
translationY = mainLayout.getTranslationY();
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
// Calculate drag distance and apply translation downwards only.
|
||||||
|
final float deltaY = event.getRawY() - touchY;
|
||||||
|
// Only allow downward drag (positive deltaY).
|
||||||
|
if (deltaY >= 0) {
|
||||||
|
mainLayout.setTranslationY(translationY + deltaY);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
// Check if dialog should be dismissed based on drag distance.
|
||||||
|
if (mainLayout.getTranslationY() > dismissThreshold) {
|
||||||
|
// Animate dialog off-screen and dismiss.
|
||||||
|
//noinspection ExtractMethodRecommender
|
||||||
|
final float remainingDistance = context.getResources().getDisplayMetrics().heightPixels
|
||||||
|
- mainLayout.getTop();
|
||||||
|
TranslateAnimation slideOut = new TranslateAnimation(
|
||||||
|
0, 0, mainLayout.getTranslationY(), remainingDistance);
|
||||||
|
slideOut.setDuration(fadeDurationFast);
|
||||||
|
slideOut.setAnimationListener(new Animation.AnimationListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart(Animation animation) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animation animation) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationRepeat(Animation animation) {}
|
||||||
|
});
|
||||||
|
mainLayout.startAnimation(slideOut);
|
||||||
|
} else {
|
||||||
|
// Animate back to original position if not dragged far enough.
|
||||||
|
TranslateAnimation slideBack = new TranslateAnimation(
|
||||||
|
0, 0, mainLayout.getTranslationY(), 0);
|
||||||
|
slideBack.setDuration(fadeDurationFast);
|
||||||
|
mainLayout.startAnimation(slideBack);
|
||||||
|
mainLayout.setTranslationY(0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Create observer for PlayerType changes.
|
// Create observer for PlayerType changes.
|
||||||
Function1<PlayerType, Unit> playerTypeObserver = new Function1<>() {
|
Function1<PlayerType, Unit> playerTypeObserver = new Function1<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -515,27 +591,9 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
Logger.printDebug(() -> "PlayerType observer removed on dialog dismiss");
|
Logger.printDebug(() -> "PlayerType observer removed on dialog dismiss");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply slide-in animation when showing the dialog.
|
|
||||||
final int fadeDurationFast = Utils.getResourceInteger("fade_duration_fast");
|
|
||||||
Animation slideInABottomAnimation = Utils.getResourceAnimation("slide_in_bottom");
|
|
||||||
slideInABottomAnimation.setDuration(fadeDurationFast);
|
|
||||||
mainLayout.startAnimation(slideInABottomAnimation);
|
|
||||||
|
|
||||||
dialog.show(); // Display the dialog.
|
dialog.show(); // Display the dialog.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an array of corner radii for a rounded rectangle shape.
|
|
||||||
*
|
|
||||||
* @param dp The radius in density-independent pixels (dp) to apply to all corners.
|
|
||||||
* @return An array of eight float values representing the corner radii
|
|
||||||
* (top-left, top-right, bottom-right, bottom-left).
|
|
||||||
*/
|
|
||||||
private static float[] createCornerRadii(float dp) {
|
|
||||||
final float radius = dipToPixels(dp);
|
|
||||||
return new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param speed The playback speed value to format.
|
* @param speed The playback speed value to format.
|
||||||
* @return A string representation of the speed with 'x' (e.g. "1.25x" or "1.00x").
|
* @return A string representation of the speed with 'x' (e.g. "1.25x" or "1.00x").
|
||||||
@ -573,12 +631,12 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
* for light themes to ensure visual contrast.
|
* for light themes to ensure visual contrast.
|
||||||
*/
|
*/
|
||||||
public static int getAdjustedBackgroundColor(boolean isHandleBar) {
|
public static int getAdjustedBackgroundColor(boolean isHandleBar) {
|
||||||
final int baseColor = ThemeHelper.getDialogBackgroundColor();
|
final int baseColor = Utils.getDialogBackgroundColor();
|
||||||
float darkThemeFactor = isHandleBar ? 1.25f : 1.115f; // 1.25f for handleBar, 1.115f for others in dark theme.
|
float darkThemeFactor = isHandleBar ? 1.25f : 1.115f; // 1.25f for handleBar, 1.115f for others in dark theme.
|
||||||
float lightThemeFactor = isHandleBar ? 0.9f : 0.95f; // 0.9f for handleBar, 0.95f for others in light theme.
|
float lightThemeFactor = isHandleBar ? 0.9f : 0.95f; // 0.9f for handleBar, 0.95f for others in light theme.
|
||||||
return ThemeHelper.isDarkTheme()
|
return Utils.isDarkModeEnabled()
|
||||||
? ThemeHelper.adjustColorBrightness(baseColor, darkThemeFactor) // Lighten for dark theme.
|
? Utils.adjustColorBrightness(baseColor, darkThemeFactor) // Lighten for dark theme.
|
||||||
: ThemeHelper.adjustColorBrightness(baseColor, lightThemeFactor); // Darken for light theme.
|
: Utils.adjustColorBrightness(baseColor, lightThemeFactor); // Darken for light theme.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +650,7 @@ class OutlineSymbolDrawable extends Drawable {
|
|||||||
OutlineSymbolDrawable(boolean isPlus) {
|
OutlineSymbolDrawable(boolean isPlus) {
|
||||||
this.isPlus = isPlus;
|
this.isPlus = isPlus;
|
||||||
paint = new Paint(Paint.ANTI_ALIAS_FLAG); // Enable anti-aliasing for smooth rendering.
|
paint = new Paint(Paint.ANTI_ALIAS_FLAG); // Enable anti-aliasing for smooth rendering.
|
||||||
paint.setColor(ThemeHelper.getForegroundColor());
|
paint.setColor(Utils.getAppForegroundColor());
|
||||||
paint.setStyle(Paint.Style.STROKE); // Use stroke style for outline.
|
paint.setStyle(Paint.Style.STROKE); // Use stroke style for outline.
|
||||||
paint.setStrokeWidth(dipToPixels(1)); // 1dp stroke width.
|
paint.setStrokeWidth(dipToPixels(1)); // 1dp stroke width.
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ -75,7 +74,7 @@ public class ThemePatch {
|
|||||||
* @return The new or original color value
|
* @return The new or original color value
|
||||||
*/
|
*/
|
||||||
public static int getValue(int originalValue) {
|
public static int getValue(int originalValue) {
|
||||||
if (ThemeHelper.isDarkTheme()) {
|
if (Utils.isDarkModeEnabled()) {
|
||||||
if (anyEquals(originalValue, DARK_VALUES)) return BLACK_COLOR;
|
if (anyEquals(originalValue, DARK_VALUES)) return BLACK_COLOR;
|
||||||
} else {
|
} else {
|
||||||
if (anyEquals(originalValue, WHITE_VALUES)) return WHITE_COLOR;
|
if (anyEquals(originalValue, WHITE_VALUES)) return WHITE_COLOR;
|
||||||
|
@ -30,11 +30,15 @@ import java.util.HashMap;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData;
|
import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData;
|
||||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
@ -177,7 +181,7 @@ public class ReturnYouTubeDislike {
|
|||||||
* Ideally, this would be the actual color YT uses at runtime.
|
* Ideally, this would be the actual color YT uses at runtime.
|
||||||
*/
|
*/
|
||||||
private static int getSeparatorColor() {
|
private static int getSeparatorColor() {
|
||||||
return ThemeHelper.isDarkTheme()
|
return Utils.isDarkModeEnabled()
|
||||||
? 0x33FFFFFF
|
? 0x33FFFFFF
|
||||||
: 0xFFD9D9D9;
|
: 0xFFD9D9D9;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import app.revanced.extension.shared.Logger;
|
|||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.AppLanguage;
|
import app.revanced.extension.shared.settings.AppLanguage;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.patches.VersionCheckPatch;
|
import app.revanced.extension.youtube.patches.VersionCheckPatch;
|
||||||
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
|
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
|
||||||
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
||||||
@ -27,6 +26,8 @@ import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFrag
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class LicenseActivityHook {
|
public class LicenseActivityHook {
|
||||||
|
|
||||||
|
private static int currentThemeValueOrdinal = -1; // Must initially be a non-valid enum ordinal value.
|
||||||
|
|
||||||
private static ViewGroup.LayoutParams toolbarLayoutParams;
|
private static ViewGroup.LayoutParams toolbarLayoutParams;
|
||||||
|
|
||||||
public static void setToolbarLayoutParams(Toolbar toolbar) {
|
public static void setToolbarLayoutParams(Toolbar toolbar) {
|
||||||
@ -78,8 +79,8 @@ public class LicenseActivityHook {
|
|||||||
*/
|
*/
|
||||||
public static void initialize(Activity licenseActivity) {
|
public static void initialize(Activity licenseActivity) {
|
||||||
try {
|
try {
|
||||||
ThemeHelper.setActivityTheme(licenseActivity);
|
setActivityTheme(licenseActivity);
|
||||||
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
|
ReVancedPreferenceFragment.setNavigationBarColor(licenseActivity.getWindow());
|
||||||
licenseActivity.setContentView(getResourceIdentifier(
|
licenseActivity.setContentView(getResourceIdentifier(
|
||||||
"revanced_settings_with_toolbar", "layout"));
|
"revanced_settings_with_toolbar", "layout"));
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ public class LicenseActivityHook {
|
|||||||
toolBarParent.removeView(dummyToolbar);
|
toolBarParent.removeView(dummyToolbar);
|
||||||
|
|
||||||
Toolbar toolbar = new Toolbar(toolBarParent.getContext());
|
Toolbar toolbar = new Toolbar(toolBarParent.getContext());
|
||||||
toolbar.setBackgroundColor(ThemeHelper.getToolbarBackgroundColor());
|
toolbar.setBackgroundColor(getToolbarBackgroundColor());
|
||||||
toolbar.setNavigationIcon(ReVancedPreferenceFragment.getBackButtonDrawable());
|
toolbar.setNavigationIcon(ReVancedPreferenceFragment.getBackButtonDrawable());
|
||||||
toolbar.setTitle(getResourceIdentifier("revanced_settings_title", "string"));
|
toolbar.setTitle(getResourceIdentifier("revanced_settings_title", "string"));
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ public class LicenseActivityHook {
|
|||||||
TextView toolbarTextView = Utils.getChildView(toolbar, false,
|
TextView toolbarTextView = Utils.getChildView(toolbar, false,
|
||||||
view -> view instanceof TextView);
|
view -> view instanceof TextView);
|
||||||
if (toolbarTextView != null) {
|
if (toolbarTextView != null) {
|
||||||
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
|
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
|
||||||
}
|
}
|
||||||
setToolbarLayoutParams(toolbar);
|
setToolbarLayoutParams(toolbar);
|
||||||
|
|
||||||
@ -135,4 +136,34 @@ public class LicenseActivityHook {
|
|||||||
|
|
||||||
toolBarParent.addView(toolbar, 0);
|
toolBarParent.addView(toolbar, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setActivityTheme(Activity activity) {
|
||||||
|
final var theme = Utils.isDarkModeEnabled()
|
||||||
|
? "Theme.YouTube.Settings.Dark"
|
||||||
|
: "Theme.YouTube.Settings";
|
||||||
|
activity.setTheme(getResourceIdentifier(theme, "style"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getToolbarBackgroundColor() {
|
||||||
|
final String colorName = Utils.isDarkModeEnabled()
|
||||||
|
? "yt_black3"
|
||||||
|
: "yt_white1";
|
||||||
|
|
||||||
|
return Utils.getColorFromString(colorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*
|
||||||
|
* Updates dark/light mode since YT settings can force light/dark mode
|
||||||
|
* which can differ from the global device settings.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static void updateLightDarkModeStatus(Enum<?> value) {
|
||||||
|
final int themeOrdinal = value.ordinal();
|
||||||
|
if (currentThemeValueOrdinal != themeOrdinal) {
|
||||||
|
currentThemeValueOrdinal = themeOrdinal;
|
||||||
|
Utils.setIsDarkModeEnabled(themeOrdinal == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,10 @@ import static app.revanced.extension.shared.StringRef.str;
|
|||||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
@ -31,7 +32,6 @@ import app.revanced.extension.shared.Utils;
|
|||||||
import app.revanced.extension.shared.settings.AppLanguage;
|
import app.revanced.extension.shared.settings.AppLanguage;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,10 +58,10 @@ public class SearchViewController {
|
|||||||
GradientDrawable background = new GradientDrawable();
|
GradientDrawable background = new GradientDrawable();
|
||||||
background.setShape(GradientDrawable.RECTANGLE);
|
background.setShape(GradientDrawable.RECTANGLE);
|
||||||
background.setCornerRadius(28 * context.getResources().getDisplayMetrics().density); // 28dp corner radius.
|
background.setCornerRadius(28 * context.getResources().getDisplayMetrics().density); // 28dp corner radius.
|
||||||
int baseColor = ThemeHelper.getBackgroundColor();
|
int baseColor = Utils.getAppBackgroundColor();
|
||||||
int adjustedColor = ThemeHelper.isDarkTheme()
|
int adjustedColor = Utils.isDarkModeEnabled()
|
||||||
? ThemeHelper.adjustColorBrightness(baseColor, 1.11f) // Lighten for dark theme.
|
? Utils.adjustColorBrightness(baseColor, 1.11f) // Lighten for dark theme.
|
||||||
: ThemeHelper.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme.
|
: Utils.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme.
|
||||||
background.setColor(adjustedColor);
|
background.setColor(adjustedColor);
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
@ -171,10 +171,6 @@ public class SearchViewController {
|
|||||||
final int actionSearchId = getResourceIdentifier("action_search", "id");
|
final int actionSearchId = getResourceIdentifier("action_search", "id");
|
||||||
toolbar.inflateMenu(getResourceIdentifier("revanced_search_menu", "menu"));
|
toolbar.inflateMenu(getResourceIdentifier("revanced_search_menu", "menu"));
|
||||||
MenuItem searchItem = toolbar.getMenu().findItem(actionSearchId);
|
MenuItem searchItem = toolbar.getMenu().findItem(actionSearchId);
|
||||||
searchItem.setIcon(getResourceIdentifier(ThemeHelper.isDarkTheme()
|
|
||||||
? "yt_outline_search_white_24"
|
|
||||||
: "yt_outline_search_black_24",
|
|
||||||
"drawable")).setTooltipText(null);
|
|
||||||
|
|
||||||
// Set menu item click listener.
|
// Set menu item click listener.
|
||||||
toolbar.setOnMenuItemClickListener(item -> {
|
toolbar.setOnMenuItemClickListener(item -> {
|
||||||
@ -316,12 +312,7 @@ public class SearchViewController {
|
|||||||
private void closeSearch() {
|
private void closeSearch() {
|
||||||
isSearchActive = false;
|
isSearchActive = false;
|
||||||
toolbar.getMenu().findItem(getResourceIdentifier(
|
toolbar.getMenu().findItem(getResourceIdentifier(
|
||||||
"action_search", "id"))
|
"action_search", "id")).setVisible(true);
|
||||||
.setIcon(getResourceIdentifier(ThemeHelper.isDarkTheme()
|
|
||||||
? "yt_outline_search_white_24"
|
|
||||||
: "yt_outline_search_black_24",
|
|
||||||
"drawable")
|
|
||||||
).setVisible(true);
|
|
||||||
toolbar.setTitle(originalTitle);
|
toolbar.setTitle(originalTitle);
|
||||||
searchContainer.setVisibility(View.GONE);
|
searchContainer.setVisibility(View.GONE);
|
||||||
searchView.setQuery("", false);
|
searchView.setQuery("", false);
|
||||||
@ -365,13 +356,22 @@ public class SearchViewController {
|
|||||||
|
|
||||||
// Set long click listener for deletion confirmation.
|
// Set long click listener for deletion confirmation.
|
||||||
convertView.setOnLongClickListener(v -> {
|
convertView.setOnLongClickListener(v -> {
|
||||||
new AlertDialog.Builder(activity)
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setTitle(query)
|
activity,
|
||||||
.setMessage(str("revanced_settings_search_remove_message"))
|
query, // Title.
|
||||||
.setPositiveButton(android.R.string.ok,
|
str("revanced_settings_search_remove_message"), // Message.
|
||||||
(dialog, which) -> removeSearchQuery(query))
|
null, // No EditText.
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
null, // OK button text.
|
||||||
.show();
|
() -> removeSearchQuery(query), // OK button action.
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
null, // No Neutral button text.
|
||||||
|
() -> {}, // Neutral button action (dismiss only).
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
dialog.setCancelable(true); // Allow dismissal via back button.
|
||||||
|
dialog.show(); // Show the dialog.
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,17 +3,18 @@ package app.revanced.extension.youtube.settings.preference;
|
|||||||
import static app.revanced.extension.shared.StringRef.sf;
|
import static app.revanced.extension.shared.StringRef.sf;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
|
||||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A custom ListPreference that uses a styled custom dialog with a custom checkmark indicator.
|
||||||
* Custom video speeds used by {@link CustomPlaybackSpeedPatch}.
|
* Custom video speeds used by {@link CustomPlaybackSpeedPatch}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public final class CustomVideoSpeedListPreference extends ListPreference {
|
public final class CustomVideoSpeedListPreference extends CustomDialogListPreference {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a settings preference list with the available playback speeds.
|
* Initialize a settings preference list with the available playback speeds.
|
||||||
@ -59,4 +60,5 @@ public final class CustomVideoSpeedListPreference extends ListPreference {
|
|||||||
public CustomVideoSpeedListPreference(Context context) {
|
public CustomVideoSpeedListPreference(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import android.text.SpannableStringBuilder;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.BackgroundColorSpan;
|
import android.text.style.BackgroundColorSpan;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
@ -40,7 +41,6 @@ import app.revanced.extension.shared.Utils;
|
|||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
|
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
|
||||||
import app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory;
|
import app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
import app.revanced.extension.youtube.settings.LicenseActivityHook;
|
import app.revanced.extension.youtube.settings.LicenseActivityHook;
|
||||||
import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGroup;
|
import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGroup;
|
||||||
|
|
||||||
@ -71,11 +71,27 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
|
|
||||||
@SuppressLint("UseCompatLoadingForDrawables")
|
@SuppressLint("UseCompatLoadingForDrawables")
|
||||||
public static Drawable getBackButtonDrawable() {
|
public static Drawable getBackButtonDrawable() {
|
||||||
final int backButtonResource = getResourceIdentifier(ThemeHelper.isDarkTheme()
|
final int backButtonResource = getResourceIdentifier("revanced_settings_toolbar_arrow_left", "drawable");
|
||||||
? "yt_outline_arrow_left_white_24"
|
Drawable drawable = Utils.getContext().getResources().getDrawable(backButtonResource);
|
||||||
: "yt_outline_arrow_left_black_24",
|
drawable.setTint(Utils.getAppForegroundColor());
|
||||||
"drawable");
|
return drawable;
|
||||||
return Utils.getContext().getResources().getDrawable(backButtonResource);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the system navigation bar color for the activity.
|
||||||
|
* Applies the background color obtained from {@link Utils#getAppBackgroundColor()} to the navigation bar.
|
||||||
|
* For Android 10 (API 29) and above, enforces navigation bar contrast to ensure visibility.
|
||||||
|
*/
|
||||||
|
public static void setNavigationBarColor(@Nullable Window window) {
|
||||||
|
if (window == null) {
|
||||||
|
Logger.printDebug(() -> "Cannot set navigation bar color, window is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setNavigationBarColor(Utils.getAppBackgroundColor());
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
window.setNavigationBarContrastEnforced(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,9 +217,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
// Set icon for the placeholder preference.
|
// Set icon for the placeholder preference.
|
||||||
noResultsPreference.setLayoutResource(getResourceIdentifier(
|
noResultsPreference.setLayoutResource(getResourceIdentifier(
|
||||||
"revanced_preference_with_icon_no_search_result", "layout"));
|
"revanced_preference_with_icon_no_search_result", "layout"));
|
||||||
noResultsPreference.setIcon(getResourceIdentifier(
|
noResultsPreference.setIcon(getResourceIdentifier("revanced_settings_search_icon", "drawable"));
|
||||||
ThemeHelper.isDarkTheme() ? "yt_outline_search_white_24" : "yt_outline_search_black_24",
|
|
||||||
"drawable"));
|
|
||||||
preferenceScreen.addPreference(noResultsPreference);
|
preferenceScreen.addPreference(noResultsPreference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,7 +240,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
.getParent();
|
.getParent();
|
||||||
|
|
||||||
// Fix the system navigation bar color for submenus.
|
// Fix the system navigation bar color for submenus.
|
||||||
ThemeHelper.setNavigationBarColor(preferenceScreenDialog.getWindow());
|
setNavigationBarColor(preferenceScreenDialog.getWindow());
|
||||||
|
|
||||||
// Fix edge-to-edge screen with Android 15 and YT 19.45+
|
// Fix edge-to-edge screen with Android 15 and YT 19.45+
|
||||||
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
|
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
|
||||||
@ -250,7 +264,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
TextView toolbarTextView = Utils.getChildView(toolbar,
|
TextView toolbarTextView = Utils.getChildView(toolbar,
|
||||||
true, TextView.class::isInstance);
|
true, TextView.class::isInstance);
|
||||||
if (toolbarTextView != null) {
|
if (toolbarTextView != null) {
|
||||||
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
|
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
LicenseActivityHook.setToolbarLayoutParams(toolbar);
|
LicenseActivityHook.setToolbarLayoutParams(toolbar);
|
||||||
@ -304,10 +318,10 @@ class AbstractPreferenceSearchData<T extends Preference> {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int baseColor = ThemeHelper.getBackgroundColor();
|
final int baseColor = Utils.getAppBackgroundColor();
|
||||||
final int adjustedColor = ThemeHelper.isDarkTheme()
|
final int adjustedColor = Utils.isDarkModeEnabled()
|
||||||
? ThemeHelper.adjustColorBrightness(baseColor, 1.20f) // Lighten for dark theme.
|
? Utils.adjustColorBrightness(baseColor, 1.20f) // Lighten for dark theme.
|
||||||
: ThemeHelper.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme.
|
: Utils.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme.
|
||||||
BackgroundColorSpan highlightSpan = new BackgroundColorSpan(adjustedColor);
|
BackgroundColorSpan highlightSpan = new BackgroundColorSpan(adjustedColor);
|
||||||
|
|
||||||
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
|
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.settings.preference;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class ReVancedYouTubeAboutPreference extends ReVancedAboutPreference {
|
|
||||||
|
|
||||||
public int getLightColor() {
|
|
||||||
return ThemeHelper.getLightThemeColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDarkColor() {
|
|
||||||
return ThemeHelper.getDarkThemeColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
public ReVancedYouTubeAboutPreference(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,9 +2,11 @@ package app.revanced.extension.youtube.sponsorblock;
|
|||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -56,7 +58,7 @@ public class SponsorBlockSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < categorySelectionsArray.length(); i++) {
|
for (int i = 0, length = categorySelectionsArray.length(); i < length; i++) {
|
||||||
JSONObject categorySelectionObject = categorySelectionsArray.getJSONObject(i);
|
JSONObject categorySelectionObject = categorySelectionsArray.getJSONObject(i);
|
||||||
|
|
||||||
String categoryKey = categorySelectionObject.getString("name");
|
String categoryKey = categorySelectionObject.getString("name");
|
||||||
@ -181,13 +183,25 @@ public class SponsorBlockSettings {
|
|||||||
// If user has a SponsorBlock user id then show a warning.
|
// If user has a SponsorBlock user id then show a warning.
|
||||||
if (dialogContext != null && SponsorBlockSettings.userHasSBPrivateId()
|
if (dialogContext != null && SponsorBlockSettings.userHasSBPrivateId()
|
||||||
&& !Settings.SB_HIDE_EXPORT_WARNING.get()) {
|
&& !Settings.SB_HIDE_EXPORT_WARNING.get()) {
|
||||||
new AlertDialog.Builder(dialogContext)
|
// Create the custom dialog.
|
||||||
.setMessage(str("revanced_sb_settings_revanced_export_user_id_warning"))
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setNeutralButton(str("revanced_sb_settings_revanced_export_user_id_warning_dismiss"),
|
dialogContext,
|
||||||
(dialog, which) -> Settings.SB_HIDE_EXPORT_WARNING.save(true))
|
null, // No title.
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
str("revanced_sb_settings_revanced_export_user_id_warning"), // Message.
|
||||||
.setCancelable(false)
|
null, // No EditText.
|
||||||
.show();
|
null, // OK button text.
|
||||||
|
() -> {}, // OK button action (dismiss only).
|
||||||
|
null, // No cancel button action.
|
||||||
|
str("revanced_sb_settings_revanced_export_user_id_warning_dismiss"), // Neutral button text.
|
||||||
|
() -> Settings.SB_HIDE_EXPORT_WARNING.save(true), // Neutral button action.
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set dialog as non-cancelable.
|
||||||
|
dialogPair.first.setCancelable(false);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@ package app.revanced.extension.youtube.sponsorblock.objects;
|
|||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||||
|
import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||||
import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.getColorString;
|
import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.getColorString;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
|
import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -15,12 +15,14 @@ import android.preference.ListPreference;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.GridLayout;
|
import android.widget.GridLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
@ -52,6 +54,7 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
private EditText dialogColorEditText;
|
private EditText dialogColorEditText;
|
||||||
private EditText dialogOpacityEditText;
|
private EditText dialogOpacityEditText;
|
||||||
private ColorPickerView dialogColorPickerView;
|
private ColorPickerView dialogColorPickerView;
|
||||||
|
private Dialog dialog;
|
||||||
|
|
||||||
public SegmentCategoryListPreference(Context context, SegmentCategory category) {
|
public SegmentCategoryListPreference(Context context, SegmentCategory category) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -75,30 +78,47 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void showDialog(Bundle state) {
|
||||||
try {
|
try {
|
||||||
Utils.setEditTextDialogTheme(builder);
|
Context context = getContext();
|
||||||
|
|
||||||
categoryColor = category.getColorNoOpacity();
|
categoryColor = category.getColorNoOpacity();
|
||||||
categoryOpacity = category.getOpacity();
|
categoryOpacity = category.getOpacity();
|
||||||
|
selectedDialogEntryIndex = findIndexOfValue(getValue());
|
||||||
|
|
||||||
Context context = builder.getContext();
|
// Create the main layout for the dialog content.
|
||||||
LinearLayout mainLayout = new LinearLayout(context);
|
LinearLayout contentLayout = new LinearLayout(context);
|
||||||
mainLayout.setOrientation(LinearLayout.VERTICAL);
|
contentLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
mainLayout.setPadding(70, 0, 70, 0);
|
final int dip10 = dipToPixels(10);
|
||||||
|
contentLayout.setPadding(0, 0, 0, dip10);
|
||||||
|
|
||||||
|
// Add behavior selection radio buttons.
|
||||||
|
RadioGroup radioGroup = new RadioGroup(context);
|
||||||
|
radioGroup.setOrientation(RadioGroup.VERTICAL);
|
||||||
|
CharSequence[] entries = getEntries();
|
||||||
|
for (int i = 0; i < entries.length; i++) {
|
||||||
|
RadioButton radioButton = new RadioButton(context);
|
||||||
|
radioButton.setText(entries[i]);
|
||||||
|
radioButton.setId(i);
|
||||||
|
radioButton.setChecked(i == selectedDialogEntryIndex);
|
||||||
|
radioGroup.addView(radioButton);
|
||||||
|
}
|
||||||
|
radioGroup.setOnCheckedChangeListener((group, checkedId) -> selectedDialogEntryIndex = checkedId);
|
||||||
|
radioGroup.setPadding(dip10, 0, 0, 0);
|
||||||
|
contentLayout.addView(radioGroup);
|
||||||
|
|
||||||
// Inflate the color picker view.
|
// Inflate the color picker view.
|
||||||
View colorPickerContainer = LayoutInflater.from(context)
|
View colorPickerContainer = LayoutInflater.from(context)
|
||||||
.inflate(getResourceIdentifier("revanced_color_picker", "layout"), null);
|
.inflate(getResourceIdentifier("revanced_color_picker", "layout"), null);
|
||||||
dialogColorPickerView = colorPickerContainer.findViewById(
|
dialogColorPickerView = colorPickerContainer.findViewById(
|
||||||
getResourceIdentifier("color_picker_view", "id"));
|
getResourceIdentifier("revanced_color_picker_view", "id"));
|
||||||
dialogColorPickerView.setColor(categoryColor);
|
dialogColorPickerView.setColor(categoryColor);
|
||||||
mainLayout.addView(colorPickerContainer);
|
contentLayout.addView(colorPickerContainer);
|
||||||
|
|
||||||
// Grid layout for color and opacity inputs.
|
// Grid layout for color and opacity inputs.
|
||||||
GridLayout gridLayout = new GridLayout(context);
|
GridLayout gridLayout = new GridLayout(context);
|
||||||
gridLayout.setColumnCount(3);
|
gridLayout.setColumnCount(3);
|
||||||
gridLayout.setRowCount(2);
|
gridLayout.setRowCount(2);
|
||||||
|
gridLayout.setPadding(dipToPixels(16), 0, 0, 0);
|
||||||
|
|
||||||
GridLayout.LayoutParams gridParams = new GridLayout.LayoutParams();
|
GridLayout.LayoutParams gridParams = new GridLayout.LayoutParams();
|
||||||
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||||
@ -111,7 +131,7 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
gridParams = new GridLayout.LayoutParams();
|
gridParams = new GridLayout.LayoutParams();
|
||||||
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||||
gridParams.columnSpec = GridLayout.spec(1); // Second column.
|
gridParams.columnSpec = GridLayout.spec(1); // Second column.
|
||||||
gridParams.setMargins(0, 0, 10, 0);
|
gridParams.setMargins(0, 0, dip10, 0);
|
||||||
dialogColorDotView = new TextView(context);
|
dialogColorDotView = new TextView(context);
|
||||||
dialogColorDotView.setLayoutParams(gridParams);
|
dialogColorDotView.setLayoutParams(gridParams);
|
||||||
gridLayout.addView(dialogColorDotView);
|
gridLayout.addView(dialogColorDotView);
|
||||||
@ -162,8 +182,7 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dialogColorEditText.setLayoutParams(gridParams);
|
gridLayout.addView(dialogColorEditText, gridParams);
|
||||||
gridLayout.addView(dialogColorEditText);
|
|
||||||
|
|
||||||
gridParams = new GridLayout.LayoutParams();
|
gridParams = new GridLayout.LayoutParams();
|
||||||
gridParams.rowSpec = GridLayout.spec(1); // Second row.
|
gridParams.rowSpec = GridLayout.spec(1); // Second row.
|
||||||
@ -226,11 +245,10 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dialogOpacityEditText.setLayoutParams(gridParams);
|
gridLayout.addView(dialogOpacityEditText, gridParams);
|
||||||
gridLayout.addView(dialogOpacityEditText);
|
|
||||||
updateOpacityText();
|
updateOpacityText();
|
||||||
|
|
||||||
mainLayout.addView(gridLayout);
|
contentLayout.addView(gridLayout);
|
||||||
|
|
||||||
// Set up color picker listener.
|
// Set up color picker listener.
|
||||||
// Do last to prevent listener callbacks while setting up view.
|
// Do last to prevent listener callbacks while setting up view.
|
||||||
@ -247,69 +265,74 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
dialogColorEditText.setSelection(hexColor.length());
|
dialogColorEditText.setSelection(hexColor.length());
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.setView(mainLayout);
|
// Create the custom dialog.
|
||||||
builder.setTitle(category.title.toString());
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
context,
|
||||||
|
category.title.toString(), // Title.
|
||||||
|
null, // No message (replaced by contentLayout).
|
||||||
|
null, // No EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action.
|
||||||
|
if (selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
||||||
|
String value = getEntryValues()[selectedDialogEntryIndex].toString();
|
||||||
|
if (callChangeListener(value)) {
|
||||||
|
setValue(value);
|
||||||
|
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
|
||||||
|
SegmentCategory.updateEnabledCategories();
|
||||||
|
}
|
||||||
|
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
try {
|
||||||
onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
category.setColor(dialogColorEditText.getText().toString());
|
||||||
});
|
category.setOpacity(categoryOpacity);
|
||||||
builder.setNeutralButton(str("revanced_settings_reset_color"), null);
|
} catch (IllegalArgumentException ex) {
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
Utils.showToastShort(str("revanced_settings_color_invalid"));
|
||||||
|
}
|
||||||
|
|
||||||
selectedDialogEntryIndex = findIndexOfValue(getValue());
|
updateUI();
|
||||||
builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
|
}
|
||||||
(dialog, which) -> selectedDialogEntryIndex = which);
|
},
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
str("revanced_settings_reset_color"), // Neutral button text.
|
||||||
|
() -> {
|
||||||
|
// Neutral button action (Reset).
|
||||||
|
try {
|
||||||
|
// Setting view color causes callback to update the UI.
|
||||||
|
dialogColorPickerView.setColor(category.getColorNoOpacityDefault());
|
||||||
|
|
||||||
|
categoryOpacity = category.getOpacityDefault();
|
||||||
|
updateOpacityText();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "resetButton onClick failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false // Do not dismiss dialog on Neutral button click.
|
||||||
|
);
|
||||||
|
|
||||||
|
dialog = dialogPair.first;
|
||||||
|
LinearLayout dialogMainLayout = dialogPair.second;
|
||||||
|
|
||||||
|
// Add the custom content to the dialog's main layout.
|
||||||
|
dialogMainLayout.addView(contentLayout, 1); // Add after title, before buttons.
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialog.show();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
Logger.printException(() -> "showDialog failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showDialog(Bundle state) {
|
|
||||||
super.showDialog(state);
|
|
||||||
|
|
||||||
// Do not close dialog when reset is pressed.
|
|
||||||
Button button = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_NEUTRAL);
|
|
||||||
button.setOnClickListener(view -> {
|
|
||||||
try {
|
|
||||||
// Setting view color causes callback to update the UI.
|
|
||||||
dialogColorPickerView.setColor(category.getColorNoOpacityDefault());
|
|
||||||
|
|
||||||
categoryOpacity = category.getOpacityDefault();
|
|
||||||
updateOpacityText();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "setOnClickListener failure", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDialogClosed(boolean positiveResult) {
|
protected void onDialogClosed(boolean positiveResult) {
|
||||||
try {
|
// Nullify dialog references.
|
||||||
if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
dialogColorDotView = null;
|
||||||
String value = getEntryValues()[selectedDialogEntryIndex].toString();
|
dialogColorEditText = null;
|
||||||
if (callChangeListener(value)) {
|
dialogOpacityEditText = null;
|
||||||
setValue(value);
|
dialogColorPickerView = null;
|
||||||
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
|
|
||||||
SegmentCategory.updateEnabledCategories();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if (dialog != null) {
|
||||||
category.setColor(dialogColorEditText.getText().toString());
|
dialog.dismiss();
|
||||||
category.setOpacity(categoryOpacity);
|
dialog = null;
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
Utils.showToastShort(str("revanced_settings_color_invalid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "onDialogClosed failure", ex);
|
|
||||||
} finally {
|
|
||||||
dialogColorDotView = null;
|
|
||||||
dialogColorEditText = null;
|
|
||||||
dialogOpacityEditText = null;
|
|
||||||
dialogColorPickerView = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,19 +3,25 @@ package app.revanced.extension.youtube.sponsorblock.ui;
|
|||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.preference.*;
|
import android.os.Bundle;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceCategory;
|
||||||
|
import android.preference.PreferenceGroup;
|
||||||
|
import android.preference.SwitchPreference;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -279,14 +285,26 @@ public class SponsorBlockPreferenceGroup extends PreferenceGroup {
|
|||||||
addNewSegment.setOnPreferenceChangeListener((preference1, o) -> {
|
addNewSegment.setOnPreferenceChangeListener((preference1, o) -> {
|
||||||
Boolean newValue = (Boolean) o;
|
Boolean newValue = (Boolean) o;
|
||||||
if (newValue && !Settings.SB_SEEN_GUIDELINES.get()) {
|
if (newValue && !Settings.SB_SEEN_GUIDELINES.get()) {
|
||||||
new AlertDialog.Builder(preference1.getContext())
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setTitle(str("revanced_sb_guidelines_popup_title"))
|
preference1.getContext(),
|
||||||
.setMessage(str("revanced_sb_guidelines_popup_content"))
|
str("revanced_sb_guidelines_popup_title"), // Title.
|
||||||
.setNegativeButton(str("revanced_sb_guidelines_popup_already_read"), null)
|
str("revanced_sb_guidelines_popup_content"), // Message.
|
||||||
.setPositiveButton(str("revanced_sb_guidelines_popup_open"), (dialogInterface, i) -> openGuidelines())
|
null, // No EditText.
|
||||||
.setOnDismissListener(dialog -> Settings.SB_SEEN_GUIDELINES.save(true))
|
str("revanced_sb_guidelines_popup_open"), // OK button text.
|
||||||
.setCancelable(false)
|
() -> openGuidelines(), // OK button action.
|
||||||
.show();
|
null, // Cancel button action.
|
||||||
|
str("revanced_sb_guidelines_popup_already_read"), // Neutral button text.
|
||||||
|
() -> {}, // Neutral button action (dismiss only).
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set dialog as non-cancelable.
|
||||||
|
dialogPair.first.setCancelable(false);
|
||||||
|
|
||||||
|
dialogPair.first.setOnDismissListener(dialog -> Settings.SB_SEEN_GUIDELINES.save(true));
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
}
|
}
|
||||||
Settings.SB_CREATE_NEW_SEGMENT.save(newValue);
|
Settings.SB_CREATE_NEW_SEGMENT.save(newValue);
|
||||||
updateUI();
|
updateUI();
|
||||||
@ -372,16 +390,52 @@ public class SponsorBlockPreferenceGroup extends PreferenceGroup {
|
|||||||
generalCategory.addPreference(minSegmentDuration);
|
generalCategory.addPreference(minSegmentDuration);
|
||||||
|
|
||||||
privateUserId = new EditTextPreference(context) {
|
privateUserId = new EditTextPreference(context) {
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
@Override
|
||||||
Utils.setEditTextDialogTheme(builder);
|
protected void showDialog(Bundle state) {
|
||||||
|
try {
|
||||||
|
Context context = getContext();
|
||||||
|
EditText editText = getEditText();
|
||||||
|
|
||||||
builder.setNeutralButton(str("revanced_sb_settings_copy"), (dialog, which) -> {
|
// Set initial EditText value to the current persisted value or empty string.
|
||||||
try {
|
String initialValue = getText() != null ? getText() : "";
|
||||||
Utils.setClipboard(getEditText().getText());
|
editText.setText(initialValue);
|
||||||
} catch (Exception ex) {
|
editText.setSelection(initialValue.length()); // Move cursor to end.
|
||||||
Logger.printException(() -> "Copy settings failure", ex);
|
|
||||||
}
|
// Create custom dialog.
|
||||||
});
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
|
context,
|
||||||
|
getTitle() != null ? getTitle().toString() : "", // Title.
|
||||||
|
null, // Message is replaced by EditText.
|
||||||
|
editText, // Pass the EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action. Persist the EditText value when OK is clicked.
|
||||||
|
String newValue = editText.getText().toString();
|
||||||
|
if (callChangeListener(newValue)) {
|
||||||
|
setText(newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
str("revanced_sb_settings_copy"), // Neutral button text (Copy).
|
||||||
|
() -> {
|
||||||
|
// Neutral button action (Copy).
|
||||||
|
try {
|
||||||
|
Utils.setClipboard(getEditText().getText());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Copy settings failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set dialog as cancelable.
|
||||||
|
dialogPair.first.setCancelable(true);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "showDialog failure", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
privateUserId.setTitle(str("revanced_sb_general_uuid"));
|
privateUserId.setTitle(str("revanced_sb_general_uuid"));
|
||||||
@ -407,51 +461,90 @@ public class SponsorBlockPreferenceGroup extends PreferenceGroup {
|
|||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
|
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
|
||||||
editText.setText(Settings.SB_API_URL.get());
|
editText.setText(Settings.SB_API_URL.get());
|
||||||
|
|
||||||
DialogInterface.OnClickListener urlChangeListener = (dialog, buttonPressed) -> {
|
// Create a custom dialog.
|
||||||
if (buttonPressed == DialogInterface.BUTTON_NEUTRAL) {
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
Settings.SB_API_URL.resetToDefault();
|
context,
|
||||||
Utils.showToastLong(str("revanced_sb_api_url_reset"));
|
str("revanced_sb_general_api_url"), // Title.
|
||||||
} else if (buttonPressed == DialogInterface.BUTTON_POSITIVE) {
|
null, // No message, EditText replaces it.
|
||||||
String serverAddress = editText.getText().toString();
|
editText, // Pass the EditText.
|
||||||
if (!SponsorBlockSettings.isValidSBServerAddress(serverAddress)) {
|
null, // OK button text.
|
||||||
Utils.showToastLong(str("revanced_sb_api_url_invalid"));
|
() -> {
|
||||||
} else if (!serverAddress.equals(Settings.SB_API_URL.get())) {
|
// OK button action.
|
||||||
Settings.SB_API_URL.save(serverAddress);
|
String serverAddress = editText.getText().toString();
|
||||||
Utils.showToastLong(str("revanced_sb_api_url_changed"));
|
if (!SponsorBlockSettings.isValidSBServerAddress(serverAddress)) {
|
||||||
}
|
Utils.showToastLong(str("revanced_sb_api_url_invalid"));
|
||||||
}
|
} else if (!serverAddress.equals(Settings.SB_API_URL.get())) {
|
||||||
};
|
Settings.SB_API_URL.save(serverAddress);
|
||||||
new AlertDialog.Builder(context)
|
Utils.showToastLong(str("revanced_sb_api_url_changed"));
|
||||||
.setTitle(apiUrl.getTitle())
|
}
|
||||||
.setView(editText)
|
},
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
() -> {}, // Cancel button action (dismiss dialog).
|
||||||
.setNeutralButton(str("revanced_settings_reset"), urlChangeListener)
|
str("revanced_settings_reset"), // Neutral (Reset) button text.
|
||||||
.setPositiveButton(android.R.string.ok, urlChangeListener)
|
() -> {
|
||||||
.show();
|
// Neutral button action.
|
||||||
|
Settings.SB_API_URL.resetToDefault();
|
||||||
|
Utils.showToastLong(str("revanced_sb_api_url_reset"));
|
||||||
|
},
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
generalCategory.addPreference(apiUrl);
|
generalCategory.addPreference(apiUrl);
|
||||||
|
|
||||||
importExport = new EditTextPreference(context) {
|
importExport = new EditTextPreference(context) {
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
@Override
|
||||||
Utils.setEditTextDialogTheme(builder);
|
protected void showDialog(Bundle state) {
|
||||||
|
try {
|
||||||
|
Context context = getContext();
|
||||||
|
EditText editText = getEditText();
|
||||||
|
|
||||||
builder.setNeutralButton(str("revanced_sb_settings_copy"), (dialog, which) -> {
|
// Create a custom dialog.
|
||||||
try {
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
Utils.setClipboard(getEditText().getText());
|
context,
|
||||||
} catch (Exception ex) {
|
str("revanced_sb_settings_ie"), // Title.
|
||||||
Logger.printException(() -> "Copy settings failure", ex);
|
null, // No message, EditText replaces it.
|
||||||
}
|
editText, // Pass the EditText.
|
||||||
});
|
str("revanced_settings_import"), // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action. Trigger OnPreferenceChangeListener.
|
||||||
|
String newValue = editText.getText().toString();
|
||||||
|
if (getOnPreferenceChangeListener() != null) {
|
||||||
|
getOnPreferenceChangeListener().onPreferenceChange(this, newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
str("revanced_sb_settings_copy"), // Neutral button text (Copy).
|
||||||
|
() -> {
|
||||||
|
// Neutral button action (Copy).
|
||||||
|
try {
|
||||||
|
Utils.setClipboard(editText.getText());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Copy settings failure", ex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "showDialog failure", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
importExport.setTitle(str("revanced_sb_settings_ie"));
|
importExport.setTitle(str("revanced_sb_settings_ie"));
|
||||||
// Summary is set in updateUI()
|
// Summary is set in updateUI().
|
||||||
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
|
EditText editText = importExport.getEditText();
|
||||||
|
editText.setInputType(InputType.TYPE_CLASS_TEXT
|
||||||
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||||
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
importExport.getEditText().setAutofillHints((String) null);
|
editText.setAutofillHints((String) null);
|
||||||
importExport.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
|
editText.setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
|
||||||
|
|
||||||
|
// Set preference listeners.
|
||||||
importExport.setOnPreferenceClickListener(preference1 -> {
|
importExport.setOnPreferenceClickListener(preference1 -> {
|
||||||
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
|
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
|
||||||
return true;
|
return true;
|
||||||
|
@ -3,7 +3,7 @@ package app.revanced.extension.youtube.sponsorblock.ui;
|
|||||||
import static android.text.Html.fromHtml;
|
import static android.text.Html.fromHtml;
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -11,6 +11,8 @@ import android.preference.EditTextPreference;
|
|||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceCategory;
|
import android.preference.PreferenceCategory;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -194,14 +196,26 @@ public class SponsorBlockStatsPreferenceCategory extends PreferenceCategory {
|
|||||||
updateStatsSelfSaved.run();
|
updateStatsSelfSaved.run();
|
||||||
|
|
||||||
preference.setOnPreferenceClickListener(preference1 -> {
|
preference.setOnPreferenceClickListener(preference1 -> {
|
||||||
new AlertDialog.Builder(preference1.getContext())
|
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
|
||||||
.setTitle(str("revanced_sb_stats_self_saved_reset_title"))
|
preference.getContext(),
|
||||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
str("revanced_sb_stats_self_saved_reset_title"), // Title.
|
||||||
|
null, // No message.
|
||||||
|
null, // No EditText.
|
||||||
|
null, // OK button text.
|
||||||
|
() -> {
|
||||||
|
// OK button action.
|
||||||
Settings.SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS.resetToDefault();
|
Settings.SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS.resetToDefault();
|
||||||
Settings.SB_LOCAL_TIME_SAVED_MILLISECONDS.resetToDefault();
|
Settings.SB_LOCAL_TIME_SAVED_MILLISECONDS.resetToDefault();
|
||||||
updateStatsSelfSaved.run();
|
updateStatsSelfSaved.run();
|
||||||
})
|
},
|
||||||
.setNegativeButton(android.R.string.no, null).show();
|
() -> {}, // Cancel button action (dismiss only).
|
||||||
|
null, // No neutral button.
|
||||||
|
null, // No neutral button action.
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show the dialog.
|
||||||
|
dialogPair.first.show();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -694,6 +694,7 @@ public final class app/revanced/patches/shared/misc/pairip/license/DisableLicens
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
||||||
|
public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
|
public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
@ -1678,6 +1679,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V
|
||||||
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
||||||
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V
|
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V
|
||||||
@ -1687,6 +1689,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V
|
||||||
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
||||||
public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V
|
public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.idaustria.detection.signature
|
package app.revanced.patches.idaustria.detection.signature
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofSignaturePatch = bytecodePatch(
|
val spoofSignaturePatch = bytecodePatch(
|
||||||
@ -24,12 +24,6 @@ val spoofSignaturePatch = bytecodePatch(
|
|||||||
"77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" +
|
"77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" +
|
||||||
"bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}"
|
"bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}"
|
||||||
|
|
||||||
spoofSignatureFingerprint.method.addInstructions(
|
spoofSignatureFingerprint.method.returnEarly(expectedSignature)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$expectedSignature"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.nunl.firebase
|
package app.revanced.patches.nunl.firebase
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofCertificatePatch = bytecodePatch(
|
val spoofCertificatePatch = bytecodePatch(
|
||||||
@ -12,13 +12,7 @@ val spoofCertificatePatch = bytecodePatch(
|
|||||||
|
|
||||||
execute {
|
execute {
|
||||||
getFingerprintHashForPackageFingerprints.forEach { fingerprint ->
|
getFingerprintHashForPackageFingerprints.forEach { fingerprint ->
|
||||||
fingerprint.method.addInstructions(
|
fingerprint.method.returnEarly("eae41fc018df2731a9b6ae1ac327da44a288667b")
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "eae41fc018df2731a9b6ae1ac327da44a288667b"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patches.photomath.detection.deviceid
|
package app.revanced.patches.photomath.detection.deviceid
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch
|
import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -15,12 +15,6 @@ val getDeviceIdPatch = bytecodePatch(
|
|||||||
compatibleWith("com.microblink.photomath")
|
compatibleWith("com.microblink.photomath")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
getDeviceIdFingerprint.method.replaceInstructions(
|
getDeviceIdFingerprint.method.returnEarly(Random.nextLong().toString(16))
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "${Random.nextLong().toString(16)}"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package app.revanced.patches.piccomafr.misc
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.stringOption
|
import app.revanced.patcher.patch.stringOption
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofAndroidDeviceIdPatch = bytecodePatch(
|
val spoofAndroidDeviceIdPatch = bytecodePatch(
|
||||||
@ -39,12 +40,6 @@ val spoofAndroidDeviceIdPatch = bytecodePatch(
|
|||||||
) { it!!.matches("[A-Fa-f0-9]{16}".toRegex()) }
|
) { it!!.matches("[A-Fa-f0-9]{16}".toRegex()) }
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
getAndroidIdFingerprint.method.addInstructions(
|
getAndroidIdFingerprint.method.returnEarly(androidDeviceId!!)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$androidDeviceId"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package app.revanced.patches.reddit.customclients.boostforreddit.api
|
package app.revanced.patches.reddit.customclients.boostforreddit.api
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
|
val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
|
||||||
@ -14,13 +14,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com")
|
|||||||
execute {
|
execute {
|
||||||
// region Patch client id.
|
// region Patch client id.
|
||||||
|
|
||||||
getClientIdFingerprint.method.addInstructions(
|
getClientIdFingerprint.method.returnEarly(clientId!!)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$clientId"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package app.revanced.patches.reddit.customclients.joeyforreddit.api
|
package app.revanced.patches.reddit.customclients.joeyforreddit.api
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
|
||||||
import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch
|
import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/authorize_callback") { clientIdOption ->
|
val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/authorize_callback") { clientIdOption ->
|
||||||
dependsOn(disablePiracyDetectionPatch)
|
dependsOn(disablePiracyDetectionPatch)
|
||||||
@ -19,13 +18,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/a
|
|||||||
execute {
|
execute {
|
||||||
// region Patch client id.
|
// region Patch client id.
|
||||||
|
|
||||||
getClientIdFingerprint.method.addInstructions(
|
getClientIdFingerprint.method.returnEarly(clientId!!)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$clientId"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@ -35,13 +28,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/a
|
|||||||
val randomName = (0..100000).random()
|
val randomName = (0..100000).random()
|
||||||
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
||||||
|
|
||||||
authUtilityUserAgentFingerprint.method.replaceInstructions(
|
authUtilityUserAgentFingerprint.method.returnEarly(userAgent)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$userAgent"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ package app.revanced.patches.reddit.customclients.redditisfun.api
|
|||||||
|
|
||||||
import app.revanced.patcher.Fingerprint
|
import app.revanced.patcher.Fingerprint
|
||||||
import app.revanced.patcher.Match
|
import app.revanced.patcher.Match
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
|
|
||||||
@ -54,13 +54,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl
|
|||||||
val randomName = (0..100000).random()
|
val randomName = (0..100000).random()
|
||||||
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
||||||
|
|
||||||
getUserAgentFingerprint.method.addInstructions(
|
getUserAgentFingerprint.method.returnEarly(userAgent)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$userAgent"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.reddit.customclients.slide.api
|
package app.revanced.patches.reddit.customclients.slide.api
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") { clientIdOption ->
|
val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") { clientIdOption ->
|
||||||
compatibleWith("me.ccrama.redditslide")
|
compatibleWith("me.ccrama.redditslide")
|
||||||
@ -9,12 +9,6 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") {
|
|||||||
val clientId by clientIdOption
|
val clientId by clientIdOption
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
getClientIdFingerprint.method.addInstructions(
|
getClientIdFingerprint.method.returnEarly(clientId!!)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$clientId"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package app.revanced.patches.reddit.customclients.sync.syncforreddit.api
|
package app.revanced.patches.reddit.customclients.sync.syncforreddit.api
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch
|
import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
import java.util.*
|
import java.util.Base64
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(
|
val spoofClientPatch = spoofClientPatch(
|
||||||
redirectUri = "http://redditsync/auth",
|
redirectUri = "http://redditsync/auth",
|
||||||
@ -28,13 +28,8 @@ val spoofClientPatch = spoofClientPatch(
|
|||||||
|
|
||||||
getBearerTokenFingerprint.match(getAuthorizationStringFingerprint.originalClassDef).method.apply {
|
getBearerTokenFingerprint.match(getAuthorizationStringFingerprint.originalClassDef).method.apply {
|
||||||
val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8))
|
val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8))
|
||||||
addInstructions(
|
returnEarly("Basic $auth")
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "Basic $auth"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
val occurrenceIndex =
|
val occurrenceIndex =
|
||||||
getAuthorizationStringFingerprint.stringMatches!!.first().index
|
getAuthorizationStringFingerprint.stringMatches!!.first().index
|
||||||
|
|
||||||
@ -63,23 +58,19 @@ val spoofClientPatch = spoofClientPatch(
|
|||||||
val randomName = (0..100000).random()
|
val randomName = (0..100000).random()
|
||||||
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
||||||
|
|
||||||
getUserAgentFingerprint.method.replaceInstruction(
|
getUserAgentFingerprint.method.returnEarly(userAgent)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$userAgent"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Patch Imgur API URL.
|
// region Patch Imgur API URL.
|
||||||
|
|
||||||
val apiUrlIndex = imgurImageAPIFingerprint.stringMatches!!.first().index
|
imgurImageAPIFingerprint.let {
|
||||||
imgurImageAPIFingerprint.method.replaceInstruction(
|
val apiUrlIndex = it.stringMatches!!.first().index
|
||||||
apiUrlIndex,
|
it.method.replaceInstruction(
|
||||||
"const-string v1, \"https://api.imgur.com/3/image\"",
|
apiUrlIndex,
|
||||||
)
|
"const-string v1, \"https://api.imgur.com/3/image\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ fun checkEnvironmentPatch(
|
|||||||
|
|
||||||
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
|
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
||||||
)
|
)
|
||||||
|
|
||||||
setPatchInfo()
|
setPatchInfo()
|
||||||
|
@ -3,11 +3,11 @@ package app.revanced.patches.shared.misc.extension
|
|||||||
import app.revanced.patcher.Fingerprint
|
import app.revanced.patcher.Fingerprint
|
||||||
import app.revanced.patcher.FingerprintBuilder
|
import app.revanced.patcher.FingerprintBuilder
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.util.jar.JarFile
|
import java.util.jar.JarFile
|
||||||
@ -80,14 +80,7 @@ fun sharedExtensionPatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val manifestValue = getPatchesManifestEntry("Version")
|
val manifestValue = getPatchesManifestEntry("Version")
|
||||||
|
returnEarly(manifestValue)
|
||||||
addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$manifestValue"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patches.shared.misc.settings
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.patches.shared.misc.extension.EXTENSION_CLASS_DESCRIPTOR
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal val themeLightColorResourceNameFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||||
|
returns("Ljava/lang/String;")
|
||||||
|
parameters()
|
||||||
|
custom { method, classDef ->
|
||||||
|
method.name == "getThemeLightColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val themeDarkColorResourceNameFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||||
|
returns("Ljava/lang/String;")
|
||||||
|
parameters()
|
||||||
|
custom { method, classDef ->
|
||||||
|
method.name == "getThemeDarkColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.shared.misc.settings
|
package app.revanced.patches.shared.misc.settings
|
||||||
|
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patches.all.misc.resources.addResource
|
import app.revanced.patches.all.misc.resources.addResource
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
@ -13,6 +14,7 @@ import app.revanced.util.ResourceGroup
|
|||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
import app.revanced.util.getNode
|
import app.revanced.util.getNode
|
||||||
import app.revanced.util.insertFirst
|
import app.revanced.util.insertFirst
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
|
|
||||||
// TODO: Delete this on next major version bump.
|
// TODO: Delete this on next major version bump.
|
||||||
@ -22,6 +24,30 @@ fun settingsPatch (
|
|||||||
preferences: Set<BasePreference>,
|
preferences: Set<BasePreference>,
|
||||||
) = settingsPatch(listOf(rootPreference), preferences)
|
) = settingsPatch(listOf(rootPreference), preferences)
|
||||||
|
|
||||||
|
private var themeForegroundColor : String? = null
|
||||||
|
private var themeBackgroundColor : String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default theme colors used in various ReVanced specific settings menus.
|
||||||
|
* By default these colors are white and black, but instead can be set to the
|
||||||
|
* same color the target app uses for it's own settings.
|
||||||
|
*/
|
||||||
|
fun overrideThemeColors(foregroundColor: String, backgroundColor: String) {
|
||||||
|
themeForegroundColor = foregroundColor
|
||||||
|
themeBackgroundColor = backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
private val settingsColorPatch = bytecodePatch {
|
||||||
|
finalize {
|
||||||
|
if (themeForegroundColor != null) {
|
||||||
|
themeLightColorResourceNameFingerprint.method.returnEarly(themeForegroundColor!!)
|
||||||
|
}
|
||||||
|
if (themeBackgroundColor != null) {
|
||||||
|
themeDarkColorResourceNameFingerprint.method.returnEarly(themeBackgroundColor!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resource patch that adds settings to a settings fragment.
|
* A resource patch that adds settings to a settings fragment.
|
||||||
*
|
*
|
||||||
@ -33,12 +59,28 @@ fun settingsPatch (
|
|||||||
rootPreferences: List<Pair<BasePreference, String>>? = null,
|
rootPreferences: List<Pair<BasePreference, String>>? = null,
|
||||||
preferences: Set<BasePreference>,
|
preferences: Set<BasePreference>,
|
||||||
) = resourcePatch {
|
) = resourcePatch {
|
||||||
dependsOn(addResourcesPatch)
|
dependsOn(addResourcesPatch, settingsColorPatch)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
copyResources(
|
copyResources(
|
||||||
"settings",
|
"settings",
|
||||||
ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"),
|
ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"),
|
||||||
|
ResourceGroup("drawable",
|
||||||
|
// CustomListPreference resources.
|
||||||
|
"revanced_ic_dialog_alert.xml",
|
||||||
|
"revanced_settings_arrow_time.xml",
|
||||||
|
"revanced_settings_circle_background.xml",
|
||||||
|
"revanced_settings_cursor.xml",
|
||||||
|
"revanced_settings_custom_checkmark.xml",
|
||||||
|
"revanced_settings_search_icon.xml",
|
||||||
|
"revanced_settings_toolbar_arrow_left.xml",
|
||||||
|
),
|
||||||
|
ResourceGroup("layout",
|
||||||
|
"revanced_custom_list_item_checked.xml",
|
||||||
|
// Color picker.
|
||||||
|
"revanced_color_dot_widget.xml",
|
||||||
|
"revanced_color_picker.xml",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
addResources("shared", "misc.settings.settingsResourcePatch")
|
addResources("shared", "misc.settings.settingsResourcePatch")
|
||||||
|
@ -12,7 +12,7 @@ import org.w3c.dom.Document
|
|||||||
* @param summaryKey The preference summary key.
|
* @param summaryKey The preference summary key.
|
||||||
* @param icon The preference icon resource name.
|
* @param icon The preference icon resource name.
|
||||||
* @param layout Layout declaration.
|
* @param layout Layout declaration.
|
||||||
* @param tag The preference tag.
|
* @param tag The preference class type.
|
||||||
* @param entriesKey The entries array key.
|
* @param entriesKey The entries array key.
|
||||||
* @param entryValuesKey The entry values array key.
|
* @param entryValuesKey The entry values array key.
|
||||||
*/
|
*/
|
||||||
@ -20,10 +20,12 @@ import org.w3c.dom.Document
|
|||||||
class ListPreference(
|
class ListPreference(
|
||||||
key: String? = null,
|
key: String? = null,
|
||||||
titleKey: String = "${key}_title",
|
titleKey: String = "${key}_title",
|
||||||
summaryKey: String? = "${key}_summary",
|
/** Summary key is ignored and will be removed soon */
|
||||||
|
//@Deprecated
|
||||||
|
summaryKey: String? = null,
|
||||||
icon: String? = null,
|
icon: String? = null,
|
||||||
layout: String? = null,
|
layout: String? = null,
|
||||||
tag: String = "ListPreference",
|
tag: String = "app.revanced.extension.shared.settings.preference.CustomDialogListPreference",
|
||||||
val entriesKey: String? = "${key}_entries",
|
val entriesKey: String? = "${key}_entries",
|
||||||
val entryValuesKey: String? = "${key}_entry_values"
|
val entryValuesKey: String? = "${key}_entry_values"
|
||||||
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
|
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
|
||||||
|
@ -25,7 +25,7 @@ val embeddedAdsPatch = bytecodePatch(
|
|||||||
addResources("twitch", "ad.embedded.embeddedAdsPatch")
|
addResources("twitch", "ad.embedded.embeddedAdsPatch")
|
||||||
|
|
||||||
PreferenceScreen.ADS.SURESTREAM.addPreferences(
|
PreferenceScreen.ADS.SURESTREAM.addPreferences(
|
||||||
ListPreference("revanced_block_embedded_ads", summaryKey = null),
|
ListPreference("revanced_block_embedded_ads"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Inject OkHttp3 application interceptor
|
// Inject OkHttp3 application interceptor
|
||||||
|
@ -34,10 +34,7 @@ val showDeletedMessagesPatch = bytecodePatch(
|
|||||||
addResources("twitch", "chat.antidelete.showDeletedMessagesPatch")
|
addResources("twitch", "chat.antidelete.showDeletedMessagesPatch")
|
||||||
|
|
||||||
PreferenceScreen.CHAT.GENERAL.addPreferences(
|
PreferenceScreen.CHAT.GENERAL.addPreferences(
|
||||||
ListPreference(
|
ListPreference("revanced_show_deleted_messages")
|
||||||
key = "revanced_show_deleted_messages",
|
|
||||||
summaryKey = null,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Spoiler mode: Force set hasModAccess member to true in constructor
|
// Spoiler mode: Force set hasModAccess member to true in constructor
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.warnwetter.misc.firebasegetcert
|
package app.revanced.patches.warnwetter.misc.firebasegetcert
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
val firebaseGetCertPatch = bytecodePatch(
|
val firebaseGetCertPatch = bytecodePatch(
|
||||||
description = "Spoofs the X-Android-Cert header.",
|
description = "Spoofs the X-Android-Cert header.",
|
||||||
@ -10,13 +10,7 @@ val firebaseGetCertPatch = bytecodePatch(
|
|||||||
|
|
||||||
execute {
|
execute {
|
||||||
listOf(getRegistrationCertFingerprint, getMessagingCertFingerprint).forEach { match ->
|
listOf(getRegistrationCertFingerprint, getMessagingCertFingerprint).forEach { match ->
|
||||||
match.method.addInstructions(
|
match.method.returnEarly("0799DDF0414D3B3475E88743C91C0676793ED450")
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "0799DDF0414D3B3475E88743C91C0676793ED450"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,14 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.*
|
import app.revanced.patches.youtube.misc.playercontrols.addBottomControl
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.initializeBottomControl
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.injectVisibilityCheckCall
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.playerControlsResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.shared.mainActivityFingerprint
|
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
@ -83,12 +87,10 @@ val downloadsPatch = bytecodePatch(
|
|||||||
injectVisibilityCheckCall(BUTTON_DESCRIPTOR)
|
injectVisibilityCheckCall(BUTTON_DESCRIPTOR)
|
||||||
|
|
||||||
// Main activity is used to launch downloader intent.
|
// Main activity is used to launch downloader intent.
|
||||||
mainActivityFingerprint.method.apply {
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
addInstruction(
|
1,
|
||||||
implementation!!.instructions.lastIndex,
|
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V"
|
||||||
"invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V",
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
offlineVideoEndpointFingerprint.method.apply {
|
offlineVideoEndpointFingerprint.method.apply {
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
|
@ -14,7 +14,7 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
|||||||
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.shared.mainActivityFingerprint
|
import app.revanced.patches.youtube.shared.mainActivityConstructorFingerprint
|
||||||
import app.revanced.util.*
|
import app.revanced.util.*
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
@ -43,10 +43,7 @@ private val swipeControlsResourcePatch = resourcePatch {
|
|||||||
SwitchPreference("revanced_swipe_haptic_feedback"),
|
SwitchPreference("revanced_swipe_haptic_feedback"),
|
||||||
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
||||||
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
||||||
ListPreference(
|
ListPreference("revanced_swipe_overlay_style"),
|
||||||
"revanced_swipe_overlay_style",
|
|
||||||
summaryKey = null,
|
|
||||||
),
|
|
||||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||||
TextPreference("revanced_swipe_overlay_progress_brightness_color",
|
TextPreference("revanced_swipe_overlay_progress_brightness_color",
|
||||||
tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference",
|
tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference",
|
||||||
@ -103,7 +100,7 @@ val swipeControlsPatch = bytecodePatch(
|
|||||||
|
|
||||||
execute {
|
execute {
|
||||||
val wrapperClass = swipeControlsHostActivityFingerprint.classDef
|
val wrapperClass = swipeControlsHostActivityFingerprint.classDef
|
||||||
val targetClass = mainActivityFingerprint.classDef
|
val targetClass = mainActivityConstructorFingerprint.classDef
|
||||||
|
|
||||||
// Inject the wrapper class from the extension into the class hierarchy of MainActivity.
|
// Inject the wrapper class from the extension into the class hierarchy of MainActivity.
|
||||||
wrapperClass.setSuperClass(targetClass.superclass)
|
wrapperClass.setSuperClass(targetClass.superclass)
|
||||||
|
@ -47,10 +47,7 @@ val changeFormFactorPatch = bytecodePatch(
|
|||||||
addResources("youtube", "layout.formfactor.changeFormFactorPatch")
|
addResources("youtube", "layout.formfactor.changeFormFactorPatch")
|
||||||
|
|
||||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||||
ListPreference(
|
ListPreference("revanced_change_form_factor")
|
||||||
"revanced_change_form_factor",
|
|
||||||
summaryKey = null,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
||||||
|
@ -184,23 +184,18 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
|
|
||||||
preferences +=
|
preferences +=
|
||||||
if (is_20_03_or_greater) {
|
if (is_20_03_or_greater) {
|
||||||
ListPreference(
|
ListPreference("revanced_miniplayer_type")
|
||||||
"revanced_miniplayer_type",
|
|
||||||
summaryKey = null,
|
|
||||||
)
|
|
||||||
} else if (is_19_43_or_greater) {
|
} else if (is_19_43_or_greater) {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_miniplayer_type",
|
key = "revanced_miniplayer_type",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_miniplayer_type_legacy_19_43_entries",
|
entriesKey = "revanced_miniplayer_type_legacy_19_43_entries",
|
||||||
entryValuesKey = "revanced_miniplayer_type_legacy_19_43_entry_values",
|
entryValuesKey = "revanced_miniplayer_type_legacy_19_43_entry_values"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_miniplayer_type",
|
key = "revanced_miniplayer_type",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_miniplayer_type_legacy_19_16_entries",
|
entriesKey = "revanced_miniplayer_type_legacy_19_16_entries",
|
||||||
entryValuesKey = "revanced_miniplayer_type_legacy_19_16_entry_values",
|
entryValuesKey = "revanced_miniplayer_type_legacy_19_16_entry_values"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +49,7 @@ internal val exitFullscreenPatch = bytecodePatch(
|
|||||||
addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch")
|
addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch")
|
||||||
|
|
||||||
PreferenceScreen.PLAYER.addPreferences(
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
ListPreference(
|
ListPreference("revanced_exit_fullscreen")
|
||||||
"revanced_exit_fullscreen",
|
|
||||||
summaryKey = null,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
autoRepeatFingerprint.match(autoRepeatParentFingerprint.originalClassDef).method.apply {
|
autoRepeatFingerprint.match(autoRepeatParentFingerprint.originalClassDef).method.apply {
|
||||||
|
@ -70,8 +70,7 @@ val shortsAutoplayPatch = bytecodePatch(
|
|||||||
// Main activity is used to check if app is in pip mode.
|
// Main activity is used to check if app is in pip mode.
|
||||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
1,
|
1,
|
||||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->setMainActivity(Landroid/app/Activity;)V",
|
||||||
"setMainActivity(Landroid/app/Activity;)V",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val reelEnumClass = reelEnumConstructorFingerprint.originalClassDef.type
|
val reelEnumClass = reelEnumConstructorFingerprint.originalClassDef.type
|
||||||
|
@ -79,14 +79,10 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
|
|||||||
|
|
||||||
PreferenceScreen.SHORTS.addPreferences(
|
PreferenceScreen.SHORTS.addPreferences(
|
||||||
if (is_19_46_or_greater) {
|
if (is_19_46_or_greater) {
|
||||||
ListPreference(
|
ListPreference("revanced_shorts_player_type")
|
||||||
key = "revanced_shorts_player_type",
|
|
||||||
summaryKey = null,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_shorts_player_type",
|
key = "revanced_shorts_player_type",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_shorts_player_type_legacy_entries",
|
entriesKey = "revanced_shorts_player_type_legacy_entries",
|
||||||
entryValuesKey = "revanced_shorts_player_type_legacy_entry_values"
|
entryValuesKey = "revanced_shorts_player_type_legacy_entry_values"
|
||||||
)
|
)
|
||||||
|
@ -83,7 +83,7 @@ val spoofAppVersionPatch = bytecodePatch(
|
|||||||
if (is_19_43_or_greater) {
|
if (is_19_43_or_greater) {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_spoof_app_version_target",
|
key = "revanced_spoof_app_version_target",
|
||||||
summaryKey = null,
|
summaryKey = null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
|
@ -53,7 +53,6 @@ val changeStartPagePatch = bytecodePatch(
|
|||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_change_start_page",
|
key = "revanced_change_start_page",
|
||||||
summaryKey = null,
|
|
||||||
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
|
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
|
||||||
),
|
),
|
||||||
SwitchPreference("revanced_change_start_page_always")
|
SwitchPreference("revanced_change_start_page_always")
|
||||||
|
@ -24,26 +24,6 @@ internal val lithoThemeFingerprint = fingerprint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val themeHelperDarkColorFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
|
||||||
returns("Ljava/lang/String;")
|
|
||||||
parameters()
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "darkThemeResourceName" &&
|
|
||||||
method.definingClass == "Lapp/revanced/extension/youtube/ThemeHelper;"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val themeHelperLightColorFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
|
||||||
returns("Ljava/lang/String;")
|
|
||||||
parameters()
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "lightThemeResourceName" &&
|
|
||||||
method.definingClass == "Lapp/revanced/extension/youtube/ThemeHelper;"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal const val GRADIENT_LOADING_SCREEN_AB_CONSTANT = 45412406L
|
internal const val GRADIENT_LOADING_SCREEN_AB_CONSTANT = 45412406L
|
||||||
|
|
||||||
internal val useGradientLoadingScreenFingerprint = fingerprint {
|
internal val useGradientLoadingScreenFingerprint = fingerprint {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.layout.theme
|
package app.revanced.patches.youtube.layout.theme
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
@ -8,6 +7,7 @@ import app.revanced.patcher.patch.stringOption
|
|||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.overrideThemeColors
|
||||||
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
@ -22,16 +22,17 @@ import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater
|
|||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.util.childElementsSequence
|
||||||
import app.revanced.util.forEachChildElement
|
import app.revanced.util.forEachChildElement
|
||||||
import app.revanced.util.insertLiteralOverride
|
import app.revanced.util.insertLiteralOverride
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/ThemePatch;"
|
||||||
"Lapp/revanced/extension/youtube/patches/theme/ThemePatch;"
|
|
||||||
|
|
||||||
val themePatch = bytecodePatch(
|
val themePatch = bytecodePatch(
|
||||||
name = "Theme",
|
name = "Theme",
|
||||||
description = "Adds options for theming and applies a custom background theme (dark background theme defaults to amoled black).",
|
description = "Adds options for theming and applies a custom background theme " +
|
||||||
|
"(dark background theme defaults to amoled black).",
|
||||||
) {
|
) {
|
||||||
val amoledBlackColor = "@android:color/black"
|
val amoledBlackColor = "@android:color/black"
|
||||||
val whiteColor = "@android:color/white"
|
val whiteColor = "@android:color/white"
|
||||||
@ -109,27 +110,23 @@ val themePatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
overrideThemeColors(lightThemeBackgroundColor!!, darkThemeBackgroundColor!!)
|
||||||
|
|
||||||
// Edit theme colors via resources.
|
// Edit theme colors via resources.
|
||||||
document("res/values/colors.xml").use { document ->
|
document("res/values/colors.xml").use { document ->
|
||||||
|
|
||||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
val children = resourcesNode.childNodes
|
resourcesNode.childElementsSequence().forEach { node ->
|
||||||
for (i in 0 until children.length) {
|
when (node.getAttribute("name")) {
|
||||||
val node = children.item(i) as? Element ?: continue
|
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98",
|
||||||
|
"yt_black2", "yt_black3", "yt_black4", "yt_status_bar_background_dark",
|
||||||
|
"material_grey_850",
|
||||||
|
-> node.textContent = darkThemeBackgroundColor
|
||||||
|
|
||||||
node.textContent =
|
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
|
||||||
when (node.getAttribute("name")) {
|
"yt_white2", "yt_white3", "yt_white4",
|
||||||
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3",
|
-> node.textContent = lightThemeBackgroundColor
|
||||||
"yt_black4", "yt_status_bar_background_dark", "material_grey_850",
|
}
|
||||||
-> darkThemeBackgroundColor ?: continue
|
|
||||||
|
|
||||||
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
|
|
||||||
"yt_white2", "yt_white3", "yt_white4",
|
|
||||||
-> lightThemeBackgroundColor ?: continue
|
|
||||||
|
|
||||||
else -> continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,67 +148,56 @@ val themePatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val splashBackgroundColor = "revanced_splash_background_color"
|
|
||||||
|
|
||||||
// Add a dynamic background color to the colors.xml file.
|
// Add a dynamic background color to the colors.xml file.
|
||||||
lightThemeBackgroundColor?.let {
|
val splashBackgroundColorKey = "revanced_splash_background_color"
|
||||||
addColorResource("res/values/colors.xml", splashBackgroundColor, it)
|
addColorResource("res/values/colors.xml", splashBackgroundColorKey, lightThemeBackgroundColor!!)
|
||||||
}
|
addColorResource("res/values-night/colors.xml", splashBackgroundColorKey, darkThemeBackgroundColor!!)
|
||||||
|
|
||||||
darkThemeBackgroundColor?.let {
|
|
||||||
addColorResource("res/values-night/colors.xml", splashBackgroundColor, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edit splash screen files and change the background color,
|
// Edit splash screen files and change the background color,
|
||||||
// if the background colors are set.
|
arrayOf(
|
||||||
if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) {
|
"res/drawable/quantum_launchscreen_youtube.xml",
|
||||||
val splashScreenResourceFiles = listOf(
|
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
|
||||||
"res/drawable/quantum_launchscreen_youtube.xml",
|
).forEach editSplashScreen@{ resourceFileName ->
|
||||||
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
|
document(resourceFileName).use { document ->
|
||||||
)
|
document.getElementsByTagName("layer-list").item(0).forEachChildElement { node ->
|
||||||
|
if (node.hasAttribute("android:drawable")) {
|
||||||
splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile ->
|
node.setAttribute("android:drawable", "@color/$splashBackgroundColorKey")
|
||||||
document(resourceFile).use { document ->
|
return@editSplashScreen
|
||||||
document.getElementsByTagName("layer-list").item(0).forEachChildElement { node ->
|
|
||||||
if (node.hasAttribute("android:drawable")) {
|
|
||||||
node.setAttribute("android:drawable", "@color/$splashBackgroundColor")
|
|
||||||
return@editSplashScreen
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw PatchException("Failed to modify launch screen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix the splash screen dark mode background color.
|
|
||||||
// In 19.32+ the dark mode splash screen is white and fades to black.
|
|
||||||
// Maybe it's a bug in YT, or maybe it intentionally. Who knows.
|
|
||||||
document("res/values-night-v27/styles.xml").use { document ->
|
|
||||||
// Create a night mode specific override for the splash screen background.
|
|
||||||
val style = document.createElement("style")
|
|
||||||
style.setAttribute("name", "Theme.YouTube.Home")
|
|
||||||
style.setAttribute("parent", "@style/Base.V27.Theme.YouTube.Home")
|
|
||||||
|
|
||||||
// Fix status and navigation bar showing white on some Android devices,
|
|
||||||
// such as SDK 28 Android 10 medium tablet.
|
|
||||||
val colorSplashBackgroundColor = "@color/$splashBackgroundColor"
|
|
||||||
arrayOf(
|
|
||||||
"android:navigationBarColor" to colorSplashBackgroundColor,
|
|
||||||
"android:windowBackground" to colorSplashBackgroundColor,
|
|
||||||
"android:colorBackground" to colorSplashBackgroundColor,
|
|
||||||
"colorPrimaryDark" to colorSplashBackgroundColor,
|
|
||||||
"android:windowLightStatusBar" to "false",
|
|
||||||
).forEach { (name, value) ->
|
|
||||||
val styleItem = document.createElement("item")
|
|
||||||
styleItem.setAttribute("name", name)
|
|
||||||
styleItem.textContent = value
|
|
||||||
style.appendChild(styleItem)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
throw PatchException("Failed to modify launch screen")
|
||||||
resourcesNode.appendChild(style)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix the splash screen dark mode background color.
|
||||||
|
// In 19.32+ the dark mode splash screen is white and fades to black.
|
||||||
|
// Maybe it's a bug in YT, or maybe it intentionally. Who knows.
|
||||||
|
document("res/values-night-v27/styles.xml").use { document ->
|
||||||
|
// Create a night mode specific override for the splash screen background.
|
||||||
|
val style = document.createElement("style")
|
||||||
|
style.setAttribute("name", "Theme.YouTube.Home")
|
||||||
|
style.setAttribute("parent", "@style/Base.V27.Theme.YouTube.Home")
|
||||||
|
|
||||||
|
// Fix status and navigation bar showing white on some Android devices,
|
||||||
|
// such as SDK 28 Android 10 medium tablet.
|
||||||
|
val colorSplashBackgroundColor = "@color/$splashBackgroundColorKey"
|
||||||
|
arrayOf(
|
||||||
|
"android:navigationBarColor" to colorSplashBackgroundColor,
|
||||||
|
"android:windowBackground" to colorSplashBackgroundColor,
|
||||||
|
"android:colorBackground" to colorSplashBackgroundColor,
|
||||||
|
"colorPrimaryDark" to colorSplashBackgroundColor,
|
||||||
|
"android:windowLightStatusBar" to "false",
|
||||||
|
).forEach { (name, value) ->
|
||||||
|
val styleItem = document.createElement("item")
|
||||||
|
styleItem.setAttribute("name", name)
|
||||||
|
styleItem.textContent = value
|
||||||
|
style.appendChild(styleItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||||
|
resourcesNode.appendChild(style)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -237,10 +223,7 @@ val themePatch = bytecodePatch(
|
|||||||
|
|
||||||
if (is_19_47_or_greater) {
|
if (is_19_47_or_greater) {
|
||||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||||
ListPreference(
|
ListPreference("splash_screen_animation_style")
|
||||||
key = "splash_screen_animation_style",
|
|
||||||
summaryKey = null
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,21 +240,6 @@ val themePatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayOf(
|
|
||||||
themeHelperLightColorFingerprint to lightThemeBackgroundColor,
|
|
||||||
themeHelperDarkColorFingerprint to darkThemeBackgroundColor,
|
|
||||||
).forEach { (fingerprint, color) ->
|
|
||||||
fingerprint.method.apply {
|
|
||||||
addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const-string v0, "$color"
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getValue")
|
lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,34 +50,29 @@ val alternativeThumbnailsPatch = bytecodePatch(
|
|||||||
val values = "revanced_alt_thumbnail_options_entry_values"
|
val values = "revanced_alt_thumbnail_options_entry_values"
|
||||||
PreferenceScreen.ALTERNATIVE_THUMBNAILS.addPreferences(
|
PreferenceScreen.ALTERNATIVE_THUMBNAILS.addPreferences(
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_alt_thumbnail_home",
|
key = "revanced_alt_thumbnail_home",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = entries,
|
entriesKey = entries,
|
||||||
entryValuesKey = values,
|
entryValuesKey = values
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_alt_thumbnail_subscription",
|
key = "revanced_alt_thumbnail_subscription",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = entries,
|
entriesKey = entries,
|
||||||
entryValuesKey = values,
|
entryValuesKey = values
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_alt_thumbnail_library",
|
key = "revanced_alt_thumbnail_library",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = entries,
|
entriesKey = entries,
|
||||||
entryValuesKey = values,
|
entryValuesKey = values
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_alt_thumbnail_player",
|
key = "revanced_alt_thumbnail_player",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = entries,
|
entriesKey = entries,
|
||||||
entryValuesKey = values,
|
entryValuesKey = values
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
"revanced_alt_thumbnail_search",
|
key = "revanced_alt_thumbnail_search",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = entries,
|
entriesKey = entries,
|
||||||
entryValuesKey = values,
|
entryValuesKey = values
|
||||||
),
|
),
|
||||||
NonInteractivePreference(
|
NonInteractivePreference(
|
||||||
"revanced_alt_thumbnail_dearrow_about",
|
"revanced_alt_thumbnail_dearrow_about",
|
||||||
@ -89,7 +84,7 @@ val alternativeThumbnailsPatch = bytecodePatch(
|
|||||||
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
|
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
|
||||||
NonInteractivePreference("revanced_alt_thumbnail_stills_about"),
|
NonInteractivePreference("revanced_alt_thumbnail_stills_about"),
|
||||||
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
|
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
|
||||||
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null),
|
ListPreference("revanced_alt_thumbnail_stills_time"),
|
||||||
)
|
)
|
||||||
|
|
||||||
addImageUrlHook(EXTENSION_CLASS_DESCRIPTOR)
|
addImageUrlHook(EXTENSION_CLASS_DESCRIPTOR)
|
||||||
|
@ -4,15 +4,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
internal val onBackPressedFingerprint = fingerprint {
|
|
||||||
returns("V")
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
opcodes(Opcode.RETURN_VOID)
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val scrollPositionFingerprint = fingerprint {
|
internal val scrollPositionFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||||
returns("V")
|
returns("V")
|
||||||
|
@ -2,6 +2,7 @@ package app.revanced.patches.youtube.misc.fix.backtoexitgesture
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
@ -10,7 +11,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;"
|
||||||
|
|
||||||
internal val fixBackToExitGesturePatch = bytecodePatch(
|
internal val fixBackToExitGesturePatch = bytecodePatch(
|
||||||
description = "Fixes the swipe back to exit gesture.",
|
description = "Fixes the swipe back to exit gesture."
|
||||||
) {
|
) {
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -36,12 +37,12 @@ internal val fixBackToExitGesturePatch = bytecodePatch(
|
|||||||
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V"
|
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackPressedFingerprint.let {
|
mainActivityOnBackPressedFingerprint.method.apply {
|
||||||
it.method.addInstruction(
|
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)
|
||||||
it.patternMatch!!.endIndex,
|
addInstruction(
|
||||||
|
index,
|
||||||
"invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V"
|
"invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -42,15 +42,6 @@ internal val initializeButtonsFingerprint = fingerprint {
|
|||||||
literal { imageOnlyTabResourceId }
|
literal { imageOnlyTabResourceId }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val mainActivityOnBackPressedFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
returns("V")
|
|
||||||
parameters()
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension method, used for callback into to other patches.
|
* Extension method, used for callback into to other patches.
|
||||||
* Specifically, [navigationButtonsPatch].
|
* Specifically, [navigationButtonsPatch].
|
||||||
|
@ -15,6 +15,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater
|
||||||
|
import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
|
@ -3,7 +3,6 @@ package app.revanced.patches.youtube.misc.settings
|
|||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import app.revanced.util.literal
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal val licenseActivityOnCreateFingerprint = fingerprint {
|
internal val licenseActivityOnCreateFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
@ -18,13 +17,9 @@ internal val setThemeFingerprint = fingerprint {
|
|||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returns("L")
|
returns("L")
|
||||||
parameters()
|
parameters()
|
||||||
opcodes(Opcode.RETURN_OBJECT)
|
|
||||||
literal { appearanceStringId }
|
literal { appearanceStringId }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Added in YouTube v19.04.38.
|
|
||||||
*/
|
|
||||||
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
|
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
|
||||||
|
|
||||||
internal val cairoFragmentConfigFingerprint = fingerprint {
|
internal val cairoFragmentConfigFingerprint = fingerprint {
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.misc.settings
|
package app.revanced.patches.youtube.misc.settings
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
@ -13,15 +11,32 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|||||||
import app.revanced.patches.shared.misc.mapping.get
|
import app.revanced.patches.shared.misc.mapping.get
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.*
|
import app.revanced.patches.shared.misc.settings.overrideThemeColors
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.shared.misc.settings.settingsPatch
|
import app.revanced.patches.shared.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch
|
import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
|
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.util.*
|
import app.revanced.util.ResourceGroup
|
||||||
|
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||||
|
import app.revanced.util.copyResources
|
||||||
|
import app.revanced.util.copyXmlNode
|
||||||
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
|
import app.revanced.util.inputStreamFromBundledResource
|
||||||
|
import app.revanced.util.insertLiteralOverride
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
@ -30,7 +45,9 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
// Used by a fingerprint() from SettingsPatch.
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/settings/LicenseActivityHook;"
|
||||||
|
|
||||||
internal var appearanceStringId = -1L
|
internal var appearanceStringId = -1L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -69,13 +86,13 @@ private val settingsResourcePatch = resourcePatch {
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Used for a fingerprint from SettingsPatch.
|
|
||||||
appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
|
appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
|
||||||
|
|
||||||
|
// Use same colors as stock YouTube.
|
||||||
|
overrideThemeColors("@color/yt_white1", "@color/yt_black3")
|
||||||
|
|
||||||
arrayOf(
|
arrayOf(
|
||||||
ResourceGroup("drawable",
|
ResourceGroup("drawable",
|
||||||
"revanced_settings_circle_background.xml",
|
|
||||||
"revanced_settings_cursor.xml",
|
|
||||||
"revanced_settings_icon.xml",
|
"revanced_settings_icon.xml",
|
||||||
"revanced_settings_screen_00_about.xml",
|
"revanced_settings_screen_00_about.xml",
|
||||||
"revanced_settings_screen_01_ads.xml",
|
"revanced_settings_screen_01_ads.xml",
|
||||||
@ -92,11 +109,10 @@ private val settingsResourcePatch = resourcePatch {
|
|||||||
"revanced_settings_screen_12_video.xml",
|
"revanced_settings_screen_12_video.xml",
|
||||||
),
|
),
|
||||||
ResourceGroup("layout",
|
ResourceGroup("layout",
|
||||||
"revanced_color_dot_widget.xml",
|
|
||||||
"revanced_color_picker.xml",
|
|
||||||
"revanced_preference_with_icon_no_search_result.xml",
|
"revanced_preference_with_icon_no_search_result.xml",
|
||||||
"revanced_search_suggestion_item.xml",
|
"revanced_search_suggestion_item.xml",
|
||||||
"revanced_settings_with_toolbar.xml"),
|
"revanced_settings_with_toolbar.xml"
|
||||||
|
),
|
||||||
ResourceGroup("menu", "revanced_search_menu.xml")
|
ResourceGroup("menu", "revanced_search_menu.xml")
|
||||||
).forEach { resourceGroup ->
|
).forEach { resourceGroup ->
|
||||||
copyResources("settings", resourceGroup)
|
copyResources("settings", resourceGroup)
|
||||||
@ -170,12 +186,6 @@ val settingsPatch = bytecodePatch(
|
|||||||
checkEnvironmentPatch,
|
checkEnvironmentPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
val extensionPackage = "app/revanced/extension/youtube"
|
|
||||||
val activityHookClassDescriptor = "L$extensionPackage/settings/LicenseActivityHook;"
|
|
||||||
|
|
||||||
val themeHelperDescriptor = "L$extensionPackage/ThemeHelper;"
|
|
||||||
val setThemeMethodName = "setTheme"
|
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("youtube", "misc.settings.settingsPatch")
|
addResources("youtube", "misc.settings.settingsPatch")
|
||||||
|
|
||||||
@ -185,7 +195,7 @@ val settingsPatch = bytecodePatch(
|
|||||||
icon = "@drawable/revanced_settings_screen_00_about",
|
icon = "@drawable/revanced_settings_screen_00_about",
|
||||||
layout = "@layout/preference_with_icon",
|
layout = "@layout/preference_with_icon",
|
||||||
summaryKey = null,
|
summaryKey = null,
|
||||||
tag = "app.revanced.extension.youtube.settings.preference.ReVancedYouTubeAboutPreference",
|
tag = "app.revanced.extension.shared.settings.preference.ReVancedAboutPreference",
|
||||||
selectable = true,
|
selectable = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -210,31 +220,10 @@ val settingsPatch = bytecodePatch(
|
|||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_language",
|
key = "revanced_language",
|
||||||
summaryKey = null,
|
|
||||||
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
|
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
setThemeFingerprint.method.let { setThemeMethod ->
|
|
||||||
setThemeMethod.implementation!!.instructions.mapIndexedNotNull { i, instruction ->
|
|
||||||
if (instruction.opcode == Opcode.RETURN_OBJECT) i else null
|
|
||||||
}.asReversed().forEach { returnIndex ->
|
|
||||||
// The following strategy is to replace the return instruction with the setTheme instruction,
|
|
||||||
// then add a return instruction after the setTheme instruction.
|
|
||||||
// This is done because the return instruction is a target of another instruction.
|
|
||||||
|
|
||||||
setThemeMethod.apply {
|
|
||||||
// This register is returned by the setTheme method.
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(returnIndex).registerA
|
|
||||||
replaceInstruction(
|
|
||||||
returnIndex,
|
|
||||||
"invoke-static { v$register }, " +
|
|
||||||
"$themeHelperDescriptor->$setThemeMethodName(Ljava/lang/Enum;)V",
|
|
||||||
)
|
|
||||||
addInstruction(returnIndex + 1, "return-object v$register")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify the license activity and remove all existing layout code.
|
// Modify the license activity and remove all existing layout code.
|
||||||
// Must modify an existing activity and cannot add a new activity to the manifest,
|
// Must modify an existing activity and cannot add a new activity to the manifest,
|
||||||
@ -243,9 +232,9 @@ val settingsPatch = bytecodePatch(
|
|||||||
licenseActivityOnCreateFingerprint.method.addInstructions(
|
licenseActivityOnCreateFingerprint.method.addInstructions(
|
||||||
1,
|
1,
|
||||||
"""
|
"""
|
||||||
invoke-static { p0 }, $activityHookClassDescriptor->initialize(Landroid/app/Activity;)V
|
invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->initialize(Landroid/app/Activity;)V
|
||||||
return-void
|
return-void
|
||||||
""",
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remove other methods as they will break as the onCreate method is modified above.
|
// Remove other methods as they will break as the onCreate method is modified above.
|
||||||
@ -267,7 +256,7 @@ val settingsPatch = bytecodePatch(
|
|||||||
).toMutable().apply {
|
).toMutable().apply {
|
||||||
addInstructions(
|
addInstructions(
|
||||||
"""
|
"""
|
||||||
invoke-static { p1 }, $activityHookClassDescriptor->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context;
|
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context;
|
||||||
move-result-object p1
|
move-result-object p1
|
||||||
invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V
|
invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V
|
||||||
return-void
|
return-void
|
||||||
@ -278,10 +267,23 @@ val settingsPatch = bytecodePatch(
|
|||||||
methods.add(attachBaseContext)
|
methods.add(attachBaseContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update shared dark mode status based on YT theme.
|
||||||
|
// This is needed because YT allows forcing light/dark mode
|
||||||
|
// which then differs from the system dark mode status.
|
||||||
|
setThemeFingerprint.method.apply {
|
||||||
|
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
index,
|
||||||
|
"invoke-static { v$register }, ${EXTENSION_CLASS_DESCRIPTOR}->updateLightDarkModeStatus(Ljava/lang/Enum;)V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add setting to force cairo settings fragment on/off.
|
// Add setting to force cairo settings fragment on/off.
|
||||||
cairoFragmentConfigFingerprint.method.insertLiteralOverride(
|
cairoFragmentConfigFingerprint.method.insertLiteralOverride(
|
||||||
CAIRO_CONFIG_LITERAL_VALUE,
|
CAIRO_CONFIG_LITERAL_VALUE,
|
||||||
"$activityHookClassDescriptor->useCairoSettingsFragment(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->useCairoSettingsFragment(Z)Z"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,10 +48,7 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
|||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_spoof_video_streams"),
|
SwitchPreference("revanced_spoof_video_streams"),
|
||||||
ListPreference(
|
ListPreference("revanced_spoof_video_streams_client_type"),
|
||||||
"revanced_spoof_video_streams_client_type",
|
|
||||||
summaryKey = null,
|
|
||||||
),
|
|
||||||
NonInteractivePreference(
|
NonInteractivePreference(
|
||||||
// Requires a key and title but the actual text is chosen at runtime.
|
// Requires a key and title but the actual text is chosen at runtime.
|
||||||
key = "revanced_spoof_video_streams_about_android",
|
key = "revanced_spoof_video_streams_about_android",
|
||||||
@ -59,7 +56,6 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
|||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_spoof_video_streams_language",
|
key = "revanced_spoof_video_streams_language",
|
||||||
summaryKey = null,
|
|
||||||
// Language strings are declared in Setting patch.
|
// Language strings are declared in Setting patch.
|
||||||
entriesKey = "revanced_language_entries",
|
entriesKey = "revanced_language_entries",
|
||||||
entryValuesKey = "revanced_language_entry_values",
|
entryValuesKey = "revanced_language_entry_values",
|
||||||
|
@ -44,11 +44,20 @@ internal val layoutConstructorFingerprint = fingerprint {
|
|||||||
strings("1.0x")
|
strings("1.0x")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val mainActivityFingerprint = fingerprint {
|
internal val mainActivityConstructorFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||||
parameters()
|
parameters()
|
||||||
custom { _, classDef ->
|
custom { _, classDef ->
|
||||||
classDef.endsWith("MainActivity;")
|
classDef.endsWith("/MainActivity;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val mainActivityOnBackPressedFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters()
|
||||||
|
custom { method, classDef ->
|
||||||
|
method.name == "onBackPressed" && classDef.endsWith("/MainActivity;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +65,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
|
|||||||
returns("V")
|
returns("V")
|
||||||
parameters("Landroid/os/Bundle;")
|
parameters("Landroid/os/Bundle;")
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
method.name == "onCreate" && classDef.endsWith("MainActivity;")
|
method.name == "onCreate" && classDef.endsWith("/MainActivity;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,29 +37,25 @@ val rememberVideoQualityPatch = bytecodePatch {
|
|||||||
settingsMenuVideoQualityGroup.addAll(listOf(
|
settingsMenuVideoQualityGroup.addAll(listOf(
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_video_quality_default_mobile",
|
key = "revanced_video_quality_default_mobile",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_video_quality_default_entries",
|
entriesKey = "revanced_video_quality_default_entries",
|
||||||
entryValuesKey = "revanced_video_quality_default_entry_values",
|
entryValuesKey = "revanced_video_quality_default_entry_values"
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_video_quality_default_wifi",
|
key = "revanced_video_quality_default_wifi",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_video_quality_default_entries",
|
entriesKey = "revanced_video_quality_default_entries",
|
||||||
entryValuesKey = "revanced_video_quality_default_entry_values",
|
entryValuesKey = "revanced_video_quality_default_entry_values"
|
||||||
),
|
),
|
||||||
SwitchPreference("revanced_remember_video_quality_last_selected"),
|
SwitchPreference("revanced_remember_video_quality_last_selected"),
|
||||||
|
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_shorts_quality_default_mobile",
|
key = "revanced_shorts_quality_default_mobile",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_shorts_quality_default_entries",
|
entriesKey = "revanced_shorts_quality_default_entries",
|
||||||
entryValuesKey = "revanced_shorts_quality_default_entry_values",
|
entryValuesKey = "revanced_shorts_quality_default_entry_values",
|
||||||
),
|
),
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_shorts_quality_default_wifi",
|
key = "revanced_shorts_quality_default_wifi",
|
||||||
summaryKey = null,
|
|
||||||
entriesKey = "revanced_shorts_quality_default_entries",
|
entriesKey = "revanced_shorts_quality_default_entries",
|
||||||
entryValuesKey = "revanced_shorts_quality_default_entry_values",
|
entryValuesKey = "revanced_shorts_quality_default_entry_values"
|
||||||
),
|
),
|
||||||
SwitchPreference("revanced_remember_shorts_quality_last_selected")
|
SwitchPreference("revanced_remember_shorts_quality_last_selected")
|
||||||
))
|
))
|
||||||
|
@ -34,7 +34,6 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch {
|
|||||||
listOf(
|
listOf(
|
||||||
ListPreference(
|
ListPreference(
|
||||||
key = "revanced_playback_speed_default",
|
key = "revanced_playback_speed_default",
|
||||||
summaryKey = null,
|
|
||||||
// Entries and values are set by the extension code based on the actual speeds available.
|
// Entries and values are set by the extension code based on the actual speeds available.
|
||||||
entriesKey = null,
|
entriesKey = null,
|
||||||
entryValuesKey = null,
|
entryValuesKey = null,
|
||||||
|
@ -742,16 +742,22 @@ private const val RETURN_TYPE_MISMATCH = "Mismatch between override type and Met
|
|||||||
*
|
*
|
||||||
* For methods that return an object or any array type, calling this method with `false`
|
* For methods that return an object or any array type, calling this method with `false`
|
||||||
* will force the method to return a `null` value.
|
* will force the method to return a `null` value.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Boolean = false) {
|
fun MutableMethod.returnEarly(value: Boolean = false) {
|
||||||
val returnType = returnType.first()
|
val returnType = returnType.first()
|
||||||
check(returnType == 'Z' || (!value && (returnType in setOf('V', 'L', '[')))) { RETURN_TYPE_MISMATCH }
|
check(returnType == 'Z' || (!value && (returnType == 'V' || returnType == 'L' || returnType != '['))) {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
overrideReturnValue(value.toHexString(), false)
|
overrideReturnValue(value.toHexString(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Byte` return value.
|
* Overrides the first instruction of a method with a constant `Byte` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Byte) {
|
fun MutableMethod.returnEarly(value: Byte) {
|
||||||
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
||||||
@ -761,6 +767,8 @@ fun MutableMethod.returnEarly(value: Byte) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Short` return value.
|
* Overrides the first instruction of a method with a constant `Short` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Short) {
|
fun MutableMethod.returnEarly(value: Short) {
|
||||||
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
||||||
@ -770,6 +778,8 @@ fun MutableMethod.returnEarly(value: Short) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Char` return value.
|
* Overrides the first instruction of a method with a constant `Char` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Char) {
|
fun MutableMethod.returnEarly(value: Char) {
|
||||||
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
||||||
@ -779,6 +789,8 @@ fun MutableMethod.returnEarly(value: Char) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Int` return value.
|
* Overrides the first instruction of a method with a constant `Int` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Int) {
|
fun MutableMethod.returnEarly(value: Int) {
|
||||||
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
||||||
@ -788,6 +800,8 @@ fun MutableMethod.returnEarly(value: Int) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Long` return value.
|
* Overrides the first instruction of a method with a constant `Long` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Long) {
|
fun MutableMethod.returnEarly(value: Long) {
|
||||||
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
||||||
@ -797,6 +811,8 @@ fun MutableMethod.returnEarly(value: Long) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Float` return value.
|
* Overrides the first instruction of a method with a constant `Float` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Float) {
|
fun MutableMethod.returnEarly(value: Float) {
|
||||||
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
||||||
@ -806,12 +822,30 @@ fun MutableMethod.returnEarly(value: Float) {
|
|||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Double` return value.
|
* Overrides the first instruction of a method with a constant `Double` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Double) {
|
fun MutableMethod.returnEarly(value: Double) {
|
||||||
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
||||||
overrideReturnValue(value.toString(), false)
|
overrideReturnValue(value.toString(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the first instruction of a method with a constant String return value.
|
||||||
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* Target method must have return type
|
||||||
|
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnEarly(value: String) {
|
||||||
|
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
overrideReturnValue(value, false)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides all return statements with a constant `Boolean` value.
|
* Overrides all return statements with a constant `Boolean` value.
|
||||||
* All method code is executed the same as unpatched.
|
* All method code is executed the same as unpatched.
|
||||||
@ -826,7 +860,9 @@ fun MutableMethod.returnLate(value: Boolean) {
|
|||||||
if (returnType == 'V') {
|
if (returnType == 'V') {
|
||||||
error("Cannot return late for Method of void type")
|
error("Cannot return late for Method of void type")
|
||||||
}
|
}
|
||||||
check(returnType == 'Z' || (!value && returnType in setOf('L', '['))) { RETURN_TYPE_MISMATCH }
|
check(returnType == 'Z' || (!value && (returnType == 'L' || returnType == '['))) {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
|
||||||
overrideReturnValue(value.toHexString(), true)
|
overrideReturnValue(value.toHexString(), true)
|
||||||
}
|
}
|
||||||
@ -908,8 +944,29 @@ fun MutableMethod.returnLate(value: Double) {
|
|||||||
overrideReturnValue(value.toString(), true)
|
overrideReturnValue(value.toString(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant String value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* Target method must have return type
|
||||||
|
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: String) {
|
||||||
|
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
overrideReturnValue(value, true)
|
||||||
|
}
|
||||||
|
|
||||||
private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) {
|
private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) {
|
||||||
val instructions = when (returnType.first()) {
|
val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;" ) {
|
||||||
|
"""
|
||||||
|
const-string v0, "$value"
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
} else when (returnType.first()) {
|
||||||
// If return type is an object, always return null.
|
// If return type is an object, always return null.
|
||||||
'L', '[' -> {
|
'L', '[' -> {
|
||||||
"""
|
"""
|
||||||
|
@ -33,11 +33,12 @@ Second \"item\" text"</string>
|
|||||||
<patch id="misc.settings.settingsResourcePatch">
|
<patch id="misc.settings.settingsResourcePatch">
|
||||||
<string name="revanced_settings_submenu_title">Settings</string>
|
<string name="revanced_settings_submenu_title">Settings</string>
|
||||||
<string name="revanced_settings_title" translatable="false">ReVanced</string>
|
<string name="revanced_settings_title" translatable="false">ReVanced</string>
|
||||||
<string name="revanced_settings_confirm_user_dialog_title">Do you wish to proceed?</string>
|
<string name="revanced_settings_confirm_user_dialog_title">Are you sure you want to proceed?</string>
|
||||||
<string name="revanced_settings_reset">Reset</string>
|
<string name="revanced_settings_reset">Reset</string>
|
||||||
<string name="revanced_settings_reset_color">Reset color</string>
|
<string name="revanced_settings_reset_color">Reset color</string>
|
||||||
<string name="revanced_settings_color_invalid">Invalid color</string>
|
<string name="revanced_settings_color_invalid">Invalid color</string>
|
||||||
<string name="revanced_settings_restart_title">Refresh and restart</string>
|
<string name="revanced_settings_restart_title">Restart required</string>
|
||||||
|
<string name="revanced_settings_restart_dialog_message">Restart the app for this change to take affect.</string>
|
||||||
<string name="revanced_settings_restart">Restart</string>
|
<string name="revanced_settings_restart">Restart</string>
|
||||||
<string name="revanced_settings_import">Import</string>
|
<string name="revanced_settings_import">Import</string>
|
||||||
<string name="revanced_settings_import_copy">Copy</string>
|
<string name="revanced_settings_import_copy">Copy</string>
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M26.5977,6.29688 L43.8672,36.2031 C45.0195,38.2031,43.5781,40.7031,41.2695,40.7031 L6.73047,40.7031 C4.42188,40.7031,2.98047,38.2031,4.13281,36.2031 L21.4023,6.29688 C22.5547,4.29688,25.4453,4.29688,26.5977,6.29688 Z M24,30 C22.8945,30,22,30.8945,22,32 C22,33.1055,22.8945,34,24,34 C25.1055,34,26,33.1055,26,32 C26,30.8945,25.1055,30,24,30 Z M24,16 C22.9727,16,22.1289,16.7734,22.0117,17.7656 L22,18 L22,26 C22,27.1055,22.8945,28,24,28 C25.0273,28,25.8711,27.2266,25.9883,26.2344 L26,26 L26,18 C26,16.8945,25.1055,16,24,16 Z M24,16" />
|
||||||
|
</vector>
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M14.97,16.95L10,13.87V7h2v5.76l4.03,2.49L14.97,16.95zM22,12c0,5.51 -4.49,10 -10,10S2,17.51 2,12h1c0,4.96 4.04,9 9,9s9,-4.04 9,-9s-4.04,-9 -9,-9C8.81,3 5.92,4.64 4.28,7.38C4.17,7.56 4.06,7.75 3.97,7.94C3.96,7.96 3.95,7.98 3.94,8H8v1H1.96V3h1v4.74C3,7.65 3.03,7.57 3.07,7.49C3.18,7.27 3.3,7.07 3.42,6.86C5.22,3.86 8.51,2 12,2C17.51,2 22,6.49 22,12z"/>
|
||||||
|
</vector>
|
@ -2,6 +2,6 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle"
|
android:shape="rectangle"
|
||||||
android:id="@+id/revanced_settings_cursor">
|
android:id="@+id/revanced_settings_cursor">
|
||||||
<solid android:color="?ytTextPrimary" />
|
<solid android:color="?android:attr/textColorPrimary" />
|
||||||
<size android:width="1dp" />
|
<size android:width="1dp" />
|
||||||
</shape>
|
</shape>
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||||
|
</vector>
|
@ -11,14 +11,14 @@
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:fillType="evenOdd"
|
android:fillType="evenOdd"
|
||||||
android:pathData="M19.5872,3.99233 C19.6801,3.77185,19.8789,3.44672,19.7613,3.20362 C19.6963,3.06937,19.5861,3.00019,19.3558,3.00019 L19.1405,3.00019 C18.9457,3.00019,18.773,3.11962,18.6933,3.30335 C17.9185,5.13604,13.8142,14.8277,12.4461,18.066 C12.3664,18.2497,12.1937,18.3646,11.9989,18.3646 C11.8085,18.3646,11.6314,18.2498,11.5562,18.066 C10.1837,14.8278,6.08386,5.13603,5.30905,3.30335 C5.22936,3.11962,5.05669,3.00019,4.86188,3.00019 L4.64558,3.00019 C4.41535,3.00019,4.30543,3.06946,4.24052,3.20362 C4.1229,3.44674,4.32165,3.77185,4.41463,3.99233 C5.88012,7.46938,10.2054,17.7051,11.206,20.066 C11.3123,20.3232,11.5285,20.6,11.803,20.6 L12.2,20.6 C12.4701,20.6,12.6907,20.3232,12.797,20.066 C13.7932,17.7051,18.1217,7.4694,19.5872,3.9923 Z"
|
android:pathData="M19.5872,3.99233 C19.6801,3.77185,19.8789,3.44672,19.7613,3.20362 C19.6963,3.06937,19.5861,3.00019,19.3558,3.00019 L19.1405,3.00019 C18.9457,3.00019,18.773,3.11962,18.6933,3.30335 C17.9185,5.13604,13.8142,14.8277,12.4461,18.066 C12.3664,18.2497,12.1937,18.3646,11.9989,18.3646 C11.8085,18.3646,11.6314,18.2498,11.5562,18.066 C10.1837,14.8278,6.08386,5.13603,5.30905,3.30335 C5.22936,3.11962,5.05669,3.00019,4.86188,3.00019 L4.64558,3.00019 C4.41535,3.00019,4.30543,3.06946,4.24052,3.20362 C4.1229,3.44674,4.32165,3.77185,4.41463,3.99233 C5.88012,7.46938,10.2054,17.7051,11.206,20.066 C11.3123,20.3232,11.5285,20.6,11.803,20.6 L12.2,20.6 C12.4701,20.6,12.6907,20.3232,12.797,20.066 C13.7932,17.7051,18.1217,7.4694,19.5872,3.9923 Z"
|
||||||
android:strokeWidth="1"
|
android:strokeWidth="1"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeMiterLimit="2" />
|
android:strokeMiterLimit="2" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:fillType="evenOdd"
|
android:fillType="evenOdd"
|
||||||
android:pathData="M7.44206,3 C7.23397,3,7.04036,3.11405,6.9341,3.30237 C6.83227,3.49069,6.83227,3.72094,6.9341,3.90926 C7.99669,5.81085,10.4352,10.1975,11.4933,12.0991 C11.5996,12.2874,11.7911,12.4015,11.9992,12.4015 C12.2117,12.4015,12.4009,12.2875,12.5072,12.0991 C13.5654,10.1975,16.0062,5.81085,17.0643,3.90926 C17.1706,3.72094,17.1706,3.49069,17.0643,3.30237 C16.9625,3.11405,16.7665,3,16.5584,3 Z M8.91225,4.09059 L15.1052,4.09059 C15.2375,4.09059,15.361,4.16305,15.4258,4.28281 C15.4934,4.40257,15.4934,4.54965,15.4258,4.66941 C14.7529,5.8787,13.0031,8.89953,12.3302,10.1088 C12.2626,10.2286,12.1427,10.3032,12.0075,10.3032 C11.8752,10.3032,11.7545,10.2286,11.6869,10.1088 C11.014,8.89952,9.2653,5.8787,8.58956,4.66941 C8.52481,4.54965,8.52481,4.40257,8.58956,4.28281 C8.65713,4.16305,8.77992,4.09059,8.91225,4.09059 Z"
|
android:pathData="M7.44206,3 C7.23397,3,7.04036,3.11405,6.9341,3.30237 C6.83227,3.49069,6.83227,3.72094,6.9341,3.90926 C7.99669,5.81085,10.4352,10.1975,11.4933,12.0991 C11.5996,12.2874,11.7911,12.4015,11.9992,12.4015 C12.2117,12.4015,12.4009,12.2875,12.5072,12.0991 C13.5654,10.1975,16.0062,5.81085,17.0643,3.90926 C17.1706,3.72094,17.1706,3.49069,17.0643,3.30237 C16.9625,3.11405,16.7665,3,16.5584,3 Z M8.91225,4.09059 L15.1052,4.09059 C15.2375,4.09059,15.361,4.16305,15.4258,4.28281 C15.4934,4.40257,15.4934,4.54965,15.4258,4.66941 C14.7529,5.8787,13.0031,8.89953,12.3302,10.1088 C12.2626,10.2286,12.1427,10.3032,12.0075,10.3032 C11.8752,10.3032,11.7545,10.2286,11.6869,10.1088 C11.014,8.89952,9.2653,5.8787,8.58956,4.66941 C8.52481,4.54965,8.52481,4.40257,8.58956,4.28281 C8.65713,4.16305,8.77992,4.09059,8.91225,4.09059 Z"
|
||||||
android:strokeWidth="1"
|
android:strokeWidth="1"
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M13,17h-2v-6h2V17zM13,7h-2v2h2V7zM12,3c-4.96,0 -9,4.04 -9,9s4.04,9 9,9c4.96,0 9,-4.04 9,-9S16.96,3 12,3M12,2c5.52,0 10,4.48 10,10s-4.48,10 -10,10C6.48,22 2,17.52 2,12S6.48,2 12,2L12,2z" />
|
android:pathData="M13,17h-2v-6h2V17zM13,7h-2v2h2V7zM12,3c-4.96,0 -9,4.04 -9,9s4.04,9 9,9c4.96,0 9,-4.04 9,-9S16.96,3 12,3M12,2c5.52,0 10,4.48 10,10s-4.48,10 -10,10C6.48,22 2,17.52 2,12S6.48,2 12,2L12,2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M17.9219,12.5 L17.9219,11.5 L21.1523,11.5 L21.1523,12.5 Z M19.0078,18.9609 L16.4219,17.0234 L17.0469,16.2305 L19.6289,18.168 Z M16.9688,7.69141 L16.3477,6.89844 L18.9297,4.96094 L19.5547,5.75391 Z M5.5,17.9609 L5.5,14.1523 L4.46094,14.1523 C4.01563,14.1523,3.63281,13.9961,3.31641,13.6836 C3.00391,13.3672,2.84766,12.9844,2.84766,12.5391 L2.84766,11.4609 C2.84766,11.0156,3.00391,10.6328,3.31641,10.3164 C3.63281,10.0039,4.01563,9.84766,4.46094,9.84766 L8.19141,9.84766 L12.1523,7.5 L12.1523,16.5 L8.19141,14.1523 L6.5,14.1523 L6.5,17.9609 Z M11.1523,14.7188 L11.1523,9.28125 L8.47266,10.8477 L4.46094,10.8477 C4.30859,10.8477,4.16797,10.9102,4.03906,11.0391 C3.91016,11.168,3.84766,11.3086,3.84766,11.4609 L3.84766,12.5391 C3.84766,12.6914,3.91016,12.832,4.03906,12.9609 C4.16797,13.0898,4.30859,13.1523,4.46094,13.1523 L8.47266,13.1523 Z M13.9219,14.8867 L13.9219,9.11328 C14.2578,9.42188,14.5273,9.82813,14.7305,10.332 C14.9375,10.8398,15.0391,11.3945,15.0391,12 C15.0391,12.6055,14.9375,13.1602,14.7305,13.668 C14.5273,14.1719,14.2578,14.5781,13.9219,14.8867 Z M7.5,12 Z M7.5,12" />
|
android:pathData="M17.9219,12.5 L17.9219,11.5 L21.1523,11.5 L21.1523,12.5 Z M19.0078,18.9609 L16.4219,17.0234 L17.0469,16.2305 L19.6289,18.168 Z M16.9688,7.69141 L16.3477,6.89844 L18.9297,4.96094 L19.5547,5.75391 Z M5.5,17.9609 L5.5,14.1523 L4.46094,14.1523 C4.01563,14.1523,3.63281,13.9961,3.31641,13.6836 C3.00391,13.3672,2.84766,12.9844,2.84766,12.5391 L2.84766,11.4609 C2.84766,11.0156,3.00391,10.6328,3.31641,10.3164 C3.63281,10.0039,4.01563,9.84766,4.46094,9.84766 L8.19141,9.84766 L12.1523,7.5 L12.1523,16.5 L8.19141,14.1523 L6.5,14.1523 L6.5,17.9609 Z M11.1523,14.7188 L11.1523,9.28125 L8.47266,10.8477 L4.46094,10.8477 C4.30859,10.8477,4.16797,10.9102,4.03906,11.0391 C3.91016,11.168,3.84766,11.3086,3.84766,11.4609 L3.84766,12.5391 C3.84766,12.6914,3.91016,12.832,4.03906,12.9609 C4.16797,13.0898,4.30859,13.1523,4.46094,13.1523 L8.47266,13.1523 Z M13.9219,14.8867 L13.9219,9.11328 C14.2578,9.42188,14.5273,9.82813,14.7305,10.332 C14.9375,10.8398,15.0391,11.3945,15.0391,12 C15.0391,12.6055,14.9375,13.1602,14.7305,13.668 C14.5273,14.1719,14.2578,14.5781,13.9219,14.8867 Z M7.5,12 Z M7.5,12" />
|
||||||
</vector>
|
</vector>
|
@ -12,10 +12,10 @@ Copyright 2023 Ajay Ramachandran <dev@ajay.app>
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:strokeColor="?ytTextPrimary"
|
android:strokeColor="?android:attr/textColorPrimary"
|
||||||
android:strokeWidth="1"
|
android:strokeWidth="1"
|
||||||
android:pathData="M12.0814,1.99794 C9.37251,1.99794,6.97872,3.32778,5.20335,5.14149 C3.42798,6.9552,1.99794,9.39989,1.99794,12.1677 C1.99794,14.9355,3.10403,17.7107,4.87939,19.5244 C6.65476,21.3381,9.37195,21.9549,12.0814,21.9549 C14.7908,21.9549,17.0848,20.9067,18.8601,19.093 C20.6355,17.2793,22.002,14.9355,22.002,12.1677 C22.002,9.40046,20.8525,6.83638,19.0766,5.02267 C17.3013,3.20894,14.7903,1.99794,12.0814,1.99794 Z M11.9105,5.02102 C13.838,5.02102,15.5196,6.09439,16.7829,7.35711 C18.0462,8.61984,18.8878,10.3004,18.8878,12.2279 C18.8878,14.1554,18.2513,16.0427,16.988,17.3054 C15.7247,18.5681,13.8374,18.9333,11.9105,18.9333 C9.98355,18.9333,8.36976,18.2962,7.10645,17.0335 C5.84314,15.7708,5.11222,14.1554,5.11222,12.2278 C5.11222,10.3003,5.63697,8.47868,6.8997,7.21537 C8.16239,5.95218,9.98293,5.02102,11.9105,5.02102 Z" />
|
android:pathData="M12.0814,1.99794 C9.37251,1.99794,6.97872,3.32778,5.20335,5.14149 C3.42798,6.9552,1.99794,9.39989,1.99794,12.1677 C1.99794,14.9355,3.10403,17.7107,4.87939,19.5244 C6.65476,21.3381,9.37195,21.9549,12.0814,21.9549 C14.7908,21.9549,17.0848,20.9067,18.8601,19.093 C20.6355,17.2793,22.002,14.9355,22.002,12.1677 C22.002,9.40046,20.8525,6.83638,19.0766,5.02267 C17.3013,3.20894,14.7903,1.99794,12.0814,1.99794 Z M11.9105,5.02102 C13.838,5.02102,15.5196,6.09439,16.7829,7.35711 C18.0462,8.61984,18.8878,10.3004,18.8878,12.2279 C18.8878,14.1554,18.2513,16.0427,16.988,17.3054 C15.7247,18.5681,13.8374,18.9333,11.9105,18.9333 C9.98355,18.9333,8.36976,18.2962,7.10645,17.0335 C5.84314,15.7708,5.11222,14.1554,5.11222,12.2278 C5.11222,10.3003,5.63697,8.47868,6.8997,7.21537 C8.16239,5.95218,9.98293,5.02102,11.9105,5.02102 Z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M15.3108,11.899 C15.3108,13.6514,13.8411,15.1264,12.0887,15.1264 C10.3363,15.1264,8.97704,13.6515,8.97704,11.899 C8.97704,10.1466,10.3363,8.71961,12.0887,8.71961 C13.8411,8.71961,15.3108,10.1466,15.3108,11.899 Z" />
|
android:pathData="M15.3108,11.899 C15.3108,13.6514,13.8411,15.1264,12.0887,15.1264 C10.3363,15.1264,8.97704,13.6515,8.97704,11.899 C8.97704,10.1466,10.3363,8.71961,12.0887,8.71961 C13.8411,8.71961,15.3108,10.1466,15.3108,11.899 Z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M4.85,19.775Q4.15,19.775 3.688,19.312Q3.225,18.85 3.225,18.15V12.55H4.225V18.15Q4.225,18.375 4.425,18.575Q4.625,18.775 4.85,18.775H12.45V19.775ZM8.625,16Q7.925,16 7.463,15.537Q7,15.075 7,14.375V8.775H8V14.375Q8,14.625 8.188,14.812Q8.375,15 8.625,15H16.225V16ZM12.375,12.225Q11.7,12.225 11.238,11.762Q10.775,11.3 10.775,10.625V5.85Q10.775,5.15 11.238,4.687Q11.7,4.225 12.375,4.225H19.15Q19.85,4.225 20.312,4.687Q20.775,5.15 20.775,5.85V10.625Q20.775,11.3 20.312,11.762Q19.85,12.225 19.15,12.225ZM12.375,11.225H19.15Q19.375,11.225 19.575,11.037Q19.775,10.85 19.775,10.625V7.225H11.775V10.625Q11.775,10.85 11.963,11.037Q12.15,11.225 12.375,11.225Z"/>
|
android:pathData="M4.85,19.775Q4.15,19.775 3.688,19.312Q3.225,18.85 3.225,18.15V12.55H4.225V18.15Q4.225,18.375 4.425,18.575Q4.625,18.775 4.85,18.775H12.45V19.775ZM8.625,16Q7.925,16 7.463,15.537Q7,15.075 7,14.375V8.775H8V14.375Q8,14.625 8.188,14.812Q8.375,15 8.625,15H16.225V16ZM12.375,12.225Q11.7,12.225 11.238,11.762Q10.775,11.3 10.775,10.625V5.85Q10.775,5.15 11.238,4.687Q11.7,4.225 12.375,4.225H19.15Q19.85,4.225 20.312,4.687Q20.775,5.15 20.775,5.85V10.625Q20.775,11.3 20.312,11.762Q19.85,12.225 19.15,12.225ZM12.375,11.225H19.15Q19.375,11.225 19.575,11.037Q19.775,10.85 19.775,10.625V7.225H11.775V10.625Q11.775,10.85 11.963,11.037Q12.15,11.225 12.375,11.225Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5S9.5,13.38 9.5,12S10.62,9.5 12,9.5M12,8.5c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S13.93,8.5 12,8.5L12,8.5zM13.22,3l0.55,2.2l0.13,0.51l0.5,0.18c0.61,0.23 1.19,0.56 1.72,0.98l0.4,0.32l0.5,-0.14l2.17,-0.62l1.22,2.11l-1.63,1.59l-0.37,0.36l0.08,0.51c0.05,0.32 0.08,0.64 0.08,0.98s-0.03,0.66 -0.08,0.98l-0.08,0.51l0.37,0.36l1.63,1.59l-1.22,2.11l-2.17,-0.62l-0.5,-0.14l-0.4,0.32c-0.53,0.43 -1.11,0.76 -1.72,0.98l-0.5,0.18l-0.13,0.51L13.22,21h-2.44l-0.55,-2.2l-0.13,-0.51l-0.5,-0.18C9,17.88 8.42,17.55 7.88,17.12l-0.4,-0.32l-0.5,0.14l-2.17,0.62L3.6,15.44l1.63,-1.59l0.37,-0.36l-0.08,-0.51C5.47,12.66 5.44,12.33 5.44,12s0.03,-0.66 0.08,-0.98l0.08,-0.51l-0.37,-0.36L3.6,8.56l1.22,-2.11l2.17,0.62l0.5,0.14l0.4,-0.32C8.42,6.45 9,6.12 9.61,5.9l0.5,-0.18l0.13,-0.51L10.78,3H13.22M14,2h-4L9.26,4.96c-0.73,0.27 -1.4,0.66 -2,1.14L4.34,5.27l-2,3.46l2.19,2.13C4.47,11.23 4.44,11.61 4.44,12s0.03,0.77 0.09,1.14l-2.19,2.13l2,3.46l2.92,-0.83c0.6,0.48 1.27,0.87 2,1.14L10,22h4l0.74,-2.96c0.73,-0.27 1.4,-0.66 2,-1.14l2.92,0.83l2,-3.46l-2.19,-2.13c0.06,-0.37 0.09,-0.75 0.09,-1.14s-0.03,-0.77 -0.09,-1.14l2.19,-2.13l-2,-3.46L16.74,6.1c-0.6,-0.48 -1.27,-0.87 -2,-1.14L14,2L14,2z" />
|
android:pathData="M12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5S9.5,13.38 9.5,12S10.62,9.5 12,9.5M12,8.5c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S13.93,8.5 12,8.5L12,8.5zM13.22,3l0.55,2.2l0.13,0.51l0.5,0.18c0.61,0.23 1.19,0.56 1.72,0.98l0.4,0.32l0.5,-0.14l2.17,-0.62l1.22,2.11l-1.63,1.59l-0.37,0.36l0.08,0.51c0.05,0.32 0.08,0.64 0.08,0.98s-0.03,0.66 -0.08,0.98l-0.08,0.51l0.37,0.36l1.63,1.59l-1.22,2.11l-2.17,-0.62l-0.5,-0.14l-0.4,0.32c-0.53,0.43 -1.11,0.76 -1.72,0.98l-0.5,0.18l-0.13,0.51L13.22,21h-2.44l-0.55,-2.2l-0.13,-0.51l-0.5,-0.18C9,17.88 8.42,17.55 7.88,17.12l-0.4,-0.32l-0.5,0.14l-2.17,0.62L3.6,15.44l1.63,-1.59l0.37,-0.36l-0.08,-0.51C5.47,12.66 5.44,12.33 5.44,12s0.03,-0.66 0.08,-0.98l0.08,-0.51l-0.37,-0.36L3.6,8.56l1.22,-2.11l2.17,0.62l0.5,0.14l0.4,-0.32C8.42,6.45 9,6.12 9.61,5.9l0.5,-0.18l0.13,-0.51L10.78,3H13.22M14,2h-4L9.26,4.96c-0.73,0.27 -1.4,0.66 -2,1.14L4.34,5.27l-2,3.46l2.19,2.13C4.47,11.23 4.44,11.61 4.44,12s0.03,0.77 0.09,1.14l-2.19,2.13l2,3.46l2.92,-0.83c0.6,0.48 1.27,0.87 2,1.14L10,22h4l0.74,-2.96c0.73,-0.27 1.4,-0.66 2,-1.14l2.92,0.83l2,-3.46l-2.19,-2.13c0.06,-0.37 0.09,-0.75 0.09,-1.14s-0.03,-0.77 -0.09,-1.14l2.19,-2.13l-2,-3.46L16.74,6.1c-0.6,-0.48 -1.27,-0.87 -2,-1.14L14,2L14,2z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M11.5,20.5 L11.5,15.5 L12.5,15.5 L12.5,17.5 L20.5,17.5 L20.5,18.5 L12.5,18.5 L12.5,20.5 Z M3.5,18.5 L3.5,17.5 L8.5,17.5 L8.5,18.5 Z M7.5,14.5 L7.5,12.5 L3.5,12.5 L3.5,11.5 L7.5,11.5 L7.5,9.5 L8.5,9.5 L8.5,14.5 Z M11.5,12.5 L11.5,11.5 L20.5,11.5 L20.5,12.5 Z M15.5,8.5 L15.5,3.5 L16.5,3.5 L16.5,5.5 L20.5,5.5 L20.5,6.5 L16.5,6.5 L16.5,8.5 Z M3.5,6.5 L3.5,5.5 L12.5,5.5 L12.5,6.5 Z M3.5,6.5" />
|
android:pathData="M11.5,20.5 L11.5,15.5 L12.5,15.5 L12.5,17.5 L20.5,17.5 L20.5,18.5 L12.5,18.5 L12.5,20.5 Z M3.5,18.5 L3.5,17.5 L8.5,17.5 L8.5,18.5 Z M7.5,14.5 L7.5,12.5 L3.5,12.5 L3.5,11.5 L7.5,11.5 L7.5,9.5 L8.5,9.5 L8.5,14.5 Z M11.5,12.5 L11.5,11.5 L20.5,11.5 L20.5,12.5 Z M15.5,8.5 L15.5,3.5 L16.5,3.5 L16.5,5.5 L20.5,5.5 L20.5,6.5 L16.5,6.5 L16.5,8.5 Z M3.5,6.5 L3.5,5.5 L12.5,5.5 L12.5,6.5 Z M3.5,6.5" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -21,9 +21,9 @@ Copyright 2022 Google
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M8.54688,22.2031 C7.00781,22.2031,5.51172,21.375,4.73047,19.9219 C3.66797,17.9414,4.32031,15.4531,6.21875,14.25 C6.23047,14.2422,6.24219,14.2344,6.25391,14.2305 C6.40625,14.1523,6.55469,14.0703,6.70703,13.9844 C6.59375,13.9219,6.48438,13.8594,6.37109,13.793 C5.20703,13.1211,4.49219,12.1758,4.29688,11.0508 C4.04297,9.55078,4.04297,7.65625,6.32031,6.28516 C8.07031,5.23828,9.86719,4.21484,11.6016,3.22266 C12.1797,2.89453,12.7539,2.56641,13.332,2.23438 C14.5313,1.54688,16.0195,1.51953,17.3008,2.16016 C18.5977,2.80469,19.4805,4.01563,19.6641,5.40625 C19.9297,7.17578,19.0469,8.94531,17.4609,9.80469 C17.3594,9.86328,17.2578,9.91797,17.1602,9.97656 C17.2539,10.0313,17.3477,10.0859,17.4414,10.1406 C18.7422,10.8711,19.5664,12.1172,19.6953,13.5547 C19.8242,14.9766,19.25,16.332,18.1211,17.2734 C17.7617,17.5664,17.3633,17.793,16.9805,18.0078 C16.8672,18.0703,16.7578,18.1328,16.6484,18.1953 C14.8711,19.2344,12.7617,20.457,10.5859,21.6875 C9.9375,22.0352,9.23828,22.2031,8.54688,22.2031 Z M6.69141,15.0313 C5.20703,15.9805,4.69922,17.9375,5.53125,19.4922 C6.42578,21.1484,8.49609,21.7773,10.1445,20.8906 C12.3086,19.668,14.4141,18.4453,16.1875,17.4102 C16.3008,17.3438,16.418,17.2773,16.5313,17.2148 C16.8984,17.0078,17.2461,16.8125,17.543,16.5703 C18.4336,15.8281,18.8906,14.7578,18.7891,13.6367 C18.6836,12.5039,18.0313,11.5156,16.9922,10.9336 C16.8281,10.8359,16.6641,10.7383,16.4961,10.6367 C16.3516,10.5508,16.2031,10.4609,16.043,10.3711 C15.9063,10.2891,15.8203,10.1445,15.8164,9.98438 C15.8125,9.82422,15.8984,9.67188,16.0352,9.58984 C16.3516,9.39063,16.6641,9.20703,17.0195,9.00781 C18.2773,8.32813,18.9727,6.92969,18.7617,5.53125 C18.6172,4.4375,17.9219,3.48438,16.8984,2.97656 C15.8828,2.47266,14.7188,2.49219,13.7813,3.02344 C13.207,3.35547,12.6289,3.68359,12.0547,4.01172 C10.3242,5.00391,8.53125,6.02344,6.78906,7.06641 C5.34375,7.9375,4.88281,9.04688,5.19531,10.8984 C5.34375,11.7539,5.89063,12.4648,6.82422,13 C7.15234,13.1875,7.47656,13.375,7.83984,13.5898 C7.97656,13.6719,8.0625,13.8203,8.0625,13.9805 C8.0625,14.1406,7.98047,14.2891,7.83984,14.375 C7.44531,14.6133,7.08203,14.8281,6.69141,15.0313 Z M6.69141,15.0313" />
|
android:pathData="M8.54688,22.2031 C7.00781,22.2031,5.51172,21.375,4.73047,19.9219 C3.66797,17.9414,4.32031,15.4531,6.21875,14.25 C6.23047,14.2422,6.24219,14.2344,6.25391,14.2305 C6.40625,14.1523,6.55469,14.0703,6.70703,13.9844 C6.59375,13.9219,6.48438,13.8594,6.37109,13.793 C5.20703,13.1211,4.49219,12.1758,4.29688,11.0508 C4.04297,9.55078,4.04297,7.65625,6.32031,6.28516 C8.07031,5.23828,9.86719,4.21484,11.6016,3.22266 C12.1797,2.89453,12.7539,2.56641,13.332,2.23438 C14.5313,1.54688,16.0195,1.51953,17.3008,2.16016 C18.5977,2.80469,19.4805,4.01563,19.6641,5.40625 C19.9297,7.17578,19.0469,8.94531,17.4609,9.80469 C17.3594,9.86328,17.2578,9.91797,17.1602,9.97656 C17.2539,10.0313,17.3477,10.0859,17.4414,10.1406 C18.7422,10.8711,19.5664,12.1172,19.6953,13.5547 C19.8242,14.9766,19.25,16.332,18.1211,17.2734 C17.7617,17.5664,17.3633,17.793,16.9805,18.0078 C16.8672,18.0703,16.7578,18.1328,16.6484,18.1953 C14.8711,19.2344,12.7617,20.457,10.5859,21.6875 C9.9375,22.0352,9.23828,22.2031,8.54688,22.2031 Z M6.69141,15.0313 C5.20703,15.9805,4.69922,17.9375,5.53125,19.4922 C6.42578,21.1484,8.49609,21.7773,10.1445,20.8906 C12.3086,19.668,14.4141,18.4453,16.1875,17.4102 C16.3008,17.3438,16.418,17.2773,16.5313,17.2148 C16.8984,17.0078,17.2461,16.8125,17.543,16.5703 C18.4336,15.8281,18.8906,14.7578,18.7891,13.6367 C18.6836,12.5039,18.0313,11.5156,16.9922,10.9336 C16.8281,10.8359,16.6641,10.7383,16.4961,10.6367 C16.3516,10.5508,16.2031,10.4609,16.043,10.3711 C15.9063,10.2891,15.8203,10.1445,15.8164,9.98438 C15.8125,9.82422,15.8984,9.67188,16.0352,9.58984 C16.3516,9.39063,16.6641,9.20703,17.0195,9.00781 C18.2773,8.32813,18.9727,6.92969,18.7617,5.53125 C18.6172,4.4375,17.9219,3.48438,16.8984,2.97656 C15.8828,2.47266,14.7188,2.49219,13.7813,3.02344 C13.207,3.35547,12.6289,3.68359,12.0547,4.01172 C10.3242,5.00391,8.53125,6.02344,6.78906,7.06641 C5.34375,7.9375,4.88281,9.04688,5.19531,10.8984 C5.34375,11.7539,5.89063,12.4648,6.82422,13 C7.15234,13.1875,7.47656,13.375,7.83984,13.5898 C7.97656,13.6719,8.0625,13.8203,8.0625,13.9805 C8.0625,14.1406,7.98047,14.2891,7.83984,14.375 C7.44531,14.6133,7.08203,14.8281,6.69141,15.0313 Z M6.69141,15.0313" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M10.0703,15.3555 C9.99219,15.3555,9.91406,15.3359,9.84375,15.2969 C9.69922,15.2148,9.61328,15.0625,9.61328,14.9023 L9.61328,9.08594 C9.61328,8.92188,9.69922,8.76953,9.83984,8.69141 C9.98438,8.60938,10.1563,8.60938,10.2969,8.69141 L15.3281,11.5898 C15.4688,11.6719,15.5547,11.8242,15.5547,11.9844 C15.5586,12.1484,15.4688,12.3008,15.3281,12.3789 L10.2969,15.2969 C10.2266,15.3359,10.1484,15.3555,10.0703,15.3555 Z M10.5234,9.875 L10.5234,14.1094 L14.1914,11.9883 Z M10.5234,9.875" />
|
android:pathData="M10.0703,15.3555 C9.99219,15.3555,9.91406,15.3359,9.84375,15.2969 C9.69922,15.2148,9.61328,15.0625,9.61328,14.9023 L9.61328,9.08594 C9.61328,8.92188,9.69922,8.76953,9.83984,8.69141 C9.98438,8.60938,10.1563,8.60938,10.2969,8.69141 L15.3281,11.5898 C15.4688,11.6719,15.5547,11.8242,15.5547,11.9844 C15.5586,12.1484,15.4688,12.3008,15.3281,12.3789 L10.2969,15.2969 C10.2266,15.3359,10.1484,15.3555,10.0703,15.3555 Z M10.5234,9.875 L10.5234,14.1094 L14.1914,11.9883 Z M10.5234,9.875" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M4.5,14Q3.65,14 3.075,13.425Q2.5,12.85 2.5,12Q2.5,11.15 3.075,10.575Q3.65,10 4.5,10Q5.2,10 5.738,10.425Q6.275,10.85 6.425,11.5H21.5V12.5H6.425Q6.275,13.15 5.738,13.575Q5.2,14 4.5,14Z"/>
|
android:pathData="M4.5,14Q3.65,14 3.075,13.425Q2.5,12.85 2.5,12Q2.5,11.15 3.075,10.575Q3.65,10 4.5,10Q5.2,10 5.738,10.425Q6.275,10.85 6.425,11.5H21.5V12.5H6.425Q6.275,13.15 5.738,13.575Q5.2,14 4.5,14Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M3,21 L3,20.1328 L5.3125,20.1328 C4.33203,18.9844,3.54297,17.7266,2.95703,16.3516 C2.37109,14.9805,2.07813,13.5313,2.07813,12.0078 C2.07813,10.4883,2.37109,9.04297,2.95703,7.67578 C3.54297,6.30859,4.33203,5.04688,5.3125,3.88281 L3,3.88281 L3,3 L7.09766,3 L7.09766,7.11719 L6.23047,7.11719 L6.23047,4.17578 C5.25,5.28906,4.45703,6.50391,3.85156,7.82422 C3.24609,9.14063,2.94141,10.5352,2.94141,12.0078 C2.94141,13.4844,3.24609,14.875,3.85156,16.1875 C4.45703,17.4961,5.25,18.7031,6.23047,19.8047 L6.23047,16.9023 L7.09766,16.9023 L7.09766,21 Z M16.2969,19.8047 C15.9883,19.9141,15.668,19.9648,15.3359,19.9531 C15,19.9453,14.6836,19.8711,14.3789,19.7266 L8.0625,16.7891 L8.35938,16.1953 C8.46094,16.0156,8.59375,15.8711,8.75781,15.7656 C8.92188,15.6563,9.10547,15.5938,9.30859,15.5781 L12.1602,15.2578 L9.30469,7.44922 C9.25391,7.3125,9.26172,7.18359,9.32031,7.0625 C9.38281,6.94531,9.48047,6.85938,9.61719,6.80859 C9.75391,6.76172,9.88281,6.76563,10.0039,6.82813 C10.1211,6.88672,10.207,6.98438,10.2539,7.12109 L13.5508,16.1797 L9.80078,16.5078 L14.8086,18.8242 C14.9766,18.8984,15.1602,18.9453,15.3672,18.957 C15.5703,18.9727,15.7617,18.9453,15.9414,18.8711 L19.3867,17.6211 C20.043,17.3867,20.5195,16.957,20.8086,16.332 C21.1016,15.7109,21.1289,15.0703,20.8945,14.4102 L19.5195,10.6602 C19.4688,10.5234,19.4727,10.3945,19.5234,10.2734 C19.5781,10.1523,19.6719,10.0703,19.8125,10.0195 C19.9492,9.97266,20.0781,9.97266,20.1992,10.0273 C20.3203,10.0781,20.4023,10.1758,20.4531,10.3125 L21.8281,14.0625 C22.1719,14.9844,22.1406,15.8789,21.7383,16.75 C21.332,17.6172,20.668,18.2188,19.7422,18.5547 Z M14.5273,13.5469 L13.1563,9.76953 C13.1094,9.63281,13.1133,9.50391,13.1758,9.38281 C13.2344,9.26563,13.332,9.17969,13.4727,9.13281 C13.6094,9.08203,13.7383,9.08594,13.8555,9.14844 C13.9766,9.20703,14.0586,9.30469,14.1094,9.44141 L15.4844,13.1914 Z M17.1992,12.5586 L16.1719,9.73438 C16.125,9.59766,16.1289,9.46875,16.1914,9.35547 C16.25,9.23828,16.3477,9.16016,16.4844,9.11328 C16.625,9.0625,16.7539,9.06641,16.8711,9.12109 C16.9922,9.17188,17.0781,9.26563,17.125,9.40625 L18.1484,12.2266 Z M17.0898,15.2578 Z M17.0898,15.2578" />
|
android:pathData="M3,21 L3,20.1328 L5.3125,20.1328 C4.33203,18.9844,3.54297,17.7266,2.95703,16.3516 C2.37109,14.9805,2.07813,13.5313,2.07813,12.0078 C2.07813,10.4883,2.37109,9.04297,2.95703,7.67578 C3.54297,6.30859,4.33203,5.04688,5.3125,3.88281 L3,3.88281 L3,3 L7.09766,3 L7.09766,7.11719 L6.23047,7.11719 L6.23047,4.17578 C5.25,5.28906,4.45703,6.50391,3.85156,7.82422 C3.24609,9.14063,2.94141,10.5352,2.94141,12.0078 C2.94141,13.4844,3.24609,14.875,3.85156,16.1875 C4.45703,17.4961,5.25,18.7031,6.23047,19.8047 L6.23047,16.9023 L7.09766,16.9023 L7.09766,21 Z M16.2969,19.8047 C15.9883,19.9141,15.668,19.9648,15.3359,19.9531 C15,19.9453,14.6836,19.8711,14.3789,19.7266 L8.0625,16.7891 L8.35938,16.1953 C8.46094,16.0156,8.59375,15.8711,8.75781,15.7656 C8.92188,15.6563,9.10547,15.5938,9.30859,15.5781 L12.1602,15.2578 L9.30469,7.44922 C9.25391,7.3125,9.26172,7.18359,9.32031,7.0625 C9.38281,6.94531,9.48047,6.85938,9.61719,6.80859 C9.75391,6.76172,9.88281,6.76563,10.0039,6.82813 C10.1211,6.88672,10.207,6.98438,10.2539,7.12109 L13.5508,16.1797 L9.80078,16.5078 L14.8086,18.8242 C14.9766,18.8984,15.1602,18.9453,15.3672,18.957 C15.5703,18.9727,15.7617,18.9453,15.9414,18.8711 L19.3867,17.6211 C20.043,17.3867,20.5195,16.957,20.8086,16.332 C21.1016,15.7109,21.1289,15.0703,20.8945,14.4102 L19.5195,10.6602 C19.4688,10.5234,19.4727,10.3945,19.5234,10.2734 C19.5781,10.1523,19.6719,10.0703,19.8125,10.0195 C19.9492,9.97266,20.0781,9.97266,20.1992,10.0273 C20.3203,10.0781,20.4023,10.1758,20.4531,10.3125 L21.8281,14.0625 C22.1719,14.9844,22.1406,15.8789,21.7383,16.75 C21.332,17.6172,20.668,18.2188,19.7422,18.5547 Z M14.5273,13.5469 L13.1563,9.76953 C13.1094,9.63281,13.1133,9.50391,13.1758,9.38281 C13.2344,9.26563,13.332,9.17969,13.4727,9.13281 C13.6094,9.08203,13.7383,9.08594,13.8555,9.14844 C13.9766,9.20703,14.0586,9.30469,14.1094,9.44141 L15.4844,13.1914 Z M17.1992,12.5586 L16.1719,9.73438 C16.125,9.59766,16.1289,9.46875,16.1914,9.35547 C16.25,9.23828,16.3477,9.16016,16.4844,9.11328 C16.625,9.0625,16.7539,9.06641,16.8711,9.12109 C16.9922,9.17188,17.0781,9.26563,17.125,9.40625 L18.1484,12.2266 Z M17.0898,15.2578 Z M17.0898,15.2578" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M3.625,15Q3,15 2.5,14.5Q2,14 2,13.375V12.15Q2,12.025 2.025,11.862Q2.05,11.7 2.1,11.55L4.85,5.075Q5.05,4.625 5.538,4.312Q6.025,4 6.55,4H16.575V15L10.3,21.2L9.875,20.75Q9.725,20.625 9.638,20.4Q9.55,20.175 9.55,20V19.85L10.575,15ZM15.575,5H6.55Q6.325,5 6.1,5.112Q5.875,5.225 5.775,5.5L3,12V13.375Q3,13.65 3.175,13.825Q3.35,14 3.625,14H11.8L10.65,19.45L15.575,14.575ZM15.575,14.575V14Q15.575,14 15.575,13.825Q15.575,13.65 15.575,13.375V12V5.5Q15.575,5.225 15.575,5.112Q15.575,5 15.575,5ZM16.575,15V14H20V5H16.575V4H21V15Z"/>
|
android:pathData="M3.625,15Q3,15 2.5,14.5Q2,14 2,13.375V12.15Q2,12.025 2.025,11.862Q2.05,11.7 2.1,11.55L4.85,5.075Q5.05,4.625 5.538,4.312Q6.025,4 6.55,4H16.575V15L10.3,21.2L9.875,20.75Q9.725,20.625 9.638,20.4Q9.55,20.175 9.55,20V19.85L10.575,15ZM15.575,5H6.55Q6.325,5 6.1,5.112Q5.875,5.225 5.775,5.5L3,12V13.375Q3,13.65 3.175,13.825Q3.35,14 3.625,14H11.8L10.65,19.45L15.575,14.575ZM15.575,14.575V14Q15.575,14 15.575,13.825Q15.575,13.65 15.575,13.375V12V5.5Q15.575,5.225 15.575,5.112Q15.575,5 15.575,5ZM16.575,15V14H20V5H16.575V4H21V15Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -11,6 +11,6 @@ Copyright 2021 Ajay Ramachandran <dev@ajay.app>
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M 12.000145,2.0000008 C 8.8230689,1.9990926 5.6959192,2.7864027 2.9017488,4.2906678 2.3373945,4.5948398 1.9899198,5.1860103 2.000223,5.8244635 2.0930396,12.358829 5.4926743,18.31271 11.094442,21.749998 c 0.557183,0.333336 1.253849,0.333336 1.811031,0 5.601767,-3.438045 9.001096,-9.391169 9.094295,-15.9255345 0.01052,-0.6386247 -0.337035,-1.2300179 -0.9016,-1.5341683 -2.794107,-1.5040456 -5.92111,-2.2912233 -9.098023,-2.2902944 z m 0.08082,0.8705548 c 3.003625,0.013255 5.957553,0.7636027 8.599879,2.1845129 0.277414,0.151228 0.448533,0.4421907 0.44513,0.7568723 C 21.034684,12.23921 17.58825,17.8544 12.446767,21.009378 c -0.274165,0.167124 -0.619386,0.167124 -0.893551,0 C 6.4117365,17.854399 2.9652339,12.239209 2.8739372,5.8119397 2.8705209,5.4972741 3.0416092,5.2063196 3.3189962,5.0550685 6.0095892,3.608201 9.0224769,2.8570356 12.080969,2.8705556 Z M 9.6351953,6.7701615 v 8.3406435 l 7.2606727,-4.170358 z"/>
|
android:pathData="M 12.000145,2.0000008 C 8.8230689,1.9990926 5.6959192,2.7864027 2.9017488,4.2906678 2.3373945,4.5948398 1.9899198,5.1860103 2.000223,5.8244635 2.0930396,12.358829 5.4926743,18.31271 11.094442,21.749998 c 0.557183,0.333336 1.253849,0.333336 1.811031,0 5.601767,-3.438045 9.001096,-9.391169 9.094295,-15.9255345 0.01052,-0.6386247 -0.337035,-1.2300179 -0.9016,-1.5341683 -2.794107,-1.5040456 -5.92111,-2.2912233 -9.098023,-2.2902944 z m 0.08082,0.8705548 c 3.003625,0.013255 5.957553,0.7636027 8.599879,2.1845129 0.277414,0.151228 0.448533,0.4421907 0.44513,0.7568723 C 21.034684,12.23921 17.58825,17.8544 12.446767,21.009378 c -0.274165,0.167124 -0.619386,0.167124 -0.893551,0 C 6.4117365,17.854399 2.9652339,12.239209 2.8739372,5.8119397 2.8705209,5.4972741 3.0416092,5.2063196 3.3189962,5.0550685 6.0095892,3.608201 9.0224769,2.8570356 12.080969,2.8705556 Z M 9.6351953,6.7701615 v 8.3406435 l 7.2606727,-4.170358 z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M16.3,11.95 L12.075,7.725 16.3,3.5 20.525,7.725ZM4.625,10.625V4.625H10.625V10.625ZM13.375,19.375V13.375H19.375V19.375ZM4.625,19.375V13.375H10.625V19.375ZM5.625,9.625H9.625V5.625H5.625ZM16.325,10.575 L19.15,7.75 16.325,4.925 13.5,7.75ZM14.375,18.375H18.375V14.375H14.375ZM5.625,18.375H9.625V14.375H5.625ZM9.625,9.625ZM13.5,7.75ZM9.625,14.375ZM14.375,14.375Z"/>
|
android:pathData="M16.3,11.95 L12.075,7.725 16.3,3.5 20.525,7.725ZM4.625,10.625V4.625H10.625V10.625ZM13.375,19.375V13.375H19.375V19.375ZM4.625,19.375V13.375H10.625V19.375ZM5.625,9.625H9.625V5.625H5.625ZM16.325,10.575 L19.15,7.75 16.325,4.925 13.5,7.75ZM14.375,18.375H18.375V14.375H14.375ZM5.625,18.375H9.625V14.375H5.625ZM9.625,9.625ZM13.5,7.75ZM9.625,14.375ZM14.375,14.375Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -22,6 +22,6 @@ Copyright 2022 Google
|
|||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?ytTextPrimary"
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
android:pathData="M12,19H4.625Q3.925,19 3.463,18.538Q3,18.075 3,17.375V6.625Q3,5.925 3.463,5.463Q3.925,5 4.625,5H19.375Q20.075,5 20.538,5.463Q21,5.925 21,6.625V11H20V6.625Q20,6.35 19.825,6.175Q19.65,6 19.375,6H4.625Q4.35,6 4.175,6.175Q4,6.35 4,6.625V17.375Q4,17.65 4.175,17.825Q4.35,18 4.625,18H12ZM10,15.575V8.425L15.575,12ZM17.85,20.8 L17.75,19.95Q17.175,19.825 16.8,19.6Q16.425,19.375 16.1,19.025L15.3,19.4L14.725,18.525L15.45,17.95Q15.25,17.425 15.25,16.925Q15.25,16.425 15.45,15.9L14.725,15.3L15.3,14.45L16.1,14.8Q16.425,14.475 16.8,14.25Q17.175,14.025 17.75,13.9L17.85,13.05H18.85L18.95,13.9Q19.525,14.025 19.9,14.25Q20.275,14.475 20.6,14.825L21.4,14.45L21.975,15.325L21.25,15.9Q21.45,16.425 21.45,16.925Q21.45,17.425 21.25,17.95L21.975,18.525L21.4,19.4L20.6,19.025Q20.275,19.375 19.9,19.6Q19.525,19.825 18.95,19.95L18.85,20.8ZM18.35,19.075Q19.225,19.075 19.863,18.438Q20.5,17.8 20.5,16.925Q20.5,16.05 19.863,15.413Q19.225,14.775 18.35,14.775Q17.475,14.775 16.837,15.413Q16.2,16.05 16.2,16.925Q16.2,17.8 16.837,18.438Q17.475,19.075 18.35,19.075Z"/>
|
android:pathData="M12,19H4.625Q3.925,19 3.463,18.538Q3,18.075 3,17.375V6.625Q3,5.925 3.463,5.463Q3.925,5 4.625,5H19.375Q20.075,5 20.538,5.463Q21,5.925 21,6.625V11H20V6.625Q20,6.35 19.825,6.175Q19.65,6 19.375,6H4.625Q4.35,6 4.175,6.175Q4,6.35 4,6.625V17.375Q4,17.65 4.175,17.825Q4.35,18 4.625,18H12ZM10,15.575V8.425L15.575,12ZM17.85,20.8 L17.75,19.95Q17.175,19.825 16.8,19.6Q16.425,19.375 16.1,19.025L15.3,19.4L14.725,18.525L15.45,17.95Q15.25,17.425 15.25,16.925Q15.25,16.425 15.45,15.9L14.725,15.3L15.3,14.45L16.1,14.8Q16.425,14.475 16.8,14.25Q17.175,14.025 17.75,13.9L17.85,13.05H18.85L18.95,13.9Q19.525,14.025 19.9,14.25Q20.275,14.475 20.6,14.825L21.4,14.45L21.975,15.325L21.25,15.9Q21.45,16.425 21.45,16.925Q21.45,17.425 21.25,17.95L21.975,18.525L21.4,19.4L20.6,19.025Q20.275,19.375 19.9,19.6Q19.525,19.825 18.95,19.95L18.85,20.8ZM18.35,19.075Q19.225,19.075 19.863,18.438Q20.5,17.8 20.5,16.925Q20.5,16.05 19.863,15.413Q19.225,14.775 18.35,14.775Q17.475,14.775 16.837,15.413Q16.2,16.05 16.2,16.925Q16.2,17.8 16.837,18.438Q17.475,19.075 18.35,19.075Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M11.0882,3 C6.62072,3,3,6.62368,3,11.0911 C3,15.5615,6.62368,19.1822,11.0911,19.1822 C13.172,19.1822,15.0691,18.3954,16.5029,17.1039 L20.094,20.695 C21.1138,19.6754,19.8953,20.8968,21,19.7921 L17.3822,16.1773 C18.5074,14.7874,19.1822,13.018,19.1822,11.0911 C19.1793,6.62072,15.5556,3,11.0882,3 Z M11.0882,4.27895 C14.851,4.27895,17.9004,7.32829,17.9004,11.0911 C17.9004,14.851,14.8511,17.9003,11.0882,17.9003 C7.32537,17.9003,4.27603,14.851,4.27603,11.0911 C4.27898,7.32827,7.32833,4.28189,11.0882,4.27893 Z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M11.5,3.5 L3.5,11.5 L11.5,19.5 L12.3145,18.6914 L5.69531,12.0723 L21,12.0723 L21,10.9277 L5.69531,10.9277 L12.3145,4.30859 Z"/>
|
||||||
|
</vector>
|
@ -1,27 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
|
||||||
'android.R.style.ThemeOverlay_Material_Dialog' is a theme added in Android 6.0+.
|
|
||||||
|
|
||||||
'android:windowBackground' is the background color of the alert dialog.
|
|
||||||
If not overridden, the background color of the parent theme 'android.R.style.ThemeOverlay_Material_Dialog' is used.
|
|
||||||
|
|
||||||
'R.attr.ytRaisedBackground' is the background color of the YouTube alert dialog.
|
|
||||||
|
|
||||||
'android:windowIsTranslucent' makes the dialog window transparent.
|
|
||||||
|
|
||||||
'android:background' needs to be transparent for the 'contextual action bar' to be displayed properly.
|
|
||||||
|
|
||||||
Referenced documentation and framework source code:
|
|
||||||
https://android.googlesource.com/platform/frameworks/base/+/android-6.0.1_r81/core/res/res/values/themes_material.xml#1142
|
|
||||||
https://developer.android.com/reference/android/R.style#ThemeOverlay_Material_Dialog
|
|
||||||
https://developer.android.com/about/versions/marshmallow/android-6.0-changes#behavior-text-selection
|
|
||||||
-->
|
|
||||||
<resources>
|
<resources>
|
||||||
<style name="revanced_edit_text_dialog_style" parent="@android:style/ThemeOverlay.Material.Dialog">
|
|
||||||
<item name="android:windowBackground">?ytRaisedBackground</item>
|
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
|
||||||
<item name="android:background">@android:color/transparent</item>
|
|
||||||
</style>
|
|
||||||
<style name="revanced_searchbar_cursor">
|
<style name="revanced_searchbar_cursor">
|
||||||
<item name="android:textCursorDrawable">@drawable/revanced_settings_cursor</item>
|
<item name="android:textCursorDrawable">@drawable/revanced_settings_cursor</item>
|
||||||
<item name="android:textSize">16sp</item>
|
<item name="android:textSize">16sp</item>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/revanced_color_dot_widget"
|
android:id="@+id/revanced_color_dot_widget"
|
||||||
android:layout_width="20dp"
|
android:layout_width="20dp"
|
||||||
@ -15,4 +16,5 @@
|
|||||||
android:elevation="2dp"
|
android:elevation="2dp"
|
||||||
android:translationZ="2dp"
|
android:translationZ="2dp"
|
||||||
android:outlineProvider="background" />
|
android:outlineProvider="background" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:orientation="vertical">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<app.revanced.extension.shared.settings.preference.ColorPickerView
|
<app.revanced.extension.shared.settings.preference.ColorPickerView
|
||||||
android:id="@+id/color_picker_view"
|
android:id="@+id/revanced_color_picker_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp" />
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/revanced_check_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/revanced_settings_custom_checkmark"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:contentDescription="@null" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/revanced_check_icon_placeholder"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:visibility="invisible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/revanced_item_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="54dp"
|
android:minHeight="54dp">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@android:id/icon"
|
android:id="@android:id/icon"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -12,6 +13,7 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginHorizontal="18dp"
|
android:layout_marginHorizontal="18dp"
|
||||||
android:contentDescription="@null" />
|
android:contentDescription="@null" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -19,21 +21,24 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:paddingVertical="8dp">
|
android:paddingVertical="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/title"
|
android:id="@android:id/title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="@dimen/medium_font_size"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
android:textColor="?ytTextPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textAlignment="viewStart" />
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/summary"
|
android:id="@android:id/summary"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="@dimen/small_font_size"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textColor="?ytTextSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:ellipsize="end" />
|
android:ellipsize="end" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:contentDescription="History icon"
|
android:contentDescription="History icon"
|
||||||
android:src="@drawable/yt_outline_arrow_time_vd_theme_24" />
|
android:src="@drawable/revanced_settings_arrow_time" />
|
||||||
|
|
||||||
<!-- Suggestion text -->
|
<!-- Suggestion text -->
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -13,17 +13,17 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/revanced_toolbar_parent"
|
android:id="@+id/revanced_toolbar_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?android:attr/actionBarSize"
|
||||||
android:background="@color/yt_white1"
|
android:background="@android:color/white"
|
||||||
android:elevation="0dp">
|
android:elevation="0dp">
|
||||||
|
|
||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<android.support.v7.widget.Toolbar
|
<android.support.v7.widget.Toolbar
|
||||||
android:id="@+id/revanced_toolbar"
|
android:id="@+id/revanced_toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?android:attr/actionBarSize"
|
||||||
android:background="?attr/ytBrandBackgroundSolid"
|
android:background="@android:color/white"
|
||||||
app:navigationIcon="@drawable/yt_outline_arrow_left_black_24"
|
app:navigationIcon="@drawable/revanced_settings_toolbar_arrow_left"
|
||||||
app:title="@string/revanced_settings_title" />
|
app:title="@string/revanced_settings_title" />
|
||||||
|
|
||||||
<!-- Container for SearchView -->
|
<!-- Container for SearchView -->
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_search"
|
android:id="@+id/action_search"
|
||||||
android:icon="@drawable/yt_outline_search_black_24"
|
android:icon="@drawable/revanced_settings_search_icon"
|
||||||
android:title=""
|
android:title=""
|
||||||
android:showAsAction="always"/>
|
android:showAsAction="always"/>
|
||||||
</menu>
|
</menu>
|
Reference in New Issue
Block a user