Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(YouTube Music): Add Settings patch #568

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package app.revanced.integrations.music.settings;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.preference.PreferenceFragment;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import app.revanced.integrations.music.settings.preference.ReVancedPreferenceFragment;
import app.revanced.integrations.shared.Logger;

import java.util.Objects;

import static app.revanced.integrations.shared.Utils.getChildView;
import static app.revanced.integrations.shared.Utils.getResourceIdentifier;

/**
* Hooks FullStackTraceActivityHook.
* <p>
* This class is responsible for injecting our own fragment by replacing the FullStackTraceActivityHook.
*/
@SuppressWarnings("unused")
public class FullStackTraceActivityHook {

/**
* Injection point.
* <p>
* Hooks FullStackTraceActivityHook#onCreate in order to inject our own fragment.
*/
public static void initialize(Activity fullStackTraceActivityHook) {
try {
// ThemeHelper.setActivityTheme(fullStackTraceActivityHook);
fullStackTraceActivityHook.setContentView(
getResourceIdentifier("revanced_settings_with_toolbar", "layout"));
setBackButton(fullStackTraceActivityHook);

PreferenceFragment fragment;
String toolbarTitleResourceName;
String dataString = fullStackTraceActivityHook.getIntent().getDataString();
switch (dataString) {
case "revanced_settings_intent":
toolbarTitleResourceName = "revanced_settings_title";
fragment = new ReVancedPreferenceFragment();
break;
default:
Logger.printException(() -> "Unknown setting: " + dataString);
return;
}

setToolbarTitle(fullStackTraceActivityHook, toolbarTitleResourceName);
fullStackTraceActivityHook.getFragmentManager()
.beginTransaction()
.replace(getResourceIdentifier("revanced_settings_fragments", "id"), fragment)
.commit();
} catch (Exception ex) {
Logger.printException(() -> "onCreate failure", ex);
}
}

private static void setToolbarTitle(Activity activity, String toolbarTitleResourceName) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
TextView toolbarTextView = Objects.requireNonNull(getChildView(toolbar, view -> view instanceof TextView));
toolbarTextView.setText(getResourceIdentifier(toolbarTitleResourceName, "string"));
}

@SuppressLint("UseCompatLoadingForDrawables")
private static void setBackButton(Activity activity) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
ImageButton imageButton = Objects.requireNonNull(getChildView(toolbar, view -> view instanceof ImageButton));
final int backButtonResource = getResourceIdentifier(false // ThemeHelper.isDarkTheme()
? "yt_outline_arrow_left_white_24"
: "yt_outline_arrow_left_black_24",
"drawable");
imageButton.setImageDrawable(activity.getResources().getDrawable(backButtonResource));
imageButton.setOnClickListener(view -> activity.onBackPressed());
}

