feat(YouTube - SponsorBlock): Add opacity setting to category segment colors (#4582)

This commit is contained in:
LisoUseInAIKyrios 2025-03-19 18:02:06 +01:00 committed by GitHub
parent 6e4882e74f
commit 6e8ffbade9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 382 additions and 167 deletions

View File

@ -1,5 +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 android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
@ -8,17 +10,23 @@ import android.util.AttributeSet;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import app.revanced.extension.shared.Utils; import androidx.annotation.Nullable;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.Logger;
import java.util.Objects; import java.util.Objects;
import static app.revanced.extension.shared.StringRef.str; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
public class ResettableEditTextPreference extends EditTextPreference { public class ResettableEditTextPreference extends EditTextPreference {
/**
* Setting to reset.
*/
@Nullable
private Setting<?> setting;
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
} }
@ -32,12 +40,22 @@ public class ResettableEditTextPreference extends EditTextPreference {
super(context); super(context);
} }
public void setSetting(@Nullable Setting<?> setting) {
this.setting = setting;
}
@Override @Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder); super.onPrepareDialogBuilder(builder);
Utils.setEditTextDialogTheme(builder); Utils.setEditTextDialogTheme(builder);
Setting<?> setting = Setting.getSettingFromPath(getKey()); if (setting == null) {
String key = getKey();
if (key != null) {
setting = Setting.getSettingFromPath(key);
}
}
if (setting != null) { if (setting != null) {
builder.setNeutralButton(str("revanced_settings_reset"), null); builder.setNeutralButton(str("revanced_settings_reset"), null);
} }
@ -54,8 +72,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
} }
button.setOnClickListener(v -> { button.setOnClickListener(v -> {
try { try {
Setting<?> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey())); String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
String defaultStringValue = setting.defaultValue.toString();
EditText editText = getEditText(); EditText editText = getEditText();
editText.setText(defaultStringValue); editText.setText(defaultStringValue);
editText.setSelection(defaultStringValue.length()); // move cursor to end of text editText.setSelection(defaultStringValue.length()); // move cursor to end of text

View File

@ -365,24 +365,34 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false); public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400"); 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", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00"); 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", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF"); 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 = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684"); 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", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF"); 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", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED"); 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", IGNORE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6"); 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", IGNORE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF"); 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 = 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 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 = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF"); 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);
// Deprecated migrations // Deprecated migrations
private static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024 private static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024

View File

@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
import android.preference.SwitchPreference; import android.preference.SwitchPreference;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch; import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
@ -237,6 +238,8 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
"revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary")); "revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary"));
preferenceScreen.addPreference(statisticPreference); preferenceScreen.addPreference(statisticPreference);
} }
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onCreate failure", ex); Logger.printException(() -> "onCreate failure", ex);
} }

View File

