Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(twitch): integrations code for patches (#216)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
- Loading branch information
Showing
12 changed files
with
582 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
app/src/main/java/app/revanced/twitch/settings/ReturnType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package app.revanced.twitch.settings; | ||
|
||
public enum ReturnType { | ||
BOOLEAN, INTEGER, STRING, LONG, FLOAT | ||
} |
149 changes: 149 additions & 0 deletions
149
app/src/main/java/app/revanced/twitch/settings/SettingsEnum.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package app.revanced.twitch.settings; | ||
|
||
import android.content.Context; | ||
import android.content.SharedPreferences; | ||
|
||
import app.revanced.twitch.utils.LogHelper; | ||
import app.revanced.twitch.utils.ReVancedUtils; | ||
|
||
public enum SettingsEnum { | ||
/* Ads */ | ||
BLOCK_VIDEO_ADS("revanced_block_video_ads", true, ReturnType.BOOLEAN), | ||
BLOCK_AUDIO_ADS("revanced_block_audio_ads", true, ReturnType.BOOLEAN), | ||
|
||
/* Chat */ | ||
SHOW_DELETED_MESSAGES("revanced_show_deleted_messages", "cross-out", ReturnType.STRING), | ||
|
||
/* Misc */ | ||
DEBUG_MODE("revanced_debug_mode", false, ReturnType.BOOLEAN, true); | ||
|
||
public static final String REVANCED_PREFS = "revanced_prefs"; | ||
|
||
private final String path; | ||
private final Object defaultValue; | ||
private final ReturnType returnType; | ||
private final boolean rebootApp; | ||
|
||
private Object value = null; | ||
|
||
SettingsEnum(String path, Object defaultValue, ReturnType returnType) { | ||
this.path = path; | ||
this.defaultValue = defaultValue; | ||
this.returnType = returnType; | ||
this.rebootApp = false; | ||
} | ||
|
||
SettingsEnum(String path, Object defaultValue, ReturnType returnType, Boolean rebootApp) { | ||
this.path = path; | ||
this.defaultValue = defaultValue; | ||
this.returnType = returnType; | ||
this.rebootApp = rebootApp; | ||
} | ||
|
||
static { | ||
load(); | ||
} | ||
|
||
private static void load() { | ||
ReVancedUtils.ifContextAttached((context -> { | ||
try { | ||
SharedPreferences prefs = context.getSharedPreferences(REVANCED_PREFS, Context.MODE_PRIVATE); | ||
for (SettingsEnum setting : values()) { | ||
Object value = setting.getDefaultValue(); | ||
|
||
try { | ||
switch (setting.getReturnType()) { | ||
case BOOLEAN: | ||
value = prefs.getBoolean(setting.getPath(), (boolean)setting.getDefaultValue()); | ||
break; | ||
// Numbers are implicitly converted from strings | ||
case FLOAT: | ||
case LONG: | ||
case INTEGER: | ||
case STRING: | ||
value = prefs.getString(setting.getPath(), setting.getDefaultValue() + ""); | ||
break; | ||
default: | ||
LogHelper.error("Setting '%s' does not have a valid type", setting.name()); | ||
break; | ||
} | ||
} | ||
catch (ClassCastException ex) { | ||
LogHelper.printException("Failed to read value", ex); | ||
} | ||
|
||
setting.setValue(value); | ||
LogHelper.debug("Loaded setting '%s' with value %s", setting.name(), value); | ||
} | ||
} catch (Throwable th) { | ||
LogHelper.printException("Failed to load settings", th); | ||
} | ||
})); | ||
} | ||
|
||
public void setValue(Object newValue) { | ||
// Implicitly convert strings to numbers depending on the ResultType | ||
switch (returnType) { | ||
case FLOAT: | ||
value = Float.valueOf(newValue + ""); | ||
break; | ||
case LONG: | ||
value = Long.valueOf(newValue + ""); | ||
break; | ||
case INTEGER: | ||
value = Integer.valueOf(newValue + ""); | ||
break; | ||
default: | ||
value = newValue; | ||
break; | ||
} | ||
} | ||
|
||
public void saveValue(Object newValue) { | ||
ReVancedUtils.ifContextAttached((context) -> { | ||
SharedPreferences prefs = context.getSharedPreferences(REVANCED_PREFS, Context.MODE_PRIVATE); | ||
if (returnType == ReturnType.BOOLEAN) { | ||
prefs.edit().putBoolean(path, (Boolean)newValue).apply(); | ||
} else { | ||
prefs.edit().putString(path, newValue + "").apply(); | ||
} | ||
value = newValue; | ||
}); | ||
} | ||
|
||
public int getInt() { | ||
return (int) value; | ||
} | ||
|
||
public String getString() { | ||
return (String) value; | ||
} | ||
|
||
public boolean getBoolean() { | ||
return (Boolean) value; | ||
} | ||
|
||
public Long getLong() { | ||
return (Long) value; | ||
} | ||
|
||
public Float getFloat() { | ||
return (Float) value; | ||
} | ||
|
||
public Object getDefaultValue() { | ||
return defaultValue; | ||
} | ||
|
||
public String getPath() { | ||
return path; | ||
} | ||
|
||
public ReturnType getReturnType() { | ||
return returnType; | ||
} | ||
|
||
public boolean shouldRebootOnChange() { | ||
return rebootApp; | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
app/src/main/java/app/revanced/twitch/settingsmenu/ReVancedSettingsFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package app.revanced.twitch.settingsmenu; | ||
|
||
import android.annotation.SuppressLint; | ||
import android.app.Activity; | ||
import android.app.AlarmManager; | ||
import android.app.AlertDialog; | ||
import android.app.PendingIntent; | ||
import android.content.Context; | ||
import android.content.Intent; | ||
import android.content.SharedPreferences; | ||
import android.os.Bundle; | ||
import android.os.Process; | ||
import android.preference.EditTextPreference; | ||
import android.preference.ListPreference; | ||
import android.preference.Preference; | ||
import android.preference.PreferenceFragment; | ||
import android.preference.PreferenceManager; | ||
import android.preference.SwitchPreference; | ||
|
||
import androidx.annotation.Nullable; | ||
|
||
import app.revanced.twitch.settings.SettingsEnum; | ||
import app.revanced.twitch.utils.LogHelper; | ||
import app.revanced.twitch.utils.ReVancedUtils; | ||
|
||
import tv.twitch.android.app.core.LandingActivity; | ||
|
||
public class ReVancedSettingsFragment extends PreferenceFragment { | ||
|
||
private boolean registered = false; | ||
private boolean settingsInitialized = false; | ||
|
||
SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, key) -> { | ||
LogHelper.debug("Setting '%s' changed", key); | ||
syncPreference(key); | ||
}; | ||
|
||
/** | ||
* Sync preference | ||
* @param key Preference to load. If key is null, all preferences are updated | ||
*/ | ||
private void syncPreference(@Nullable String key) { | ||
for (SettingsEnum setting : SettingsEnum.values()) { | ||
if (!setting.getPath().equals(key) && key != null) | ||
continue; | ||
|
||
Preference pref = this.findPreference(setting.getPath()); | ||
LogHelper.debug("Syncing setting '%s' with UI", setting.getPath()); | ||
|
||
if (pref instanceof SwitchPreference) { | ||
setting.setValue(((SwitchPreference) pref).isChecked()); | ||
} | ||
else if (pref instanceof EditTextPreference) { | ||
setting.setValue(((EditTextPreference) pref).getText()); | ||
} | ||
else if (pref instanceof ListPreference) { | ||
ListPreference listPref = (ListPreference) pref; | ||
listPref.setSummary(listPref.getEntry()); | ||
setting.setValue(listPref.getValue()); | ||
} | ||
else { | ||
LogHelper.error("Setting '%s' cannot be handled!", pref); | ||
} | ||
|
||
if (ReVancedUtils.getContext() != null && key != null && settingsInitialized && setting.shouldRebootOnChange()) { | ||
rebootDialog(getActivity()); | ||
} | ||
|
||
// First onChange event is caused by initial state loading | ||
this.settingsInitialized = true; | ||
} | ||
} | ||
|
||
@SuppressLint("ResourceType") | ||
@Override | ||
public void onCreate(Bundle bundle) { | ||
super.onCreate(bundle); | ||
|
||
try { | ||
PreferenceManager mgr = getPreferenceManager(); | ||
mgr.setSharedPreferencesName(SettingsEnum.REVANCED_PREFS); | ||
mgr.getSharedPreferences().registerOnSharedPreferenceChangeListener(this.listener); | ||
|
||
addPreferencesFromResource( | ||
getResources().getIdentifier( | ||
SettingsEnum.REVANCED_PREFS, | ||
"xml", | ||
this.getContext().getPackageName() | ||
) | ||
); | ||
|
||
// Sync all preferences with UI | ||
syncPreference(null); | ||
|
||
this.registered = true; | ||
} catch (Throwable th) { | ||
LogHelper.printException("Error during onCreate()", th); | ||
} | ||
} | ||
|
||
@Override | ||
public void onDestroy() { | ||
if (this.registered) { | ||
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this.listener); | ||
this.registered = false; | ||
} | ||
super.onDestroy(); | ||
} | ||
|
||
@SuppressLint("MissingPermission") | ||
private void reboot(Activity activity) { | ||
int flags; | ||
flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE; | ||
((AlarmManager) activity.getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.ELAPSED_REALTIME, 1500L, PendingIntent.getActivity(activity, 0, new Intent(activity, LandingActivity.class), flags)); | ||
Process.killProcess(Process.myPid()); | ||
} | ||
|
||
private void rebootDialog(final Activity activity) { | ||
new AlertDialog.Builder(activity). | ||
setMessage(ReVancedUtils.getString("revanced_reboot_message")). | ||
setPositiveButton(ReVancedUtils.getString("revanced_reboot"), (dialog, i) -> reboot(activity)) | ||
.setNegativeButton(ReVancedUtils.getString("revanced_cancel"), null) | ||
.show(); | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
app/src/main/java/app/revanced/twitch/settingsmenu/SettingsHooks.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package app.revanced.twitch.settingsmenu; | ||
|
||
import static app.revanced.twitch.utils.ReVancedUtils.getIdentifier; | ||
import static app.revanced.twitch.utils.ReVancedUtils.getStringId; | ||
|
||
import android.content.Intent; | ||
import android.os.Bundle; | ||
|
||
import androidx.appcompat.app.ActionBar; | ||
import androidx.appcompat.app.AppCompatActivity; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import app.revanced.twitch.utils.ReVancedUtils; | ||
import app.revanced.twitch.utils.LogHelper; | ||
import tv.twitch.android.feature.settings.menu.SettingsMenuGroup; | ||
import tv.twitch.android.settings.SettingsActivity; | ||
|
||
public class SettingsHooks { | ||
private static final int REVANCED_SETTINGS_MENU_ITEM_ID = 0x7; | ||
private static final String EXTRA_REVANCED_SETTINGS = "app.revanced.twitch.settings"; | ||
|
||
/** | ||
* Launches SettingsActivity and show ReVanced settings | ||
*/ | ||
public static void startSettingsActivity() { | ||
LogHelper.debug("Launching ReVanced settings"); | ||
|
||
ReVancedUtils.ifContextAttached((c) -> { | ||
Intent intent = new Intent(c, SettingsActivity.class); | ||
Bundle bundle = new Bundle(); | ||
bundle.putBoolean(EXTRA_REVANCED_SETTINGS, true); | ||
intent.putExtras(bundle); | ||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
c.startActivity(intent); | ||
}); | ||
} | ||
|
||
/** | ||
* Helper for easy access in smali | ||
* @return Returns string resource id | ||
*/ | ||
public static int getReVancedSettingsString() { | ||
return getStringId("revanced_settings"); | ||
} | ||
|
||
/** | ||
* Intercepts settings menu group list creation in SettingsMenuPresenter$Event.MenuGroupsUpdated | ||
* @return Returns a modified list of menu groups | ||
*/ | ||
public static List<SettingsMenuGroup> handleSettingMenuCreation(List<SettingsMenuGroup> settingGroups, Object revancedEntry) { | ||
List<SettingsMenuGroup> groups = new ArrayList<>(settingGroups); | ||
|
||
if(groups.size() < 1) { | ||
// Create new menu group if none exist yet | ||
List<Object> items = new ArrayList<>(); | ||
items.add(revancedEntry); | ||
groups.add(new SettingsMenuGroup(items)); | ||
} | ||
else { | ||
// Add to last menu group | ||
int groupIdx = groups.size() - 1; | ||
List<Object> items = new ArrayList<>(groups.remove(groupIdx).getSettingsMenuItems()); | ||
items.add(revancedEntry); | ||
groups.add(new SettingsMenuGroup(items)); | ||
} | ||
|
||
LogHelper.debug("%d menu groups in list", settingGroups.size()); | ||
return groups; | ||
} | ||
|
||
/** | ||
* Intercepts settings menu group onclick events | ||
* @return Returns true if handled, otherwise false | ||
*/ | ||
@SuppressWarnings("rawtypes") | ||
public static boolean handleSettingMenuOnClick(Enum item) { | ||
LogHelper.debug("item %d clicked", item.ordinal()); | ||
if(item.ordinal() != REVANCED_SETTINGS_MENU_ITEM_ID) { | ||
return false; | ||
} | ||
|
||
startSettingsActivity(); | ||
return true; | ||
} | ||
|
||
/** | ||
* Intercepts fragment loading in SettingsActivity.onCreate | ||
* @return Returns true if the revanced settings have been requested by the user, otherwise false | ||
*/ | ||
public static boolean handleSettingsCreation(AppCompatActivity base) { | ||
if(!base.getIntent().getBooleanExtra(EXTRA_REVANCED_SETTINGS, false)) { | ||
LogHelper.debug("Revanced settings not requested"); | ||
return false; // User wants to enter another settings fragment | ||
} | ||
LogHelper.debug("ReVanced settings requested"); | ||
|
||
ReVancedSettingsFragment fragment = new ReVancedSettingsFragment(); | ||
ActionBar supportActionBar = base.getSupportActionBar(); | ||
if(supportActionBar != null) | ||
supportActionBar.setTitle(getStringId("revanced_settings")); | ||
|
||
base.getFragmentManager() | ||
.beginTransaction() | ||
.replace(getIdentifier("fragment_container", "id"), fragment) | ||
.commit(); | ||
return true; | ||
} | ||
} |
Oops, something went wrong.