private static int getToolbarResourceId() {
final int toolbarResourceId = getResourceIdentifier("revanced_toolbar", "id");
if (toolbarResourceId == 0) {
throw new IllegalStateException("Could not find back button resource");
}
return toolbarResourceId;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.revanced.integrations.music.settings;

import app.revanced.integrations.shared.settings.BaseSettings;

public class Settings extends BaseSettings {
// TODO Add settings
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.revanced.integrations.music.settings.preference;

import app.revanced.integrations.music.settings.Settings;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.settings.preference.AbstractPreferenceFragment;

/**
* Preference fragment for ReVanced settings.
*/
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
@Override
protected void initialize() {
super.initialize();

// Do anything that forces this apps Settings bundle to load.
if (Settings.DEBUG.get()) {
Logger.printDebug(() -> "Debug logging enabled"); // Any statement that references the app settings.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import app.revanced.integrations.shared.StringRef;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings;

import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;
Expand Down Expand Up @@ -62,6 +62,21 @@ public static Availability parentsAny(@NonNull BooleanSetting... parents) {
};
}

/**
* Callback for importing/exporting settings.
*/
public interface ImportExportCallback {
/**
* Called after all settings have been imported.
*/
void settingsImported(@Nullable Context context);

/**
* Called after all settings have been exported.
*/
void settingsExported(@Nullable Context context);
}

/**
* All settings that were instantiated.
* When a new setting is created, it is automatically added to this list.
Expand All @@ -78,6 +93,15 @@ public static Availability parentsAny(@NonNull BooleanSetting... parents) {
*/
public static final SharedPrefCategory preferences = new SharedPrefCategory("revanced_prefs");

private static final List<ImportExportCallback> importExportCallbacks = new ArrayList<>();

/**
* Adds a callback for {@link #importFromJSON(Context, String)} and {@link #exportToJson(Context)}.
*/
public static void addImportExportCallback(@NonNull ImportExportCallback callback) {
importExportCallbacks.add(Objects.requireNonNull(callback));
}

@Nullable
public static Setting<?> getSettingFromPath(@NonNull String str) {
return PATH_TO_SETTINGS.get(str);
Expand Down Expand Up @@ -357,7 +381,10 @@ public static String exportToJson(@Nullable Context alertDialogContext) {
setting.writeToJSON(json, importExportKey);
}
}
SponsorBlockSettings.showExportWarningIfNeeded(alertDialogContext);

for (ImportExportCallback callback : importExportCallbacks) {
callback.settingsExported(alertDialogContext);
}

if (json.length() == 0) {
return "";
Expand All @@ -377,7 +404,7 @@ public static String exportToJson(@Nullable Context alertDialogContext) {
/**
* @return if any settings that require a reboot were changed.
*/
public static boolean importFromJSON(@NonNull String settingsJsonString) {
public static boolean importFromJSON(@Nullable Context context, @NonNull String settingsJsonString) {
try {
if (!settingsJsonString.matches("[\\s\\S]*\\{")) {
settingsJsonString = '{' + settingsJsonString + '}'; // Restore outer JSON braces
Expand All @@ -403,12 +430,9 @@ public static boolean importFromJSON(@NonNull String settingsJsonString) {
}
}

// SB Enum categories are saved using StringSettings.
// Which means they need to reload again if changed by other code (such as here).
// This call could be removed by creating a custom Setting class that manages the
// "String <-> Enum" logic or by adding an event hook of when settings are imported.
// But for now this is simple and works.
SponsorBlockSettings.updateFromImportedSettings();
for (ImportExportCallback callback : importExportCallbacks) {
callback.settingsImported(context);
}

Utils.showToastLong(numberOfSettingsImported == 0
? str("revanced_settings_import_reset")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private void importSettings(String replacementSettings) {
return;
}
AbstractPreferenceFragment.settingImportInProgress = true;
final boolean rebootNeeded = Setting.importFromJSON(replacementSettings);
final boolean rebootNeeded = Setting.importFromJSON(getContext(), replacementSettings);
if (rebootNeeded) {
AbstractPreferenceFragment.showRestartDialog(getContext());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static void startSettingsActivity() {
* @return Returns string resource id
*/
public static int getReVancedSettingsString() {
return app.revanced.integrations.twitch.Utils.getStringId("revanced_settings");
return app.revanced.integrations.twitch.Utils.getStringId("revanced_settings_title");
}

/**
Expand Down Expand Up @@ -101,7 +101,7 @@ public static boolean handleSettingsCreation(androidx.appcompat.app.AppCompatAct
ReVancedPreferenceFragment fragment = new ReVancedPreferenceFragment();
ActionBar supportActionBar = base.getSupportActionBar();
if (supportActionBar != null)
supportActionBar.setTitle(app.revanced.integrations.twitch.Utils.getStringId("revanced_settings"));
supportActionBar.setTitle(app.revanced.integrations.twitch.Utils.getStringId("revanced_settings_title"));

base.getFragmentManager()
.beginTransaction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
import java.util.HashSet;
import java.util.Set;

import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.settings.BaseSettings;
import app.revanced.integrations.shared.settings.BooleanSetting;
import app.revanced.integrations.shared.settings.FloatSetting;
import app.revanced.integrations.shared.settings.IntegerSetting;
import app.revanced.integrations.shared.settings.LongSetting;
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.shared.settings.BaseSettings;
import app.revanced.integrations.shared.settings.StringSetting;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings;

public class Settings extends BaseSettings {
Expand Down Expand Up @@ -339,12 +338,18 @@ public class Settings extends BaseSettings {
}
}


// Do _not_ delete this SB private user id migration property until sometime in 2024.
// This is the only setting that cannot be reconfigured if lost,
// and more time should be given for users who rarely upgrade.
migrateOldSettingToNew(DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, SB_PRIVATE_USER_ID);

// endregion


// region SB import/export callbacks

Setting.addImportExportCallback(SponsorBlockSettings.SB_IMPORT_EXPORT_CALLBACK);

// endregion
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.revanced.integrations.youtube.sponsorblock;

import static app.revanced.integrations.shared.StringRef.str;
import static app.revanced.integrations.shared.settings.Setting.ImportExportCallback;

import android.app.AlertDialog;
import android.content.Context;
Expand Down Expand Up @@ -159,9 +160,18 @@ public static String exportDesktopSettings() {
}

/**
* Export the categories using flatten json (no embedded dictionaries or arrays).
* Updates internal data based on {@link Setting} values.
*/
private static void updateFromImportedSettings() {
// SB Enum categories are saved using StringSettings.
// Which means they need to reload again if changed by other code.
SegmentCategory.loadAllCategoriesFromSettings();
}

/**
* Shows a warning, if a private SB user id exists.
*/
public static void showExportWarningIfNeeded(@Nullable Context dialogContext) {
private static void showExportWarningIfNeeded(@Nullable Context dialogContext) {
Utils.verifyOnMainThread();
initialize();

Expand All @@ -178,6 +188,17 @@ public static void showExportWarningIfNeeded(@Nullable Context dialogContext) {
}
}

public static final ImportExportCallback SB_IMPORT_EXPORT_CALLBACK = new ImportExportCallback() {
@Override
public void settingsImported(@Nullable Context context) {
updateFromImportedSettings();
}
@Override
public void settingsExported(@Nullable Context context) {
showExportWarningIfNeeded(context);
}
};

public static boolean isValidSBUserId(@NonNull String userId) {
return !userId.isEmpty() && userId.length() >= SB_PRIVATE_USER_ID_MINIMUM_LENGTH;
}
Expand Down Expand Up @@ -234,11 +255,4 @@ public static void initialize() {

SegmentCategory.updateEnabledCategories();
}

/**
* Updates internal data based on {@link Setting} values.
*/
public static void updateFromImportedSettings() {
SegmentCategory.loadAllCategoriesFromSettings();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public static SegmentCategory byCategoryKey(@NonNull String key) {
}

/**
* 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();
Expand Down