@ -17,6 +17,7 @@ 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.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.preference.ResettableEditTextPreference;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings; import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
@ -44,8 +45,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
private SwitchPreference showTimeWithoutSegments; private SwitchPreference showTimeWithoutSegments;
private SwitchPreference toastOnConnectionError; private SwitchPreference toastOnConnectionError;
private EditTextPreference newSegmentStep; private ResettableEditTextPreference newSegmentStep;
private EditTextPreference minSegmentDuration; private ResettableEditTextPreference minSegmentDuration;
private EditTextPreference privateUserId; private EditTextPreference privateUserId;
private EditTextPreference importExport; private EditTextPreference importExport;
private Preference apiUrl; private Preference apiUrl;
@ -159,6 +160,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
addAboutCategory(context, preferenceScreen); addAboutCategory(context, preferenceScreen);
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
updateUI(); updateUI();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onCreate failure", ex); Logger.printException(() -> "onCreate failure", ex);
@ -268,7 +271,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; return true;
}); });
newSegmentStep = new EditTextPreference(context); newSegmentStep = new ResettableEditTextPreference(context);
newSegmentStep.setSetting(Settings.SB_CREATE_NEW_SEGMENT_STEP);
newSegmentStep.setTitle(str("revanced_sb_general_adjusting")); newSegmentStep.setTitle(str("revanced_sb_general_adjusting"));
newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum")); newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum"));
newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER); newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
@ -326,7 +330,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
}); });
category.addPreference(trackSkips); category.addPreference(trackSkips);
minSegmentDuration = new EditTextPreference(context); minSegmentDuration = new ResettableEditTextPreference(context);
minSegmentDuration.setSetting(Settings.SB_SEGMENT_MIN_DURATION);
minSegmentDuration.setTitle(str("revanced_sb_general_min_duration")); minSegmentDuration.setTitle(str("revanced_sb_general_min_duration"));
minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum")); minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum"));
minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
@ -345,7 +350,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
}); });
category.addPreference(minSegmentDuration); category.addPreference(minSegmentDuration);
privateUserId = new EditTextPreference(context); privateUserId = new EditTextPreference(context) {
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
Utils.setEditTextDialogTheme(builder);
builder.setNeutralButton(str("revanced_sb_settings_copy"), (dialog, which) -> {
Utils.setClipboard(getEditText().getText().toString());
});
}
};
privateUserId.setTitle(str("revanced_sb_general_uuid")); privateUserId.setTitle(str("revanced_sb_general_uuid"));
privateUserId.setSummary(str("revanced_sb_general_uuid_sum")); privateUserId.setSummary(str("revanced_sb_general_uuid_sum"));
privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> { privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> {
@ -504,7 +517,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
if (stats.totalSegmentCountIncludingIgnored > 0) { if (stats.totalSegmentCountIncludingIgnored > 0) {
// If user has not created any segments, there's no reason to set a username. // If user has not created any segments, there's no reason to set a username.
EditTextPreference preference = new EditTextPreference(context); EditTextPreference preference = new ResettableEditTextPreference(context);
statsCategory.addPreference(preference); statsCategory.addPreference(preference);
String userName = stats.userName; String userName = stats.userName;
preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName))); preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName)));

View File

