mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-11-08 05:07:02 +01:00
feat(YouTube): Add 'About' preference to settings menu (#608)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
f5720f71a6
commit
b8f260ebd3
@ -35,6 +35,7 @@ import java.util.concurrent.ThreadPoolExecutor;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import app.revanced.integrations.shared.settings.BooleanSetting;
|
import app.revanced.integrations.shared.settings.BooleanSetting;
|
||||||
|
import app.revanced.integrations.shared.settings.preference.ReVancedAboutPreference;
|
||||||
import kotlin.text.Regex;
|
import kotlin.text.Regex;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
@ -47,30 +48,44 @@ public class Utils {
|
|||||||
private Utils() {
|
private Utils() {
|
||||||
} // utility class
|
} // utility class
|
||||||
|
|
||||||
public static String getVersionName() {
|
/**
|
||||||
if (versionName != null) return versionName;
|
* Injection point.
|
||||||
|
*
|
||||||
|
* @return The manifest 'Version' entry of the patches.jar used during patching.
|
||||||
|
*/
|
||||||
|
public static String getPatchesReleaseVersion() {
|
||||||
|
return ""; // Value is replaced during patching.
|
||||||
|
}
|
||||||
|
|
||||||
PackageInfo packageInfo;
|
/**
|
||||||
try {
|
* @return The version name of the app, such as "YouTube".
|
||||||
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
|
*/
|
||||||
|
public static String getAppVersionName() {
|
||||||
|
if (versionName == null) {
|
||||||
|
try {
|
||||||
|
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
|
||||||
|
|
||||||
PackageManager packageManager = context.getPackageManager();
|
PackageManager packageManager = context.getPackageManager();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
PackageInfo packageInfo;
|
||||||
packageInfo = packageManager.getPackageInfo(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
packageName,
|
packageInfo = packageManager.getPackageInfo(
|
||||||
PackageManager.PackageInfoFlags.of(0)
|
packageName,
|
||||||
);
|
PackageManager.PackageInfoFlags.of(0)
|
||||||
else
|
);
|
||||||
packageInfo = packageManager.getPackageInfo(
|
} else {
|
||||||
packageName,
|
packageInfo = packageManager.getPackageInfo(
|
||||||
0
|
packageName,
|
||||||
);
|
0
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
);
|
||||||
Logger.printException(() -> "Failed to get package info", e);
|
}
|
||||||
return null;
|
versionName = packageInfo.versionName;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Failed to get package info", ex);
|
||||||
|
versionName = "Unknown";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return versionName = packageInfo.versionName;
|
return versionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,6 +200,7 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int getResourceColor(@NonNull String resourceIdentifierName) throws Resources.NotFoundException {
|
public static int getResourceColor(@NonNull String resourceIdentifierName) throws Resources.NotFoundException {
|
||||||
|
//noinspection deprecation
|
||||||
return getContext().getResources().getColor(getResourceIdentifier(resourceIdentifierName, "color"));
|
return getContext().getResources().getColor(getResourceIdentifier(resourceIdentifierName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +339,7 @@ public class Utils {
|
|||||||
try {
|
try {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> runnable.getClass() + ": " + ex.getMessage(), ex);
|
Logger.printException(() -> runnable.getClass().getSimpleName() + ": " + ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
new Handler(Looper.getMainLooper()).postDelayed(loggingRunnable, delayMillis);
|
new Handler(Looper.getMainLooper()).postDelayed(loggingRunnable, delayMillis);
|
||||||
@ -445,11 +461,8 @@ public class Utils {
|
|||||||
this.keySuffix = keySuffix;
|
this.keySuffix = keySuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Defaults to {@link #UNSORTED} if key is null or has no sort suffix.
|
|
||||||
*/
|
|
||||||
@NonNull
|
@NonNull
|
||||||
static Sort fromKey(@Nullable String key) {
|
static Sort fromKey(@Nullable String key, @NonNull Sort defaultSort) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
for (Sort sort : values()) {
|
for (Sort sort : values()) {
|
||||||
if (key.endsWith(sort.keySuffix)) {
|
if (key.endsWith(sort.keySuffix)) {
|
||||||
@ -457,7 +470,7 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UNSORTED;
|
return defaultSort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,19 +492,26 @@ public class Utils {
|
|||||||
* If a preference has no key or no {@link Sort} suffix,
|
* If a preference has no key or no {@link Sort} suffix,
|
||||||
* then the preferences are left unsorted.
|
* then the preferences are left unsorted.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
|
public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
|
||||||
Sort sort = Sort.fromKey(group.getKey());
|
Sort groupSort = Sort.fromKey(group.getKey(), Sort.UNSORTED);
|
||||||
SortedMap<String, Preference> preferences = new TreeMap<>();
|
SortedMap<String, Preference> preferences = new TreeMap<>();
|
||||||
|
|
||||||
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
|
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
|
||||||
Preference preference = group.getPreference(i);
|
Preference preference = group.getPreference(i);
|
||||||
|
|
||||||
|
final Sort preferenceSort;
|
||||||
if (preference instanceof PreferenceGroup) {
|
if (preference instanceof PreferenceGroup) {
|
||||||
sortPreferenceGroups((PreferenceGroup) preference);
|
sortPreferenceGroups((PreferenceGroup) preference);
|
||||||
|
preferenceSort = groupSort; // Sort value for groups is for it's content, not itself.
|
||||||
|
} else {
|
||||||
|
// Allow individual preferences to set a key sorting.
|
||||||
|
// Used to force a preference to the top or bottom of a group.
|
||||||
|
preferenceSort = Sort.fromKey(preference.getKey(), groupSort);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String sortValue;
|
final String sortValue;
|
||||||
switch (sort) {
|
switch (preferenceSort) {
|
||||||
case BY_TITLE:
|
case BY_TITLE:
|
||||||
sortValue = removePunctuationConvertToLowercase(preference.getTitle());
|
sortValue = removePunctuationConvertToLowercase(preference.getTitle());
|
||||||
break;
|
break;
|
||||||
@ -511,8 +531,9 @@ public class Utils {
|
|||||||
for (Preference pref : preferences.values()) {
|
for (Preference pref : preferences.values()) {
|
||||||
int order = index++;
|
int order = index++;
|
||||||
|
|
||||||
// If the preference is a PreferenceScreen or is an intent preference, move to the top.
|
// Move any screens, intents, and the one off About preference to the top.
|
||||||
if (pref instanceof PreferenceScreen || pref.getIntent() != null) {
|
if (pref instanceof PreferenceScreen || pref instanceof ReVancedAboutPreference
|
||||||
|
|| pref.getIntent() != null) {
|
||||||
// Arbitrary high number.
|
// Arbitrary high number.
|
||||||
order -= 1000;
|
order -= 1000;
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,7 @@ import app.revanced.integrations.shared.settings.Setting;
|
|||||||
|
|
||||||
import static app.revanced.integrations.shared.StringRef.str;
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
|
||||||
/**
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
*
|
|
||||||
*
|
|
||||||
* @noinspection deprecation, DataFlowIssue , unused */
|
|
||||||
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||||
/**
|
/**
|
||||||
* Indicates that if a preference changes,
|
* Indicates that if a preference changes,
|
||||||
|
@ -15,7 +15,7 @@ import app.revanced.integrations.shared.Utils;
|
|||||||
|
|
||||||
import static app.revanced.integrations.shared.StringRef.str;
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
|
||||||
/** @noinspection deprecation, unused */
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {
|
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {
|
||||||
|
|
||||||
private String existingSettings;
|
private String existingSettings;
|
||||||
|
@ -0,0 +1,312 @@
|
|||||||
|
package app.revanced.integrations.shared.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
import static app.revanced.integrations.youtube.requests.Route.Method.GET;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.revanced.integrations.shared.Logger;
|
||||||
|
import app.revanced.integrations.shared.Utils;
|
||||||
|
import app.revanced.integrations.youtube.requests.Requester;
|
||||||
|
import app.revanced.integrations.youtube.requests.Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a dialog showing the links from {@link SocialLinksRoutes}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
|
public class ReVancedAboutPreference extends Preference {
|
||||||
|
|
||||||
|
private static String useNonBreakingHyphens(String text) {
|
||||||
|
// Replace any dashes with non breaking dashes, so the English text 'pre-release'
|
||||||
|
// and the dev release number does not break and cover two lines.
|
||||||
|
return text.replace("-", "‑"); // #8209 = non breaking hyphen.
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getColorHexString(int color) {
|
||||||
|
return String.format("#%06X", (0x00FFFFFF & color));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isDarkModeEnabled() {
|
||||||
|
Configuration config = getContext().getResources().getConfiguration();
|
||||||
|
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses can override this and provide a themed color.
|
||||||
|
*/
|
||||||
|
protected int getLightColor() {
|
||||||
|
return Color.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses can override this and provide a themed color.
|
||||||
|
*/
|
||||||
|
protected int getDarkColor() {
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createDialogHtml(ReVancedSocialLink[] socialLinks) {
|
||||||
|
final boolean isNetworkConnected = Utils.isNetworkConnected();
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("<html>");
|
||||||
|
builder.append("<body style=\"text-align: center; padding: 10px;\">");
|
||||||
|
|
||||||
|
final boolean isDarkMode = isDarkModeEnabled();
|
||||||
|
String backgroundColorHex = getColorHexString(isDarkMode ? getDarkColor() : getLightColor());
|
||||||
|
String foregroundColorHex = getColorHexString(isDarkMode ? getLightColor() : getDarkColor());
|
||||||
|
// Apply light/dark mode colors.
|
||||||
|
builder.append(String.format(
|
||||||
|
"<style> body { background-color: %s; color: %s; } a { color: %s; } </style>",
|
||||||
|
backgroundColorHex, foregroundColorHex, foregroundColorHex));
|
||||||
|
|
||||||
|
if (isNetworkConnected) {
|
||||||
|
builder.append("<img style=\"width: 100px; height: 100px;\" "
|
||||||
|
// Hide the image if it does not load.
|
||||||
|
+ "onerror=\"this.style.display='none';\" "
|
||||||
|
+ "src=\"https://revanced.app/favicon.ico\" />");
|
||||||
|
}
|
||||||
|
|
||||||
|
String patchesVersion = Utils.getPatchesReleaseVersion();
|
||||||
|
|
||||||
|
// Add the title.
|
||||||
|
builder.append("<h1>")
|
||||||
|
.append("ReVanced")
|
||||||
|
.append("</h1>");
|
||||||
|
|
||||||
|
builder.append("<p>")
|
||||||
|
// Replace hyphens with non breaking dashes so the version number does not break lines.
|
||||||
|
.append(useNonBreakingHyphens(str("revanced_settings_about_links_body", patchesVersion)))
|
||||||
|
.append("</p>");
|
||||||
|
|
||||||
|
// Add a disclaimer if using a dev release.
|
||||||
|
if (patchesVersion.contains("dev")) {
|
||||||
|
builder.append("<h3>")
|
||||||
|
// English text 'Pre-release' can break lines.
|
||||||
|
.append(useNonBreakingHyphens(str("revanced_settings_about_links_dev_header")))
|
||||||
|
.append("</h3>");
|
||||||
|
|
||||||
|
builder.append("<p>")
|
||||||
|
.append(str("revanced_settings_about_links_dev_body"))
|
||||||
|
.append("</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("<h2 style=\"margin-top: 30px;\">")
|
||||||
|
.append(str("revanced_settings_about_links_header"))
|
||||||
|
.append("</h2>");
|
||||||
|
|
||||||
|
builder.append("<div>");
|
||||||
|
for (ReVancedSocialLink social : socialLinks) {
|
||||||
|
builder.append("<div style=\"margin-bottom: 20px;\">");
|
||||||
|
builder.append(String.format("<a href=\"%s\">%s</a>", social.url, social.name));
|
||||||
|
builder.append("</div>");
|
||||||
|
}
|
||||||
|
builder.append("</div>");
|
||||||
|
|
||||||
|
builder.append("</body></html>");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
setOnPreferenceClickListener(pref -> {
|
||||||
|
// Show a progress spinner if the social links are not fetched yet.
|
||||||
|
if (!SocialLinksRoutes.hasFetchedLinks() && Utils.isNetworkConnected()) {
|
||||||
|
ProgressDialog progress = new ProgressDialog(getContext());
|
||||||
|
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||||
|
progress.show();
|
||||||
|
Utils.runOnBackgroundThread(() -> fetchLinksAndShowDialog(progress));
|
||||||
|
} else {
|
||||||
|
// No network call required and can run now.
|
||||||
|
fetchLinksAndShowDialog(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchLinksAndShowDialog(@Nullable ProgressDialog progress) {
|
||||||
|
ReVancedSocialLink[] socialLinks = SocialLinksRoutes.fetchSocialLinks();
|
||||||
|
String htmlDialog = createDialogHtml(socialLinks);
|
||||||
|
|
||||||
|
Utils.runOnMainThreadNowOrLater(() -> {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.dismiss();
|
||||||
|
}
|
||||||
|
new WebViewDialog(getContext(), htmlDialog).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReVancedAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
public ReVancedAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
public ReVancedAboutPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
public ReVancedAboutPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays html content as a dialog. Any links a user taps on are opened in an external browser.
|
||||||
|
*/
|
||||||
|
class WebViewDialog extends Dialog {
|
||||||
|
|
||||||
|
private final String htmlContent;
|
||||||
|
|
||||||
|
public WebViewDialog(@NonNull Context context, @NonNull String htmlContent) {
|
||||||
|
super(context);
|
||||||
|
this.htmlContent = htmlContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JS required to hide any broken images. No remote javascript is ever loaded.
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
|
||||||
|
WebView webView = new WebView(getContext());
|
||||||
|
webView.getSettings().setJavaScriptEnabled(true);
|
||||||
|
webView.setWebViewClient(new OpenLinksExternallyWebClient());
|
||||||
|
webView.loadDataWithBaseURL(null, htmlContent, "text/html", "utf-8", null);
|
||||||
|
|
||||||
|
setContentView(webView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OpenLinksExternallyWebClient extends WebViewClient {
|
||||||
|
@Override
|
||||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||||
|
getContext().startActivity(intent);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Open link failure", ex);
|
||||||
|
}
|
||||||
|
// Dismiss the about dialog using a delay,
|
||||||
|
// otherwise without a delay the UI looks hectic with the dialog dismissing
|
||||||
|
// to show the settings while simultaneously a web browser is opening.
|
||||||
|
Utils.runOnMainThreadDelayed(WebViewDialog.this::dismiss, 500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReVancedSocialLink {
|
||||||
|
final boolean preferred;
|
||||||
|
final String name;
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
ReVancedSocialLink(JSONObject json) throws JSONException {
|
||||||
|
this(json.getBoolean("preferred"),
|
||||||
|
json.getString("name"),
|
||||||
|
json.getString("url")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReVancedSocialLink(boolean preferred, String name, String url) {
|
||||||
|
this.preferred = preferred;
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ReVancedSocialLink{" +
|
||||||
|
"preferred=" + preferred +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", url='" + url + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocialLinksRoutes {
|
||||||
|
/**
|
||||||
|
* Links to use if fetch links api call fails.
|
||||||
|
*/
|
||||||
|
private static final ReVancedSocialLink[] NO_CONNECTION_STATIC_LINKS = {
|
||||||
|
new ReVancedSocialLink(true, "ReVanced.app", "https://revanced.app")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String SOCIAL_LINKS_PROVIDER = "https://api.revanced.app/v2";
|
||||||
|
private static final Route.CompiledRoute GET_SOCIAL = new Route(GET, "/socials").compile();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static volatile ReVancedSocialLink[] fetchedLinks;
|
||||||
|
|
||||||
|
static boolean hasFetchedLinks() {
|
||||||
|
return fetchedLinks != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ReVancedSocialLink[] fetchSocialLinks() {
|
||||||
|
try {
|
||||||
|
if (hasFetchedLinks()) return fetchedLinks;
|
||||||
|
|
||||||
|
// Check if there is no internet connection.
|
||||||
|
if (!Utils.isNetworkConnected()) return NO_CONNECTION_STATIC_LINKS;
|
||||||
|
|
||||||
|
HttpURLConnection connection = Requester.getConnectionFromCompiledRoute(SOCIAL_LINKS_PROVIDER, GET_SOCIAL);
|
||||||
|
connection.setConnectTimeout(5000);
|
||||||
|
connection.setReadTimeout(5000);
|
||||||
|
Logger.printDebug(() -> "Fetching social links from: " + connection.getURL());
|
||||||
|
|
||||||
|
// Do not show an exception toast if the server is down
|
||||||
|
final int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode != 200) {
|
||||||
|
Logger.printDebug(() -> "Failed to get social links. Response code: " + responseCode);
|
||||||
|
return NO_CONNECTION_STATIC_LINKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject json = Requester.parseJSONObjectAndDisconnect(connection);
|
||||||
|
JSONArray socials = json.getJSONArray("socials");
|
||||||
|
|
||||||
|
List<ReVancedSocialLink> links = new ArrayList<>();
|
||||||
|
for (int i = 0, length = socials.length(); i < length; i++) {
|
||||||
|
ReVancedSocialLink link = new ReVancedSocialLink(socials.getJSONObject(i));
|
||||||
|
links.add(link);
|
||||||
|
}
|
||||||
|
Logger.printDebug(() -> "links: " + links);
|
||||||
|
|
||||||
|
return fetchedLinks = links.toArray(new ReVancedSocialLink[0]);
|
||||||
|
|
||||||
|
} catch (SocketTimeoutException ex) {
|
||||||
|
Logger.printInfo(() -> "Could not fetch social links", ex); // No toast.
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Logger.printException(() -> "Could not parse about information", ex);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Failed to get about information", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_CONNECTION_STATIC_LINKS;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static app.revanced.integrations.shared.StringRef.str;
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class ResettableEditTextPreference extends EditTextPreference {
|
public class ResettableEditTextPreference extends EditTextPreference {
|
||||||
|
|
||||||
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
@ -33,7 +33,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
super.onPrepareDialogBuilder(builder);
|
super.onPrepareDialogBuilder(builder);
|
||||||
Setting setting = Setting.getSettingFromPath(getKey());
|
Setting<?> setting = Setting.getSettingFromPath(getKey());
|
||||||
if (setting != null) {
|
if (setting != null) {
|
||||||
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
|||||||
}
|
}
|
||||||
button.setOnClickListener(v -> {
|
button.setOnClickListener(v -> {
|
||||||
try {
|
try {
|
||||||
Setting setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey()));
|
Setting<?> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey()));
|
||||||
String defaultStringValue = setting.defaultValue.toString();
|
String defaultStringValue = setting.defaultValue.toString();
|
||||||
EditText editText = getEditText();
|
EditText editText = getEditText();
|
||||||
editText.setText(defaultStringValue);
|
editText.setText(defaultStringValue);
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
package app.revanced.integrations.youtube;
|
package app.revanced.integrations.youtube;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.integrations.shared.Logger;
|
import app.revanced.integrations.shared.Logger;
|
||||||
import app.revanced.integrations.shared.Utils;
|
import app.revanced.integrations.shared.Utils;
|
||||||
|
|
||||||
public class ThemeHelper {
|
public class ThemeHelper {
|
||||||
|
@Nullable
|
||||||
|
private static Integer darkThemeColor, lightThemeColor;
|
||||||
private static int themeValue;
|
private static int themeValue;
|
||||||
|
|
||||||
public static void setTheme(Object value) {
|
/**
|
||||||
final int newOrdinalValue = ((Enum) value).ordinal();
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setTheme(Enum<?> value) {
|
||||||
|
final int newOrdinalValue = value.ordinal();
|
||||||
if (themeValue != newOrdinalValue) {
|
if (themeValue != newOrdinalValue) {
|
||||||
themeValue = newOrdinalValue;
|
themeValue = newOrdinalValue;
|
||||||
Logger.printDebug(() -> "Theme value: " + newOrdinalValue);
|
Logger.printDebug(() -> "Theme value: " + newOrdinalValue);
|
||||||
@ -26,4 +35,48 @@ public class ThemeHelper {
|
|||||||
activity.setTheme(Utils.getResourceIdentifier(theme, "style"));
|
activity.setTheme(Utils.getResourceIdentifier(theme, "style"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
private static String darkThemeResourceName() {
|
||||||
|
// Value is changed by Theme patch, if included.
|
||||||
|
return "@android:color/black";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The dark theme color as specified by the Theme patch (if included),
|
||||||
|
* or the Android color of black.
|
||||||
|
*/
|
||||||
|
public static int getDarkThemeColor() {
|
||||||
|
if (darkThemeColor == null) {
|
||||||
|
darkThemeColor = getColorInt(darkThemeResourceName());
|
||||||
|
}
|
||||||
|
return darkThemeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
private static String lightThemeResourceName() {
|
||||||
|
// Value is changed by Theme patch, if included.
|
||||||
|
return "@android:color/white";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The light theme color as specified by the Theme patch (if included),
|
||||||
|
* or the Android color of white.
|
||||||
|
*/
|
||||||
|
public static int getLightThemeColor() {
|
||||||
|
if (lightThemeColor == null) {
|
||||||
|
lightThemeColor = getColorInt(lightThemeResourceName());
|
||||||
|
}
|
||||||
|
return lightThemeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getColorInt(String colorString) {
|
||||||
|
if (colorString.startsWith("#")) {
|
||||||
|
return Color.parseColor(colorString);
|
||||||
|
}
|
||||||
|
return Utils.getResourceColor(colorString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ public final class AnnouncementsPatch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonString = Requester.parseInputStreamAndClose(connection.getInputStream(), false);
|
var jsonString = Requester.parseStringAndDisconnect(connection);
|
||||||
|
|
||||||
|
|
||||||
// Parse the announcement. Fall-back to raw string if it fails.
|
// Parse the announcement. Fall-back to raw string if it fails.
|
||||||
|
@ -37,7 +37,7 @@ final class PlayerRoutes {
|
|||||||
|
|
||||||
JSONObject client = new JSONObject();
|
JSONObject client = new JSONObject();
|
||||||
client.put("clientName", "ANDROID");
|
client.put("clientName", "ANDROID");
|
||||||
client.put("clientVersion", Utils.getVersionName());
|
client.put("clientVersion", Utils.getAppVersionName());
|
||||||
client.put("androidSdkVersion", 34);
|
client.put("androidSdkVersion", 34);
|
||||||
|
|
||||||
context.put("client", client);
|
context.put("client", client);
|
||||||
@ -85,7 +85,7 @@ final class PlayerRoutes {
|
|||||||
|
|
||||||
connection.setRequestProperty(
|
connection.setRequestProperty(
|
||||||
"User-Agent", "com.google.android.youtube/" +
|
"User-Agent", "com.google.android.youtube/" +
|
||||||
Utils.getVersionName() +
|
Utils.getAppVersionName() +
|
||||||
" (Linux; U; Android 12; GB) gzip"
|
" (Linux; U; Android 12; GB) gzip"
|
||||||
);
|
);
|
||||||
connection.setRequestProperty("X-Goog-Api-Format-Version", "2");
|
connection.setRequestProperty("X-Goog-Api-Format-Version", "2");
|
||||||
|
@ -24,7 +24,10 @@ public class Requester {
|
|||||||
String url = apiUrl + route.getCompiledRoute();
|
String url = apiUrl + route.getCompiledRoute();
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
connection.setRequestMethod(route.getMethod().name());
|
connection.setRequestMethod(route.getMethod().name());
|
||||||
connection.setRequestProperty("User-Agent", System.getProperty("http.agent") + "; ReVanced/" + Utils.getVersionName());
|
String agentString = System.getProperty("http.agent")
|
||||||
|
+ "; ReVanced/" + Utils.getAppVersionName()
|
||||||
|
+ " (" + Utils.getPatchesReleaseVersion() + ")";
|
||||||
|
connection.setRequestProperty("User-Agent", agentString);
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@ -32,72 +35,79 @@ public class Requester {
|
|||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
||||||
*/
|
*/
|
||||||
public static String parseJson(HttpURLConnection connection) throws IOException {
|
private static String parseInputStreamAndClose(InputStream inputStream) throws IOException {
|
||||||
return parseInputStreamAndClose(connection.getInputStream(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
|
||||||
*
|
|
||||||
* <b>Should only be used if other requests to the server are unlikely in the near future</b>
|
|
||||||
*
|
|
||||||
* @see #parseJson(HttpURLConnection)
|
|
||||||
*/
|
|
||||||
public static String parseJsonAndDisconnect(HttpURLConnection connection) throws IOException {
|
|
||||||
String result = parseJson(connection);
|
|
||||||
connection.disconnect();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
|
||||||
*
|
|
||||||
* @param stripNewLineCharacters if newline (\n) characters should be stripped from the InputStream
|
|
||||||
*/
|
|
||||||
public static String parseInputStreamAndClose(InputStream inputStream, boolean stripNewLineCharacters) throws IOException {
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||||
StringBuilder jsonBuilder = new StringBuilder();
|
StringBuilder jsonBuilder = new StringBuilder();
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
jsonBuilder.append(line);
|
jsonBuilder.append(line);
|
||||||
if (!stripNewLineCharacters)
|
jsonBuilder.append("\n");
|
||||||
jsonBuilder.append("\n");
|
|
||||||
}
|
}
|
||||||
return jsonBuilder.toString();
|
return jsonBuilder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
* Parse the {@link HttpURLConnection} response as a String.
|
||||||
|
* This does not close the url connection. If further requests to this host are unlikely
|
||||||
|
* in the near future, then instead use {@link #parseStringAndDisconnect(HttpURLConnection)}.
|
||||||
*/
|
*/
|
||||||
public static String parseErrorJson(HttpURLConnection connection) throws IOException {
|
public static String parseString(HttpURLConnection connection) throws IOException {
|
||||||
return parseInputStreamAndClose(connection.getErrorStream(), false);
|
return parseInputStreamAndClose(connection.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
* Parse the {@link HttpURLConnection} response as a String, and disconnect.
|
||||||
*
|
*
|
||||||
* <b>Should only be used if other requests to the server are unlikely in the near future</b>
|
* <b>Should only be used if other requests to the server in the near future are unlikely</b>
|
||||||
*
|
*
|
||||||
* @see #parseErrorJson(HttpURLConnection)
|
* @see #parseString(HttpURLConnection)
|
||||||
*/
|
*/
|
||||||
public static String parseErrorJsonAndDisconnect(HttpURLConnection connection) throws IOException {
|
public static String parseStringAndDisconnect(HttpURLConnection connection) throws IOException {
|
||||||
String result = parseErrorJson(connection);
|
String result = parseString(connection);
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
* Parse the {@link HttpURLConnection} error stream as a String.
|
||||||
|
* If the server sent no error response data, this returns an empty string.
|
||||||
|
*/
|
||||||
|
public static String parseErrorString(HttpURLConnection connection) throws IOException {
|
||||||
|
InputStream errorStream = connection.getErrorStream();
|
||||||
|
if (errorStream == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return parseInputStreamAndClose(errorStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the {@link HttpURLConnection} error stream as a String, and disconnect.
|
||||||
|
* If the server sent no error response data, this returns an empty string.
|
||||||
|
*
|
||||||
|
* Should only be used if other requests to the server are unlikely in the near future.
|
||||||
|
*
|
||||||
|
* @see #parseErrorString(HttpURLConnection)
|
||||||
|
*/
|
||||||
|
public static String parseErrorStringAndDisconnect(HttpURLConnection connection) throws IOException {
|
||||||
|
String result = parseErrorString(connection);
|
||||||
|
connection.disconnect();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the {@link HttpURLConnection} response into a JSONObject.
|
||||||
|
* This does not close the url connection. If further requests to this host are unlikely
|
||||||
|
* in the near future, then instead use {@link #parseJSONObjectAndDisconnect(HttpURLConnection)}.
|
||||||
*/
|
*/
|
||||||
public static JSONObject parseJSONObject(HttpURLConnection connection) throws JSONException, IOException {
|
public static JSONObject parseJSONObject(HttpURLConnection connection) throws JSONException, IOException {
|
||||||
return new JSONObject(parseJson(connection));
|
return new JSONObject(parseString(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
||||||
*
|
*
|
||||||
* <b>Should only be used if other requests to the server are unlikely in the near future</b>
|
* <b>Should only be used if other requests to the server in the near future are unlikely</b>
|
||||||
*
|
*
|
||||||
* @see #parseJSONObject(HttpURLConnection)
|
* @see #parseJSONObject(HttpURLConnection)
|
||||||
*/
|
*/
|
||||||
@ -109,15 +119,17 @@ public class Requester {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
* Parse the {@link HttpURLConnection}, and closes the underlying InputStream.
|
||||||
|
* This does not close the url connection. If further requests to this host are unlikely
|
||||||
|
* in the near future, then instead use {@link #parseJSONArrayAndDisconnect(HttpURLConnection)}.
|
||||||
*/
|
*/
|
||||||
public static JSONArray parseJSONArray(HttpURLConnection connection) throws JSONException, IOException {
|
public static JSONArray parseJSONArray(HttpURLConnection connection) throws JSONException, IOException {
|
||||||
return new JSONArray(parseJson(connection));
|
return new JSONArray(parseString(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
* Parse the {@link HttpURLConnection}, close the underlying InputStream, and disconnect.
|
||||||
*
|
*
|
||||||
* <b>Should only be used if other requests to the server are unlikely in the near future</b>
|
* <b>Should only be used if other requests to the server in the near future are unlikely</b>
|
||||||
*
|
*
|
||||||
* @see #parseJSONArray(HttpURLConnection)
|
* @see #parseJSONArray(HttpURLConnection)
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.integrations.youtube.returnyoutubedislike.requests;
|
package app.revanced.integrations.youtube.returnyoutubedislike.requests;
|
||||||
|
|
||||||
import static app.revanced.integrations.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeRoutes.getRYDConnectionFromRoute;
|
|
||||||
import static app.revanced.integrations.shared.StringRef.str;
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
import static app.revanced.integrations.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeRoutes.getRYDConnectionFromRoute;
|
||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
@ -22,11 +22,11 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import app.revanced.integrations.shared.Logger;
|
||||||
|
import app.revanced.integrations.shared.Utils;
|
||||||
import app.revanced.integrations.youtube.requests.Requester;
|
import app.revanced.integrations.youtube.requests.Requester;
|
||||||
import app.revanced.integrations.youtube.returnyoutubedislike.ReturnYouTubeDislike;
|
import app.revanced.integrations.youtube.returnyoutubedislike.ReturnYouTubeDislike;
|
||||||
import app.revanced.integrations.youtube.settings.Settings;
|
import app.revanced.integrations.youtube.settings.Settings;
|
||||||
import app.revanced.integrations.shared.Logger;
|
|
||||||
import app.revanced.integrations.shared.Utils;
|
|
||||||
|
|
||||||
public class ReturnYouTubeDislikeApi {
|
public class ReturnYouTubeDislikeApi {
|
||||||
/**
|
/**
|
||||||
@ -383,7 +383,7 @@ public class ReturnYouTubeDislikeApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Something went wrong, might as well disconnect.
|
// Something went wrong, might as well disconnect.
|
||||||
String response = Requester.parseJsonAndDisconnect(connection);
|
String response = Requester.parseStringAndDisconnect(connection);
|
||||||
Logger.printInfo(() -> "Failed to confirm registration for user: " + userId
|
Logger.printInfo(() -> "Failed to confirm registration for user: " + userId
|
||||||
+ " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "''");
|
+ " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "''");
|
||||||
handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode),
|
handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode),
|
||||||
@ -505,7 +505,7 @@ public class ReturnYouTubeDislikeApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Something went wrong, might as well disconnect.
|
// Something went wrong, might as well disconnect.
|
||||||
String response = Requester.parseJsonAndDisconnect(connection);
|
String response = Requester.parseStringAndDisconnect(connection);
|
||||||
Logger.printInfo(() -> "Failed to confirm vote for video: " + videoId
|
Logger.printInfo(() -> "Failed to confirm vote for video: " + videoId
|
||||||
+ " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "'");
|
+ " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "'");
|
||||||
handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode),
|
handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode),
|
||||||
|
@ -9,7 +9,7 @@ import android.util.AttributeSet;
|
|||||||
/**
|
/**
|
||||||
* Allows tapping the DeArrow about preference to open the DeArrow website.
|
* Allows tapping the DeArrow about preference to open the DeArrow website.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class AlternativeThumbnailsAboutDeArrowPreference extends Preference {
|
public class AlternativeThumbnailsAboutDeArrowPreference extends Preference {
|
||||||
{
|
{
|
||||||
setOnPreferenceClickListener(pref -> {
|
setOnPreferenceClickListener(pref -> {
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package app.revanced.integrations.youtube.settings.preference;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import app.revanced.integrations.shared.settings.preference.ReVancedAboutPreference;
|
||||||
|
import app.revanced.integrations.youtube.ThemeHelper;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ReVancedYouTubeAboutPreference extends ReVancedAboutPreference {
|
||||||
|
|
||||||
|
public int getLightColor() {
|
||||||
|
return ThemeHelper.getLightThemeColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDarkColor() {
|
||||||
|
return ThemeHelper.getDarkThemeColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
public ReVancedYouTubeAboutPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
public ReVancedYouTubeAboutPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
}
|
@ -159,13 +159,13 @@ public class SBRequester {
|
|||||||
messageToToast = str("revanced_sb_submit_failed_duplicate");
|
messageToToast = str("revanced_sb_submit_failed_duplicate");
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorJsonAndDisconnect(connection));
|
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
|
||||||
break;
|
break;
|
||||||
case 429:
|
case 429:
|
||||||
messageToToast = str("revanced_sb_submit_failed_rate_limit");
|
messageToToast = str("revanced_sb_submit_failed_rate_limit");
|
||||||
break;
|
break;
|
||||||
case 400:
|
case 400:
|
||||||
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorJsonAndDisconnect(connection));
|
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
|
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
|
||||||
@ -223,7 +223,7 @@ public class SBRequester {
|
|||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
Utils.showToastLong(
|
Utils.showToastLong(
|
||||||
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorJsonAndDisconnect(connection)));
|
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Utils.showToastLong(
|
Utils.showToastLong(
|
||||||
|
Loading…
Reference in New Issue
Block a user