diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/preference/ResettableEditTextPreference.java b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/preference/ResettableEditTextPreference.java
index 43305c23c..a67639cc0 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/preference/ResettableEditTextPreference.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/preference/ResettableEditTextPreference.java
@@ -10,6 +10,8 @@ import android.util.AttributeSet;
import android.widget.Button;
import android.widget.EditText;
+import androidx.annotation.Nullable;
+
import java.util.Objects;
import app.revanced.extension.shared.settings.Setting;
@@ -19,6 +21,12 @@ import app.revanced.extension.shared.utils.Utils;
@SuppressWarnings({"unused", "deprecation"})
public class ResettableEditTextPreference extends EditTextPreference {
+ /**
+ * Setting to reset.
+ */
+ @Nullable
+ private Setting> setting;
+
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@@ -35,6 +43,10 @@ public class ResettableEditTextPreference extends EditTextPreference {
super(context);
}
+ public void setSetting(@Nullable Setting> setting) {
+ this.setting = setting;
+ }
+
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
Utils.setEditTextDialogTheme(builder);
@@ -44,7 +56,12 @@ public class ResettableEditTextPreference extends EditTextPreference {
if (title != null) {
builder.setTitle(getTitle());
}
- final Setting> setting = Setting.getSettingFromPath(getKey());
+ if (setting == null) {
+ String key = getKey();
+ if (key != null) {
+ setting = Setting.getSettingFromPath(key);
+ }
+ }
if (setting != null) {
builder.setNeutralButton(str("revanced_extended_settings_reset"), null);
}
@@ -65,8 +82,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
}
button.setOnClickListener(v -> {
try {
- Setting> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey()));
- String defaultStringValue = setting.defaultValue.toString();
+ String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
EditText editText = getEditText();
editText.setText(defaultStringValue);
editText.setSelection(defaultStringValue.length()); // move cursor to end of text
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
index 8be012903..b5bc6994e 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
@@ -618,24 +618,34 @@ public class Settings extends BaseSettings {
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400");
+ public static final FloatSetting SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
+ public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
+ public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
+ public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
+ public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
+ public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
+ public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
+ public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
+ public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
+ public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
// SB Setting not exported
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SegmentCategoryListPreference.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SegmentCategoryListPreference.java
index b94ee3135..c62353d60 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SegmentCategoryListPreference.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SegmentCategoryListPreference.java
@@ -1,6 +1,7 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.utils.StringRef.str;
+import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
import android.app.AlertDialog;
import android.content.Context;
@@ -12,11 +13,10 @@ import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TableLayout;
-import android.widget.TableRow;
+import android.widget.GridLayout;
import android.widget.TextView;
+import java.util.Locale;
import java.util.Objects;
import app.revanced.extension.shared.utils.Logger;
@@ -27,26 +27,38 @@ import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
@SuppressWarnings({"unused", "deprecation"})
public class SegmentCategoryListPreference extends ListPreference {
- private SegmentCategory mCategory;
- private EditText mEditText;
- private int mClickedDialogEntryIndex;
+ private SegmentCategory category;
+ private TextView colorDotView;
+ private EditText colorEditText;
+ private EditText opacityEditText;
+ /**
+ * #RRGGBB
+ */
+ private int categoryColor;
+ /**
+ * [0, 1]
+ */
+ private float categoryOpacity;
+ private int selectedDialogEntryIndex;
private void init() {
final SegmentCategory segmentCategory = SegmentCategory.byCategoryKey(getKey());
- final boolean isHighlightCategory = segmentCategory == SegmentCategory.HIGHLIGHT;
- mCategory = Objects.requireNonNull(segmentCategory);
+ category = Objects.requireNonNull(segmentCategory);
+
// Edit: Using preferences to sync together multiple pieces
- // of code together is messy and should be rethought.
+ // of code is messy and should be rethought.
setKey(segmentCategory.behaviorSetting.key);
setDefaultValue(segmentCategory.behaviorSetting.defaultValue);
+ final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
setEntries(isHighlightCategory
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
: CategoryBehaviour.getBehaviorDescriptions());
setEntryValues(isHighlightCategory
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
: CategoryBehaviour.getBehaviorKeyValues());
- updateTitle();
+
+ updateTitleFromCategory();
}
public SegmentCategoryListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@@ -73,28 +85,41 @@ public class SegmentCategoryListPreference extends ListPreference {
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
try {
Utils.setEditTextDialogTheme(builder);
- super.onPrepareDialogBuilder(builder);
+
+ categoryColor = category.getColorNoOpacity();
+ categoryOpacity = category.getOpacity();
Context context = builder.getContext();
- TableLayout table = new TableLayout(context);
- table.setOrientation(LinearLayout.HORIZONTAL);
- table.setPadding(70, 0, 150, 0);
-
- TableRow row = new TableRow(context);
+ GridLayout gridLayout = new GridLayout(context);
+ gridLayout.setPadding(70, 0, 150, 0); // Padding for the entire layout.
+ gridLayout.setColumnCount(3);
+ gridLayout.setRowCount(2);
+ GridLayout.LayoutParams gridParams = new GridLayout.LayoutParams();
+ gridParams.rowSpec = GridLayout.spec(0); // First row.
+ gridParams.columnSpec = GridLayout.spec(0); // First column.
TextView colorTextLabel = new TextView(context);
colorTextLabel.setText(str("revanced_sb_color_dot_label"));
- row.addView(colorTextLabel);
+ colorTextLabel.setLayoutParams(gridParams);
+ gridLayout.addView(colorTextLabel);
- TextView colorDotView = new TextView(context);
- colorDotView.setText(mCategory.getCategoryColorDot());
- colorDotView.setPadding(30, 0, 30, 0);
- row.addView(colorDotView);
+ gridParams = new GridLayout.LayoutParams();
+ gridParams.rowSpec = GridLayout.spec(0); // First row.
+ gridParams.columnSpec = GridLayout.spec(1); // Second column.
+ gridParams.setMargins(0, 0, 10, 0);
+ colorDotView = new TextView(context);
+ colorDotView.setLayoutParams(gridParams);
+ gridLayout.addView(colorDotView);
+ updateCategoryColorDot();
- mEditText = new EditText(context);
- mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
- mEditText.setText(mCategory.colorString());
- mEditText.addTextChangedListener(new TextWatcher() {
+ gridParams = new GridLayout.LayoutParams();
+ gridParams.rowSpec = GridLayout.spec(0); // First row.
+ gridParams.columnSpec = GridLayout.spec(2); // Third column.
+ colorEditText = new EditText(context);
+ colorEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
+ colorEditText.setTextLocale(Locale.US);
+ colorEditText.setText(category.getColorString());
+ colorEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@@ -104,44 +129,111 @@ public class SegmentCategoryListPreference extends ListPreference {
}
@Override
- public void afterTextChanged(Editable s) {
+ public void afterTextChanged(Editable edit) {
try {
- String colorString = s.toString();
+ String colorString = edit.toString();
+ final int colorStringLength = colorString.length();
+
if (!colorString.startsWith("#")) {
- s.insert(0, "#"); // recursively calls back into this method
+ edit.insert(0, "#"); // Recursively calls back into this method.
return;
}
- if (colorString.length() > 7) {
- s.delete(7, colorString.length());
+
+ final int maxColorStringLength = 7; // #RRGGBB
+ if (colorStringLength > maxColorStringLength) {
+ edit.delete(maxColorStringLength, colorStringLength);
return;
}
- final int color = Color.parseColor(colorString);
- colorDotView.setText(SegmentCategory.getCategoryColorDot(color));
+
+ categoryColor = Color.parseColor(colorString);
+ updateCategoryColorDot();
} catch (IllegalArgumentException ex) {
- // ignore
+ // Ignore.
}
}
});
- mEditText.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f));
- row.addView(mEditText);
+ colorEditText.setLayoutParams(gridParams);
+ gridLayout.addView(colorEditText);
- table.addView(row);
- builder.setView(table);
- builder.setTitle(mCategory.title.toString());
+ gridParams = new GridLayout.LayoutParams();
+ gridParams.rowSpec = GridLayout.spec(1); // Second row.
+ gridParams.columnSpec = GridLayout.spec(0, 1); // First and second column.
+ TextView opacityLabel = new TextView(context);
+ opacityLabel.setText(str("revanced_sb_color_opacity_label"));
+ opacityLabel.setLayoutParams(gridParams);
+ gridLayout.addView(opacityLabel);
+
+ gridParams = new GridLayout.LayoutParams();
+ gridParams.rowSpec = GridLayout.spec(1); // Second row.
+ gridParams.columnSpec = GridLayout.spec(2); // Third column.
+ opacityEditText = new EditText(context);
+ opacityEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
+ opacityEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable edit) {
+ try {
+ String editString = edit.toString();
+ final int opacityStringLength = editString.length();
+
+ final int maxOpacityStringLength = 4; // [0.00, 1.00]
+ if (opacityStringLength > maxOpacityStringLength) {
+ edit.delete(maxOpacityStringLength, opacityStringLength);
+ return;
+ }
+
+ final float opacity = opacityStringLength == 0
+ ? 0
+ : Float.parseFloat(editString);
+ if (opacity < 0) {
+ categoryOpacity = 0;
+ edit.replace(0, opacityStringLength, "0");
+ return;
+ } else if (opacity > 1.0f) {
+ categoryOpacity = 1;
+ edit.replace(0, opacityStringLength, "1.0");
+ return;
+ } else if (!editString.endsWith(".")) {
+ // Ignore "0." and "1." until the user finishes entering a valid number.
+ categoryOpacity = opacity;
+ }
+
+ updateCategoryColorDot();
+ } catch (NumberFormatException ex) {
+ // Should never happen.
+ Logger.printException(() -> "Could not parse opacity string", ex);
+ }
+ }
+ });
+ opacityEditText.setLayoutParams(gridParams);
+ gridLayout.addView(opacityEditText);
+ updateOpacityText();
+
+ builder.setView(gridLayout);
+ builder.setTitle(category.title.toString());
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> onClick(dialog, DialogInterface.BUTTON_POSITIVE));
builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> {
try {
- mCategory.resetColor();
- updateTitle();
+ category.resetColorAndOpacity();
+ updateTitleFromCategory();
Utils.showToastShort(str("revanced_sb_color_reset"));
} catch (Exception ex) {
Logger.printException(() -> "setNeutralButton failure", ex);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
- mClickedDialogEntryIndex = findIndexOfValue(getValue());
- builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which);
+
+ selectedDialogEntryIndex = findIndexOfValue(getValue());
+ builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
+ (dialog, which) -> selectedDialogEntryIndex = which);
} catch (Exception ex) {
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
}
@@ -150,31 +242,51 @@ public class SegmentCategoryListPreference extends ListPreference {
@Override
protected void onDialogClosed(boolean positiveResult) {
try {
- if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) {
- String value = getEntryValues()[mClickedDialogEntryIndex].toString();
+ if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
+ String value = getEntryValues()[selectedDialogEntryIndex].toString();
if (callChangeListener(value)) {
setValue(value);
- mCategory.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
+ category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
SegmentCategory.updateEnabledCategories();
}
- String colorString = mEditText.getText().toString();
try {
- if (!colorString.equals(mCategory.colorString())) {
- mCategory.setColor(colorString);
+ String colorString = colorEditText.getText().toString();
+ if (!colorString.equals(category.getColorString()) || categoryOpacity != category.getOpacity()) {
+ category.setColor(colorString);
+ category.setOpacity(categoryOpacity);
Utils.showToastShort(str("revanced_sb_color_changed"));
}
} catch (IllegalArgumentException ex) {
Utils.showToastShort(str("revanced_sb_color_invalid"));
}
- updateTitle();
+
+ updateTitleFromCategory();
}
} catch (Exception ex) {
Logger.printException(() -> "onDialogClosed failure", ex);
}
}
- private void updateTitle() {
- setTitle(mCategory.getTitleWithColorDot());
+ private void applyOpacityToCategoryColor() {
+ categoryColor = applyOpacityToColor(categoryColor, categoryOpacity);
+ }
+
+ private void updateTitleFromCategory() {
+ categoryColor = category.getColorNoOpacity();
+ categoryOpacity = category.getOpacity();
+ applyOpacityToCategoryColor();
+
+ setTitle(category.getTitleWithColorDot(categoryColor));
setEnabled(Settings.SB_ENABLED.get());
}
+
+ private void updateCategoryColorDot() {
+ applyOpacityToCategoryColor();
+
+ colorDotView.setText(SegmentCategory.getCategoryColorDot(categoryColor));
+ }
+
+ private void updateOpacityText() {
+ opacityEditText.setText(String.format(Locale.US, "%.2f", categoryOpacity));
+ }
}
\ No newline at end of file
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockSettings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockSettings.java
index d4228f3ec..11a588fba 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockSettings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockSettings.java
@@ -139,7 +139,7 @@ public class SponsorBlockSettings {
for (SegmentCategory category : categories) {
JSONObject categoryObject = new JSONObject();
String categoryKey = category.keyValue;
- categoryObject.put("color", category.colorString());
+ categoryObject.put("color", category.getColorString());
barTypesObject.put(categoryKey, categoryObject);
if (category.behaviour != CategoryBehaviour.IGNORE) {
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockUtils.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockUtils.java
index 56dc52977..3d4da44e9 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockUtils.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/SponsorBlockUtils.java
@@ -6,7 +6,12 @@ import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.text.Html;
+import android.graphics.Color;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.widget.EditText;
import androidx.annotation.NonNull;
@@ -33,10 +38,9 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
/**
* Not thread safe. All fields/methods must be accessed from the main thread.
*
- * @noinspection deprecation
*/
public class SponsorBlockUtils {
- private static final String LOCKED_COLOR = "#FFC83D";
+ private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");
private static final String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
private static final Pattern manualEditTimePattern
@@ -162,28 +166,34 @@ public class SponsorBlockUtils {
SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT)
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
: SegmentVote.values();
- CharSequence[] items = new CharSequence[voteOptions.length];
+ final int voteOptionsLength = voteOptions.length;
+ final boolean userIsVip = Settings.SB_USER_IS_VIP.get();
+ CharSequence[] items = new CharSequence[voteOptionsLength];
- for (int i = 0; i < voteOptions.length; i++) {
+ for (int i = 0; i < voteOptionsLength; i++) {
SegmentVote voteOption = voteOptions[i];
- String title = voteOption.title.toString();
- if (Settings.SB_USER_IS_VIP.get() && segment.isLocked && voteOption.shouldHighlight) {
- items[i] = Html.fromHtml(String.format("%s", LOCKED_COLOR, title));
- } else {
- items[i] = title;
+ CharSequence title = voteOption.title.toString();
+ if (userIsVip && segment.isLocked && voteOption.highlightIfVipAndVideoIsLocked) {
+ SpannableString coloredTitle = new SpannableString(title);
+ coloredTitle.setSpan(new ForegroundColorSpan(LOCKED_COLOR),
+ 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ title = coloredTitle;
}
+ items[i] = title;
}
- new AlertDialog.Builder(context)
- .setItems(items, (dialog1, which1) -> {
- SegmentVote voteOption = voteOptions[which1];
- switch (voteOption) {
- case UPVOTE, DOWNVOTE ->
- SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
- case CATEGORY_CHANGE -> onNewCategorySelect(segment, context);
- }
- })
- .show();
+ new AlertDialog.Builder(context).setItems(items, (dialog1, which1) -> {
+ SegmentVote voteOption = voteOptions[which1];
+ switch (voteOption) {
+ case UPVOTE:
+ case DOWNVOTE:
+ SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
+ break;
+ case CATEGORY_CHANGE:
+ onNewCategorySelect(segment, context);
+ break;
+ }
+ }).show();
} catch (Exception ex) {
Logger.printException(() -> "segmentVoteClickListener failure", ex);
}
@@ -287,22 +297,33 @@ public class SponsorBlockUtils {
if (segment.category == SegmentCategory.UNSUBMITTED) {
continue;
}
- StringBuilder htmlBuilder = new StringBuilder();
- htmlBuilder.append(String.format("⬤ %s
",
- segment.category.color, segment.category.title));
- htmlBuilder.append(formatSegmentTime(segment.start));
- if (segment.category != SegmentCategory.HIGHLIGHT) {
- htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
+
+ SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
+
+ spannableBuilder.append(segment.category.getTitleWithColorDot());
+ spannableBuilder.append('\n');
+
+ String startTime = formatSegmentTime(segment.start);
+ if (segment.category == SegmentCategory.HIGHLIGHT) {
+ spannableBuilder.append(startTime);
+ } else {
+ String toFromString = str("revanced_sb_vote_segment_time_to_from",
+ startTime, formatSegmentTime(segment.end));
+ spannableBuilder.append(toFromString);
}
- htmlBuilder.append("");
- if (i + 1 != numberOfSegments) // prevents trailing new line after last segment
- htmlBuilder.append("
");
- titles[i] = Html.fromHtml(htmlBuilder.toString());
+
+ if (i + 1 != numberOfSegments) {
+ // prevents trailing new line after last segment
+ spannableBuilder.append('\n');
+ }
+
+ spannableBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
+ 0, spannableBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ titles[i] = spannableBuilder;
}
- new AlertDialog.Builder(context)
- .setItems(titles, segmentVoteClickListener)
- .show();
+ new AlertDialog.Builder(context).setItems(titles, segmentVoteClickListener).show();
} catch (Exception ex) {
Logger.printException(() -> "onVotingClicked failure", ex);
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SegmentCategory.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SegmentCategory.java
index 3d1e90f66..fd8360a95 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SegmentCategory.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SegmentCategory.java
@@ -1,32 +1,14 @@
package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.shared.utils.StringRef.sf;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR_COLOR;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED;
-import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED_COLOR;
+import static app.revanced.extension.youtube.settings.Settings.*;
import android.graphics.Color;
import android.graphics.Paint;
-import android.text.Html;
-import android.text.Spanned;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -34,44 +16,46 @@ import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.StringRef;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings;
-@SuppressWarnings({"deprecation", "StaticFieldLeak"})
+@SuppressWarnings("StaticFieldLeak")
public enum SegmentCategory {
SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_skip_button_sponsor"), sf("revanced_sb_skipped_sponsor"),
- SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR),
+ SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR, SB_CATEGORY_SPONSOR_OPACITY),
SELF_PROMO("selfpromo", sf("revanced_sb_segments_selfpromo"), sf("revanced_sb_skip_button_selfpromo"), sf("revanced_sb_skipped_selfpromo"),
- SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR),
+ SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR, SB_CATEGORY_SELF_PROMO_OPACITY),
INTERACTION("interaction", sf("revanced_sb_segments_interaction"), sf("revanced_sb_skip_button_interaction"), sf("revanced_sb_skipped_interaction"),
- SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR),
+ SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR, SB_CATEGORY_INTERACTION_OPACITY),
/**
* Unique category that is treated differently than the rest.
*/
HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_skip_button_highlight"), sf("revanced_sb_skipped_highlight"),
- SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR),
+ SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR, SB_CATEGORY_HIGHLIGHT_OPACITY),
INTRO("intro", sf("revanced_sb_segments_intro"),
sf("revanced_sb_skip_button_intro_beginning"), sf("revanced_sb_skip_button_intro_middle"), sf("revanced_sb_skip_button_intro_end"),
sf("revanced_sb_skipped_intro_beginning"), sf("revanced_sb_skipped_intro_middle"), sf("revanced_sb_skipped_intro_end"),
- SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR),
+ SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR, SB_CATEGORY_INTRO_OPACITY),
OUTRO("outro", sf("revanced_sb_segments_outro"), sf("revanced_sb_skip_button_outro"), sf("revanced_sb_skipped_outro"),
- SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR),
+ SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR, SB_CATEGORY_OUTRO_OPACITY),
PREVIEW("preview", sf("revanced_sb_segments_preview"),
sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"),
sf("revanced_sb_skipped_preview_beginning"), sf("revanced_sb_skipped_preview_middle"), sf("revanced_sb_skipped_preview_end"),
- SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR),
+ SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR, SB_CATEGORY_PREVIEW_OPACITY),
FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"),
- SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR),
+ SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR, SB_CATEGORY_FILLER_OPACITY),
MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"),
- SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR),
+ SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR, SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY),
UNSUBMITTED("unsubmitted", StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"),
- SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR),
+ SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR, SB_CATEGORY_UNSUBMITTED_OPACITY);
;
private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
@@ -111,12 +95,10 @@ public enum SegmentCategory {
mValuesMap.put(value.keyValue, value);
}
- @NonNull
public static SegmentCategory[] categoriesWithoutUnsubmitted() {
return categoriesWithoutUnsubmitted;
}
- @NonNull
public static SegmentCategory[] categoriesWithoutHighlights() {
return categoriesWithoutHighlights;
}
@@ -127,7 +109,7 @@ public enum SegmentCategory {
}
/**
- * Must be called if behavior of any category is changed
+ * Must be called if behavior of any category is changed.
*/
public static void updateEnabledCategories() {
Utils.verifyOnMainThread();
@@ -154,30 +136,32 @@ public enum SegmentCategory {
updateEnabledCategories();
}
- @NonNull
- public final String keyValue;
- @NonNull
- public final StringSetting behaviorSetting;
- @NonNull
- private final StringSetting colorSetting;
+ public static int applyOpacityToColor(int color, float opacity) {
+ if (opacity < 0 || opacity > 1.0f) {
+ throw new IllegalArgumentException("Invalid opacity: " + opacity);
+ }
+ final int opacityInt = (int) (255 * opacity);
+ return (color & 0x00FFFFFF) | (opacityInt << 24);
+ }
+
+ public final String keyValue;
+ public final StringSetting behaviorSetting; // TODO: Replace with EnumSetting.
+ private final StringSetting colorSetting;
+ private final FloatSetting opacitySetting;
- @NonNull
public final StringRef title;
/**
* Skip button text, if the skip occurs in the first quarter of the video
*/
- @NonNull
public final StringRef skipButtonTextBeginning;
/**
* Skip button text, if the skip occurs in the middle half of the video
*/
- @NonNull
public final StringRef skipButtonTextMiddle;
/**
* Skip button text, if the skip occurs in the last quarter of the video
*/
- @NonNull
public final StringRef skipButtonTextEnd;
/**
* Skipped segment toast, if the skip occurred in the first quarter of the video
@@ -198,10 +182,7 @@ public enum SegmentCategory {
@NonNull
public final Paint paint;
- /**
- * Value must be changed using {@link #setColor(String)}.
- */
- public int color;
+ private int color;
/**
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
@@ -213,17 +194,20 @@ public enum SegmentCategory {
SegmentCategory(String keyValue, StringRef title,
StringRef skipButtonText,
StringRef skippedToastText,
- StringSetting behavior, StringSetting color) {
+ StringSetting behavior,
+ StringSetting color, FloatSetting opacity) {
this(keyValue, title,
skipButtonText, skipButtonText, skipButtonText,
skippedToastText, skippedToastText, skippedToastText,
- behavior, color);
+ behavior,
+ color, opacity);
}
SegmentCategory(String keyValue, StringRef title,
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
- StringSetting behavior, StringSetting color) {
+ StringSetting behavior,
+ StringSetting color, FloatSetting opacity) {
this.keyValue = Objects.requireNonNull(keyValue);
this.title = Objects.requireNonNull(title);
this.skipButtonTextBeginning = Objects.requireNonNull(skipButtonTextBeginning);
@@ -234,6 +218,7 @@ public enum SegmentCategory {
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
this.behaviorSetting = Objects.requireNonNull(behavior);
this.colorSetting = Objects.requireNonNull(color);
+ this.opacitySetting = Objects.requireNonNull(opacity);
this.paint = new Paint();
loadFromSettings();
}
@@ -250,11 +235,14 @@ public enum SegmentCategory {
this.behaviour = savedBehavior;
String colorString = colorSetting.get();
+ final float opacity = opacitySetting.get();
try {
setColor(colorString);
+ setOpacity(opacity);
} catch (Exception ex) {
- Logger.printException(() -> "Invalid color: " + colorString, ex);
+ Logger.printException(() -> "Invalid color: " + colorString + " opacity: " + opacity, ex);
colorSetting.resetToDefault();
+ opacitySetting.resetToDefault();
loadFromSettings();
}
}
@@ -264,45 +252,77 @@ public enum SegmentCategory {
this.behaviorSetting.save(behaviour.reVancedKeyValue);
}
- /**
- * @return HTML color format string
- */
- @NonNull
- public String colorString() {
- return String.format("#%06X", color);
- }
-
- public void setColor(@NonNull String colorString) throws IllegalArgumentException {
- final int color = Color.parseColor(colorString) & 0xFFFFFF;
- this.color = color;
+ private void updateColor() {
+ color = applyOpacityToColor(color, opacitySetting.get());
paint.setColor(color);
- paint.setAlpha(255);
- colorSetting.save(colorString); // Save after parsing.
}
- public void resetColor() {
+ /**
+ * @param opacity Segment color opacity between [0, 1].
+ */
+ public void setOpacity(float opacity) throws IllegalArgumentException {
+ if (opacity < 0 || opacity > 1) {
+ throw new IllegalArgumentException("Invalid opacity: " + opacity);
+ }
+
+ opacitySetting.save(opacity);
+ updateColor();
+ }
+
+ public float getOpacity() {
+ return opacitySetting.get();
+ }
+
+ public void resetColorAndOpacity() {
setColor(colorSetting.defaultValue);
+ setOpacity(opacitySetting.defaultValue);
}
- @NonNull
- private static String getCategoryColorDotHTML(int color) {
- color &= 0xFFFFFF;
- return String.format("⬤", color);
+ /**
+ * @param colorString Segment color with #RRGGBB format.
+ */
+ public void setColor(String colorString) throws IllegalArgumentException {
+ color = Color.parseColor(colorString);
+ colorSetting.save(colorString);
+
+ updateColor();
}
- @NonNull
- public static Spanned getCategoryColorDot(int color) {
- return Html.fromHtml(getCategoryColorDotHTML(color));
+ /**
+ * @return Integer color of #RRGGBB format.
+ */
+ public int getColorNoOpacity() {
+ return color & 0x00FFFFFF;
}
- @NonNull
- public Spanned getCategoryColorDot() {
+ /**
+ * @return Hex color string of #RRGGBB format with no opacity level.
+ */
+ public String getColorString() {
+ return String.format(Locale.US, "#%06X", getColorNoOpacity());
+ }
+
+ private static SpannableString getCategoryColorDotSpan(String text, int color) {
+ SpannableString dotSpan = new SpannableString('⬤' + text);
+ dotSpan.setSpan(new ForegroundColorSpan(color), 0, 1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return dotSpan;
+ }
+
+ public static SpannableString getCategoryColorDot(int color) {
+ return getCategoryColorDotSpan("", color);
+ }
+
+ public SpannableString getCategoryColorDot() {
return getCategoryColorDot(color);
}
- @NonNull
- public Spanned getTitleWithColorDot() {
- return Html.fromHtml(getCategoryColorDotHTML(color) + " " + title);
+ public SpannableString getTitleWithColorDot(int categoryColor) {
+ return getCategoryColorDotSpan(" " + title, categoryColor);
+ }
+
+ public SpannableString getTitleWithColorDot() {
+ return getTitleWithColorDot(color);
}
/**
@@ -310,7 +330,6 @@ public enum SegmentCategory {
* @param videoLength length of the video
* @return the skip button text
*/
- @NonNull
StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
return (this == SegmentCategory.HIGHLIGHT)
@@ -319,7 +338,7 @@ public enum SegmentCategory {
}
if (videoLength == 0) {
- return skipButtonTextBeginning; // video is still loading. Assume it's the beginning
+ return skipButtonTextBeginning; // Video is still loading. Assume it's the beginning.
}
final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) {
@@ -335,10 +354,9 @@ public enum SegmentCategory {
* @param videoLength length of the video
* @return 'skipped segment' toast message
*/
- @NonNull
StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
if (videoLength == 0) {
- return skippedToastBeginning; // video is still loading. Assume it's the beginning
+ return skippedToastBeginning; // Video is still loading. Assume it's the beginning.
}
final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) {
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SponsorSegment.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SponsorSegment.java
index 51208c1cc..0bd5aa435 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SponsorSegment.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SponsorSegment.java
@@ -24,12 +24,15 @@ public class SponsorSegment implements Comparable {
@NonNull
public final StringRef title;
public final int apiVoteType;
- public final boolean shouldHighlight;
+ /**
+ * If the option should be highlighted for VIP users.
+ */
+ public final boolean highlightIfVipAndVideoIsLocked;
- SegmentVote(@NonNull StringRef title, int apiVoteType, boolean shouldHighlight) {
+ SegmentVote(@NonNull StringRef title, int apiVoteType, boolean highlightIfVipAndVideoIsLocked) {
this.title = title;
this.apiVoteType = apiVoteType;
- this.shouldHighlight = shouldHighlight;
+ this.highlightIfVipAndVideoIsLocked = highlightIfVipAndVideoIsLocked;
}
}
diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml
index 4d511c850..c2bdc0677 100644
--- a/patches/src/main/resources/youtube/settings/host/values/strings.xml
+++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml
@@ -1964,6 +1964,7 @@ Click to see how to issue an API key."
Show in seekbar
Disable
+ Opacity:
Color:
Color changed.
Color reset.
@@ -2036,6 +2037,8 @@ Click to see how to issue an API key."
Downvote
Change category
There are no segments to vote for.
+
+ %1$s to %2$s
Choose the segment category
Category is disabled in settings. Enable category to submit.