@ -136,7 +136,7 @@ public class SponsorBlockSettings {
for (SegmentCategory category : categories) { for (SegmentCategory category : categories) {
JSONObject categoryObject = new JSONObject(); JSONObject categoryObject = new JSONObject();
String categoryKey = category.keyValue; String categoryKey = category.keyValue;
categoryObject.put("color", category.colorString()); categoryObject.put("color", category.getColorString());
barTypesObject.put(categoryKey, categoryObject); barTypesObject.put(categoryKey, categoryObject);
if (category.behaviour != CategoryBehaviour.IGNORE) { if (category.behaviour != CategoryBehaviour.IGNORE) {

View File

@ -5,7 +5,12 @@ import static app.revanced.extension.shared.StringRef.str;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; 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 android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -33,7 +38,7 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
* Not thread safe. All fields/methods must be accessed from the main thread. * Not thread safe. All fields/methods must be accessed from the main thread.
*/ */
public class SponsorBlockUtils { 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 String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
private static final Pattern manualEditTimePattern private static final Pattern manualEditTimePattern
= Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?"); = Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?");
@ -160,20 +165,23 @@ public class SponsorBlockUtils {
SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT) SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT)
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category ? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
: SegmentVote.values(); : 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]; SegmentVote voteOption = voteOptions[i];
String title = voteOption.title.toString(); CharSequence title = voteOption.title.toString();
if (Settings.SB_USER_IS_VIP.get() && segment.isLocked && voteOption.shouldHighlight) { if (userIsVip && segment.isLocked && voteOption.highlightIfVipAndVideoIsLocked) {
items[i] = Html.fromHtml(String.format("<font color=\"%s\">%s</font>", LOCKED_COLOR, title)); SpannableString coloredTitle = new SpannableString(title);
} else { coloredTitle.setSpan(new ForegroundColorSpan(LOCKED_COLOR),
0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
title = coloredTitle;
}
items[i] = title; items[i] = title;
} }
}
new AlertDialog.Builder(context) new AlertDialog.Builder(context).setItems(items, (dialog1, which1) -> {
.setItems(items, (dialog1, which1) -> {
SegmentVote voteOption = voteOptions[which1]; SegmentVote voteOption = voteOptions[which1];
switch (voteOption) { switch (voteOption) {
case UPVOTE: case UPVOTE:
@ -184,8 +192,7 @@ public class SponsorBlockUtils {
onNewCategorySelect(segment, context); onNewCategorySelect(segment, context);
break; break;
} }
}) }).show();
.show();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "segmentVoteClickListener failure", ex); Logger.printException(() -> "segmentVoteClickListener failure", ex);
} }
@ -282,7 +289,6 @@ public class SponsorBlockUtils {
return; return;
} }
final int numberOfSegments = segments.length; final int numberOfSegments = segments.length;
CharSequence[] titles = new CharSequence[numberOfSegments]; CharSequence[] titles = new CharSequence[numberOfSegments];
for (int i = 0; i < numberOfSegments; i++) { for (int i = 0; i < numberOfSegments; i++) {
@ -290,22 +296,33 @@ public class SponsorBlockUtils {
if (segment.category == SegmentCategory.UNSUBMITTED) { if (segment.category == SegmentCategory.UNSUBMITTED) {
continue; continue;
} }
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append(String.format("<b><font color=\"#%06X\">⬤</font> %s<br>", SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
segment.category.color, segment.category.title));
htmlBuilder.append(formatSegmentTime(segment.start)); spannableBuilder.append(segment.category.getTitleWithColorDot());
if (segment.category != SegmentCategory.HIGHLIGHT) { spannableBuilder.append('\n');
htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
} String startTime = formatSegmentTime(segment.start);
htmlBuilder.append("</b>"); if (segment.category == SegmentCategory.HIGHLIGHT) {
if (i + 1 != numberOfSegments) // prevents trailing new line after last segment spannableBuilder.append(startTime);
htmlBuilder.append("<br>"); } else {
titles[i] = Html.fromHtml(htmlBuilder.toString()); String toFromString = str("revanced_sb_vote_segment_time_to_from",
startTime, formatSegmentTime(segment.end));
spannableBuilder.append(toFromString);
} }
new AlertDialog.Builder(context) if (i + 1 != numberOfSegments) {
.setItems(titles, segmentVoteClickListener) // prevents trailing new line after last segment
.show(); 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();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onVotingClicked failure", ex); Logger.printException(() -> "onVotingClicked failure", ex);
} }

View File

@ -1,13 +1,14 @@
package app.revanced.extension.youtube.sponsorblock.objects; package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.youtube.settings.Settings.*;
import static app.revanced.extension.shared.StringRef.sf; import static app.revanced.extension.shared.StringRef.sf;
import static app.revanced.extension.youtube.settings.Settings.*;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.text.Html; import android.text.Spannable;
import android.text.Spanned; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -15,43 +16,45 @@ import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.StringRef; import app.revanced.extension.shared.StringRef;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.youtube.settings.Settings;
public enum SegmentCategory { public enum SegmentCategory {
SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_segments_sponsor_sum"), sf("revanced_sb_skip_button_sponsor"), sf("revanced_sb_skipped_sponsor"), SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_segments_sponsor_sum"), 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_segments_selfpromo_sum"), sf("revanced_sb_skip_button_selfpromo"), sf("revanced_sb_skipped_selfpromo"), SELF_PROMO("selfpromo", sf("revanced_sb_segments_selfpromo"), sf("revanced_sb_segments_selfpromo_sum"), 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_segments_interaction_sum"), sf("revanced_sb_skip_button_interaction"), sf("revanced_sb_skipped_interaction"), INTERACTION("interaction", sf("revanced_sb_segments_interaction"), sf("revanced_sb_segments_interaction_sum"), 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. * Unique category that is treated differently than the rest.
*/ */
HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_segments_highlight_sum"), sf("revanced_sb_skip_button_highlight"), sf("revanced_sb_skipped_highlight"), HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_segments_highlight_sum"), 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_segments_intro_sum"), INTRO("intro", sf("revanced_sb_segments_intro"), sf("revanced_sb_segments_intro_sum"),
sf("revanced_sb_skip_button_intro_beginning"), sf("revanced_sb_skip_button_intro_middle"), sf("revanced_sb_skip_button_intro_end"), 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"), 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_segments_outro_sum"), sf("revanced_sb_skip_button_outro"), sf("revanced_sb_skipped_outro"), OUTRO("outro", sf("revanced_sb_segments_outro"), sf("revanced_sb_segments_outro_sum"), 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_segments_preview_sum"), PREVIEW("preview", sf("revanced_sb_segments_preview"), sf("revanced_sb_segments_preview_sum"),
sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"), 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"), 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_segments_filler_sum"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"), FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_segments_filler_sum"), 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_segments_nomusic_sum"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"), MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_segments_nomusic_sum"), 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, StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"), UNSUBMITTED("unsubmitted", StringRef.empty, 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"); private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight"); private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
@ -90,12 +93,10 @@ public enum SegmentCategory {
mValuesMap.put(value.keyValue, value); mValuesMap.put(value.keyValue, value);
} }
@NonNull
public static SegmentCategory[] categoriesWithoutUnsubmitted() { public static SegmentCategory[] categoriesWithoutUnsubmitted() {
return categoriesWithoutUnsubmitted; return categoriesWithoutUnsubmitted;
} }
@NonNull
public static SegmentCategory[] categoriesWithoutHighlights() { public static SegmentCategory[] categoriesWithoutHighlights() {
return categoriesWithoutHighlights; return categoriesWithoutHighlights;
} }
@ -106,7 +107,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() { public static void updateEnabledCategories() {
Utils.verifyOnMainThread(); Utils.verifyOnMainThread();
@ -133,32 +134,33 @@ public enum SegmentCategory {
updateEnabledCategories(); updateEnabledCategories();
} }
@NonNull public static int applyOpacityToColor(int color, float opacity) {
public final String keyValue; if (opacity < 0 || opacity > 1.0f) {
@NonNull throw new IllegalArgumentException("Invalid opacity: " + opacity);
public final StringSetting behaviorSetting; }
@NonNull final int opacityInt = (int) (255 * opacity);
private final StringSetting colorSetting; 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; public final StringRef title;
@NonNull
public final StringRef description; public final StringRef description;
/** /**
* Skip button text, if the skip occurs in the first quarter of the video * Skip button text, if the skip occurs in the first quarter of the video
*/ */
@NonNull
public final StringRef skipButtonTextBeginning; public final StringRef skipButtonTextBeginning;
/** /**
* Skip button text, if the skip occurs in the middle half of the video * Skip button text, if the skip occurs in the middle half of the video
*/ */
@NonNull
public final StringRef skipButtonTextMiddle; public final StringRef skipButtonTextMiddle;
/** /**
* Skip button text, if the skip occurs in the last quarter of the video * Skip button text, if the skip occurs in the last quarter of the video
*/ */
@NonNull
public final StringRef skipButtonTextEnd; public final StringRef skipButtonTextEnd;
/** /**
* Skipped segment toast, if the skip occurred in the first quarter of the video * Skipped segment toast, if the skip occurred in the first quarter of the video
@ -179,10 +181,7 @@ public enum SegmentCategory {
@NonNull @NonNull
public final Paint paint; public final Paint paint;
/** private int color;
* Value must be changed using {@link #setColor(String)}.
*/
public int color;
/** /**
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}. * Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
@ -194,17 +193,20 @@ public enum SegmentCategory {
SegmentCategory(String keyValue, StringRef title, StringRef description, SegmentCategory(String keyValue, StringRef title, StringRef description,
StringRef skipButtonText, StringRef skipButtonText,
StringRef skippedToastText, StringRef skippedToastText,
StringSetting behavior, StringSetting color) { StringSetting behavior,
StringSetting color, FloatSetting opacity) {
this(keyValue, title, description, this(keyValue, title, description,
skipButtonText, skipButtonText, skipButtonText, skipButtonText, skipButtonText, skipButtonText,
skippedToastText, skippedToastText, skippedToastText, skippedToastText, skippedToastText, skippedToastText,
behavior, color); behavior,
color, opacity);
} }
SegmentCategory(String keyValue, StringRef title, StringRef description, SegmentCategory(String keyValue, StringRef title, StringRef description,
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd, StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd, StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
StringSetting behavior, StringSetting color) { StringSetting behavior,
StringSetting color, FloatSetting opacity) {
this.keyValue = Objects.requireNonNull(keyValue); this.keyValue = Objects.requireNonNull(keyValue);
this.title = Objects.requireNonNull(title); this.title = Objects.requireNonNull(title);
this.description = Objects.requireNonNull(description); this.description = Objects.requireNonNull(description);
@ -216,6 +218,7 @@ public enum SegmentCategory {
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd); this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
this.behaviorSetting = Objects.requireNonNull(behavior); this.behaviorSetting = Objects.requireNonNull(behavior);
this.colorSetting = Objects.requireNonNull(color); this.colorSetting = Objects.requireNonNull(color);
this.opacitySetting = Objects.requireNonNull(opacity);
this.paint = new Paint(); this.paint = new Paint();
loadFromSettings(); loadFromSettings();
} }
@ -232,11 +235,14 @@ public enum SegmentCategory {
this.behaviour = savedBehavior; this.behaviour = savedBehavior;
String colorString = colorSetting.get(); String colorString = colorSetting.get();
final float opacity = opacitySetting.get();
try { try {
setColor(colorString); setColor(colorString);
setOpacity(opacity);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "Invalid color: " + colorString, ex); Logger.printException(() -> "Invalid color: " + colorString + " opacity: " + opacity, ex);
colorSetting.resetToDefault(); colorSetting.resetToDefault();
opacitySetting.resetToDefault();
loadFromSettings(); loadFromSettings();
} }
} }
@ -245,45 +251,78 @@ public enum SegmentCategory {
this.behaviour = Objects.requireNonNull(behaviour); this.behaviour = Objects.requireNonNull(behaviour);
this.behaviorSetting.save(behaviour.reVancedKeyValue); 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 { private void updateColor() {
final int color = Color.parseColor(colorString) & 0xFFFFFF; color = applyOpacityToColor(color, opacitySetting.get());
this.color = color;
paint.setColor(color); 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); setColor(colorSetting.defaultValue);
setOpacity(opacitySetting.defaultValue);
} }
@NonNull /**
private static String getCategoryColorDotHTML(int color) { * @param colorString Segment color with #RRGGBB format.
color &= 0xFFFFFF; */
return String.format("<font color=\"#%06X\">⬤</font>", color); public void setColor(String colorString) throws IllegalArgumentException {
color = Color.parseColor(colorString);
colorSetting.save(colorString);
updateColor();
} }
@NonNull /**
public static Spanned getCategoryColorDot(int color) { * @return Integer color of #RRGGBB format.
return Html.fromHtml(getCategoryColorDotHTML(color)); */
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); return getCategoryColorDot(color);
} }
@NonNull public SpannableString getTitleWithColorDot(int categoryColor) {
public Spanned getTitleWithColorDot() { return getCategoryColorDotSpan(" " + title, categoryColor);
return Html.fromHtml(getCategoryColorDotHTML(color) + " " + title); }
public SpannableString getTitleWithColorDot() {
return getTitleWithColorDot(color);
} }
/** /**
@ -291,7 +330,6 @@ public enum SegmentCategory {
* @param videoLength length of the video * @param videoLength length of the video
* @return the skip button text * @return the skip button text
*/ */
@NonNull
StringRef getSkipButtonText(long segmentStartTime, long videoLength) { StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) { if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
return (this == SegmentCategory.HIGHLIGHT) return (this == SegmentCategory.HIGHLIGHT)
@ -300,7 +338,7 @@ public enum SegmentCategory {
} }
if (videoLength == 0) { 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; final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) { if (position < 0.25f) {
@ -316,10 +354,9 @@ public enum SegmentCategory {
* @param videoLength length of the video * @param videoLength length of the video
* @return 'skipped segment' toast message * @return 'skipped segment' toast message
*/ */
@NonNull
StringRef getSkippedToastText(long segmentStartTime, long videoLength) { StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
if (videoLength == 0) { 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; final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) { if (position < 0.25f) {

View File

@ -1,6 +1,7 @@
package app.revanced.extension.youtube.sponsorblock.objects; 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.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
@ -11,11 +12,10 @@ import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.GridLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView; import android.widget.TextView;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@ -24,27 +24,38 @@ import app.revanced.extension.shared.Utils;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class SegmentCategoryListPreference extends ListPreference { public class SegmentCategoryListPreference extends ListPreference {
private final SegmentCategory category; private final SegmentCategory category;
private EditText mEditText; private TextView colorDotView;
private int mClickedDialogEntryIndex; private EditText colorEditText;
private EditText opacityEditText;
/**
* #RRGGBB
*/
private int categoryColor;
/**
* [0, 1]
*/
private float categoryOpacity;
private int selectedDialogEntryIndex;
public SegmentCategoryListPreference(Context context, SegmentCategory category) { public SegmentCategoryListPreference(Context context, SegmentCategory category) {
super(context); super(context);
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
this.category = Objects.requireNonNull(category); this.category = Objects.requireNonNull(category);
// Edit: Using preferences to sync together multiple pieces // 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(category.behaviorSetting.key); setKey(category.behaviorSetting.key);
setDefaultValue(category.behaviorSetting.defaultValue); setDefaultValue(category.behaviorSetting.defaultValue);
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
setEntries(isHighlightCategory setEntries(isHighlightCategory
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce() ? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
: CategoryBehaviour.getBehaviorDescriptions()); : CategoryBehaviour.getBehaviorDescriptions());
setEntryValues(isHighlightCategory setEntryValues(isHighlightCategory
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce() ? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
: CategoryBehaviour.getBehaviorKeyValues()); : CategoryBehaviour.getBehaviorKeyValues());
setSummary(category.description.toString()); setSummary(category.description.toString());
updateTitle();
updateTitleFromCategory();
} }
@Override @Override
@ -52,26 +63,40 @@ public class SegmentCategoryListPreference extends ListPreference {
try { try {
Utils.setEditTextDialogTheme(builder); Utils.setEditTextDialogTheme(builder);
categoryColor = category.getColorNoOpacity();
categoryOpacity = category.getOpacity();
Context context = builder.getContext(); Context context = builder.getContext();
TableLayout table = new TableLayout(context); GridLayout gridLayout = new GridLayout(context);
table.setOrientation(LinearLayout.HORIZONTAL); gridLayout.setPadding(70, 0, 150, 0); // Padding for the entire layout.
table.setPadding(70, 0, 150, 0); gridLayout.setColumnCount(3);
gridLayout.setRowCount(2);
TableRow row = new TableRow(context);
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); TextView colorTextLabel = new TextView(context);
colorTextLabel.setText(str("revanced_sb_color_dot_label")); colorTextLabel.setText(str("revanced_sb_color_dot_label"));
row.addView(colorTextLabel); colorTextLabel.setLayoutParams(gridParams);
gridLayout.addView(colorTextLabel);
TextView colorDotView = new TextView(context); gridParams = new GridLayout.LayoutParams();
colorDotView.setText(category.getCategoryColorDot()); gridParams.rowSpec = GridLayout.spec(0); // First row.
colorDotView.setPadding(30, 0, 30, 0); gridParams.columnSpec = GridLayout.spec(1); // Second column.
row.addView(colorDotView); gridParams.setMargins(0, 0, 10, 0);
colorDotView = new TextView(context);
colorDotView.setLayoutParams(gridParams);
gridLayout.addView(colorDotView);
updateCategoryColorDot();
mEditText = new EditText(context); gridParams = new GridLayout.LayoutParams();
mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); gridParams.rowSpec = GridLayout.spec(0); // First row.
mEditText.setText(category.colorString()); gridParams.columnSpec = GridLayout.spec(2); // Third column.
mEditText.addTextChangedListener(new TextWatcher() { 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 @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
@ -81,29 +106,94 @@ public class SegmentCategoryListPreference extends ListPreference {
} }
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable edit) {
try { try {
String colorString = s.toString(); String colorString = edit.toString();
final int colorStringLength = colorString.length();
if (!colorString.startsWith("#")) { if (!colorString.startsWith("#")) {
s.insert(0, "#"); // recursively calls back into this method edit.insert(0, "#"); // Recursively calls back into this method.
return; return;
} }
if (colorString.length() > 7) {
s.delete(7, colorString.length()); final int maxColorStringLength = 7; // #RRGGBB
if (colorStringLength > maxColorStringLength) {
edit.delete(maxColorStringLength, colorStringLength);
return; return;
} }
final int color = Color.parseColor(colorString);
colorDotView.setText(SegmentCategory.getCategoryColorDot(color)); categoryColor = Color.parseColor(colorString);
updateCategoryColorDot();
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// ignore // Ignore.
} }
} }
}); });
mEditText.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f)); colorEditText.setLayoutParams(gridParams);
row.addView(mEditText); gridLayout.addView(colorEditText);
table.addView(row); gridParams = new GridLayout.LayoutParams();
builder.setView(table); 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.setTitle(category.title.toString());
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
@ -111,8 +201,8 @@ public class SegmentCategoryListPreference extends ListPreference {
}); });
builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> { builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> {
try { try {
category.resetColor(); category.resetColorAndOpacity();
updateTitle(); updateTitleFromCategory();
Utils.showToastShort(str("revanced_sb_color_reset")); Utils.showToastShort(str("revanced_sb_color_reset"));
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "setNeutralButton failure", ex); Logger.printException(() -> "setNeutralButton failure", ex);
@ -120,8 +210,9 @@ public class SegmentCategoryListPreference extends ListPreference {
}); });
builder.setNegativeButton(android.R.string.cancel, null); builder.setNegativeButton(android.R.string.cancel, null);
mClickedDialogEntryIndex = findIndexOfValue(getValue()); selectedDialogEntryIndex = findIndexOfValue(getValue());
builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which); builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
(dialog, which) -> selectedDialogEntryIndex = which);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onPrepareDialogBuilder failure", ex); Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
} }
@ -130,30 +221,51 @@ public class SegmentCategoryListPreference extends ListPreference {
@Override @Override
protected void onDialogClosed(boolean positiveResult) { protected void onDialogClosed(boolean positiveResult) {
try { try {
if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) { if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
String value = getEntryValues()[mClickedDialogEntryIndex].toString(); String value = getEntryValues()[selectedDialogEntryIndex].toString();
if (callChangeListener(value)) { if (callChangeListener(value)) {
setValue(value); setValue(value);
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value))); category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
SegmentCategory.updateEnabledCategories(); SegmentCategory.updateEnabledCategories();
} }
String colorString = mEditText.getText().toString();
try { try {
if (!colorString.equals(category.colorString())) { String colorString = colorEditText.getText().toString();
if (!colorString.equals(category.getColorString()) || categoryOpacity != category.getOpacity()) {
category.setColor(colorString); category.setColor(colorString);
category.setOpacity(categoryOpacity);
Utils.showToastShort(str("revanced_sb_color_changed")); Utils.showToastShort(str("revanced_sb_color_changed"));
} }
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Utils.showToastShort(str("revanced_sb_color_invalid")); Utils.showToastShort(str("revanced_sb_color_invalid"));
} }
updateTitle();
updateTitleFromCategory();
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onDialogClosed failure", ex); Logger.printException(() -> "onDialogClosed failure", ex);
} }
} }
private void updateTitle() { private void applyOpacityToCategoryColor() {
setTitle(category.getTitleWithColorDot()); categoryColor = applyOpacityToColor(categoryColor, categoryOpacity);
}
private void updateTitleFromCategory() {
categoryColor = category.getColorNoOpacity();
categoryOpacity = category.getOpacity();
applyOpacityToCategoryColor();
setTitle(category.getTitleWithColorDot(categoryColor));
}
private void updateCategoryColorDot() {
applyOpacityToCategoryColor();
colorDotView.setText(SegmentCategory.getCategoryColorDot(categoryColor));
}
private void updateOpacityText() {
opacityEditText.setText(String.format(Locale.US, "%.2f", categoryOpacity));
} }
} }

View File

@ -23,12 +23,15 @@ public class SponsorSegment implements Comparable<SponsorSegment> {
@NonNull @NonNull
public final StringRef title; public final StringRef title;
public final int apiVoteType; 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.title = title;
this.apiVoteType = apiVoteType; this.apiVoteType = apiVoteType;
this.shouldHighlight = shouldHighlight; this.highlightIfVipAndVideoIsLocked = highlightIfVipAndVideoIsLocked;
} }
} }

View File

@ -1068,6 +1068,8 @@ Already exists"</string>
<string name="revanced_sb_vote_downvote">Downvote</string> <string name="revanced_sb_vote_downvote">Downvote</string>
<string name="revanced_sb_vote_category">Change category</string> <string name="revanced_sb_vote_category">Change category</string>
<string name="revanced_sb_vote_no_segments">There are no segments to vote for</string> <string name="revanced_sb_vote_no_segments">There are no segments to vote for</string>
<!-- A segment start and end time, such as "02:10 to 03:40" -->
<string name="revanced_sb_vote_segment_time_to_from">%1$s to %2$s</string>
<string name="revanced_sb_new_segment_choose_category">Choose the segment category</string> <string name="revanced_sb_new_segment_choose_category">Choose the segment category</string>
<string name="revanced_sb_new_segment_disabled_category">Category is disabled in settings. Enable category to submit.</string> <string name="revanced_sb_new_segment_disabled_category">Category is disabled in settings. Enable category to submit.</string>
<string name="revanced_sb_new_segment_title">New SponsorBlock segment</string> <string name="revanced_sb_new_segment_title">New SponsorBlock segment</string>
@ -1115,6 +1117,7 @@ Ready to submit?"</string>
<string name="revanced_sb_stats_saved_hour_format">%1$s hours %2$s minutes</string> <string name="revanced_sb_stats_saved_hour_format">%1$s hours %2$s minutes</string>
<string name="revanced_sb_stats_saved_minute_format">%1$s minutes %2$s seconds</string> <string name="revanced_sb_stats_saved_minute_format">%1$s minutes %2$s seconds</string>
<string name="revanced_sb_stats_saved_second_format">%s seconds</string> <string name="revanced_sb_stats_saved_second_format">%s seconds</string>
<string name="revanced_sb_color_opacity_label">Opacity:</string>
<string name="revanced_sb_color_dot_label">Color:</string> <string name="revanced_sb_color_dot_label">Color:</string>
<string name="revanced_sb_color_changed">Color changed</string> <string name="revanced_sb_color_changed">Color changed</string>
<string name="revanced_sb_color_reset">Color reset</string> <string name="revanced_sb_color_reset">Color reset</string>