feat(TikTok): Add ReVanced settings about screen (#4009)

This commit is contained in:
LisoUseInAIKyrios 2024-11-27 17:30:55 +04:00 committed by GitHub
parent c65f642074
commit 12ea26b10d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 130 additions and 34 deletions

View File

@ -1,6 +1,5 @@
package app.revanced.extension.shared.settings.preference; package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.sf;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.youtube.requests.Route.Method.GET; import static app.revanced.extension.youtube.requests.Route.Method.GET;
@ -13,6 +12,8 @@ import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.Preference; import android.preference.Preference;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Window; import android.view.Window;
@ -37,7 +38,7 @@ import app.revanced.extension.youtube.requests.Requester;
import app.revanced.extension.youtube.requests.Route; import app.revanced.extension.youtube.requests.Route;
/** /**
* Opens a dialog showing the links from {@link SocialLinksRoutes}. * Opens a dialog showing official links.
*/ */
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
public class ReVancedAboutPreference extends Preference { public class ReVancedAboutPreference extends Preference {
@ -72,7 +73,16 @@ public class ReVancedAboutPreference extends Preference {
return Color.BLACK; return Color.BLACK;
} }
private String createDialogHtml(WebLink[] socialLinks) { /**
* Apps that do not support bundling resources must override this.
*
* @return A localized string to display for the key.
*/
protected String getString(String key, Object ... args) {
return str(key, args);
}
private String createDialogHtml(WebLink[] aboutLinks) {
final boolean isNetworkConnected = Utils.isNetworkConnected(); final boolean isNetworkConnected = Utils.isNetworkConnected();
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -91,7 +101,7 @@ public class ReVancedAboutPreference extends Preference {
builder.append("<img style=\"width: 100px; height: 100px;\" " builder.append("<img style=\"width: 100px; height: 100px;\" "
// Hide the image if it does not load. // Hide the image if it does not load.
+ "onerror=\"this.style.display='none';\" " + "onerror=\"this.style.display='none';\" "
+ "src=\"https://revanced.app/favicon.ico\" />"); + "src=\"").append(AboutLinksRoutes.aboutLogoUrl).append("\" />");
} }
String patchesVersion = Utils.getPatchesReleaseVersion(); String patchesVersion = Utils.getPatchesReleaseVersion();
@ -103,29 +113,29 @@ public class ReVancedAboutPreference extends Preference {
builder.append("<p>") builder.append("<p>")
// Replace hyphens with non breaking dashes so the version number does not break lines. // Replace hyphens with non breaking dashes so the version number does not break lines.
.append(useNonBreakingHyphens(str("revanced_settings_about_links_body", patchesVersion))) .append(useNonBreakingHyphens(getString("revanced_settings_about_links_body", patchesVersion)))
.append("</p>"); .append("</p>");
// Add a disclaimer if using a dev release. // Add a disclaimer if using a dev release.
if (patchesVersion.contains("dev")) { if (patchesVersion.contains("dev")) {
builder.append("<h3>") builder.append("<h3>")
// English text 'Pre-release' can break lines. // English text 'Pre-release' can break lines.
.append(useNonBreakingHyphens(str("revanced_settings_about_links_dev_header"))) .append(useNonBreakingHyphens(getString("revanced_settings_about_links_dev_header")))
.append("</h3>"); .append("</h3>");
builder.append("<p>") builder.append("<p>")
.append(str("revanced_settings_about_links_dev_body")) .append(getString("revanced_settings_about_links_dev_body"))
.append("</p>"); .append("</p>");
} }
builder.append("<h2 style=\"margin-top: 30px;\">") builder.append("<h2 style=\"margin-top: 30px;\">")
.append(str("revanced_settings_about_links_header")) .append(getString("revanced_settings_about_links_header"))
.append("</h2>"); .append("</h2>");
builder.append("<div>"); builder.append("<div>");
for (WebLink social : socialLinks) { for (WebLink link : aboutLinks) {
builder.append("<div style=\"margin-bottom: 20px;\">"); builder.append("<div style=\"margin-bottom: 20px;\">");
builder.append(String.format("<a href=\"%s\">%s</a>", social.url, social.name)); builder.append(String.format("<a href=\"%s\">%s</a>", link.url, link.name));
builder.append("</div>"); builder.append("</div>");
} }
builder.append("</div>"); builder.append("</div>");
@ -137,25 +147,44 @@ public class ReVancedAboutPreference extends Preference {
{ {
setOnPreferenceClickListener(pref -> { setOnPreferenceClickListener(pref -> {
// Show a progress spinner if the social links are not fetched yet. // Show a progress spinner if the social links are not fetched yet.
if (!SocialLinksRoutes.hasFetchedLinks() && Utils.isNetworkConnected()) { if (!AboutLinksRoutes.hasFetchedLinks() && Utils.isNetworkConnected()) {
// Show a progress spinner, but only if the api fetch takes more than a half a second.
final long delayToShowProgressSpinner = 500;
ProgressDialog progress = new ProgressDialog(getContext()); ProgressDialog progress = new ProgressDialog(getContext());
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER); progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progress.show();
Utils.runOnBackgroundThread(() -> fetchLinksAndShowDialog(progress)); Handler handler = new Handler(Looper.getMainLooper());
Runnable showDialogRunnable = progress::show;
handler.postDelayed(showDialogRunnable, delayToShowProgressSpinner);
Utils.runOnBackgroundThread(() ->
fetchLinksAndShowDialog(handler, showDialogRunnable, progress));
} else { } else {
// No network call required and can run now. // No network call required and can run now.
fetchLinksAndShowDialog(null); fetchLinksAndShowDialog(null, null, null);
} }
return false; return false;
}); });
} }
private void fetchLinksAndShowDialog(@Nullable ProgressDialog progress) { private void fetchLinksAndShowDialog(@Nullable Handler handler,
WebLink[] socialLinks = SocialLinksRoutes.fetchSocialLinks(); Runnable showDialogRunnable,
String htmlDialog = createDialogHtml(socialLinks); @Nullable ProgressDialog progress) {
WebLink[] links = AboutLinksRoutes.fetchAboutLinks();
String htmlDialog = createDialogHtml(links);
// Enable to randomly force a delay to debug the spinner logic.
final boolean debugSpinnerDelayLogic = false;
//noinspection ConstantConditions
if (debugSpinnerDelayLogic && handler != null && Math.random() < 0.5f) {
Utils.doNothingForDuration((long) (Math.random() * 4000));
}
Utils.runOnMainThreadNowOrLater(() -> { Utils.runOnMainThreadNowOrLater(() -> {
if (handler != null) {
handler.removeCallbacks(showDialogRunnable);
}
if (progress != null) { if (progress != null) {
progress.dismiss(); progress.dismiss();
} }
@ -224,7 +253,7 @@ class WebViewDialog extends Dialog {
class WebLink { class WebLink {
final boolean preferred; final boolean preferred;
final String name; String name;
final String url; final String url;
WebLink(JSONObject json) throws JSONException { WebLink(JSONObject json) throws JSONException {
@ -243,7 +272,7 @@ class WebLink {
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
return "ReVancedSocialLink{" + return "WebLink{" +
"preferred=" + preferred + "preferred=" + preferred +
", name='" + name + '\'' + ", name='" + name + '\'' +
", url='" + url + '\'' + ", url='" + url + '\'' +
@ -251,25 +280,21 @@ class WebLink {
} }
} }
class SocialLinksRoutes { class AboutLinksRoutes {
/** /**
* Simple link to the website donate page, * Backup icon url if the API call fails.
* rather than fetching and parsing the donation links using the API.
*/ */
public static final WebLink DONATE_LINK = new WebLink(true, public static volatile String aboutLogoUrl = "https://revanced.app/favicon.ico";
sf("revanced_settings_about_links_donate").toString(),
"https://revanced.app/donate");
/** /**
* Links to use if fetch links api call fails. * Links to use if fetch links api call fails.
*/ */
private static final WebLink[] NO_CONNECTION_STATIC_LINKS = { private static final WebLink[] NO_CONNECTION_STATIC_LINKS = {
new WebLink(true, "ReVanced.app", "https://revanced.app"), new WebLink(true, "ReVanced.app", "https://revanced.app")
DONATE_LINK,
}; };
private static final String SOCIAL_LINKS_PROVIDER = "https://api.revanced.app/v2"; private static final String SOCIAL_LINKS_PROVIDER = "https://api.revanced.app/v4";
private static final Route.CompiledRoute GET_SOCIAL = new Route(GET, "/socials").compile(); private static final Route.CompiledRoute GET_SOCIAL = new Route(GET, "/about").compile();
@Nullable @Nullable
private static volatile WebLink[] fetchedLinks; private static volatile WebLink[] fetchedLinks;
@ -278,7 +303,7 @@ class SocialLinksRoutes {
return fetchedLinks != null; return fetchedLinks != null;
} }
static WebLink[] fetchSocialLinks() { static WebLink[] fetchAboutLinks() {
try { try {
if (hasFetchedLinks()) return fetchedLinks; if (hasFetchedLinks()) return fetchedLinks;
@ -298,11 +323,22 @@ class SocialLinksRoutes {
} }
JSONObject json = Requester.parseJSONObjectAndDisconnect(connection); JSONObject json = Requester.parseJSONObjectAndDisconnect(connection);
JSONArray socials = json.getJSONArray("socials"); aboutLogoUrl = json.getJSONObject("branding").getString("logo");
List<WebLink> links = new ArrayList<>(); List<WebLink> links = new ArrayList<>();
links.add(DONATE_LINK); // Show donate link first. JSONArray donations = json.getJSONObject("donations").getJSONArray("links");
for (int i = 0, length = donations.length(); i < length; i++) {
WebLink link = new WebLink(donations.getJSONObject(i));
if (link.preferred) {
// This could be localized, but TikTok does not support localized resources.
// All link names returned by the api are also non localized.
link.name = "Donate";
links.add(link);
}
}
JSONArray socials = json.getJSONArray("socials");
for (int i = 0, length = socials.length(); i < length; i++) { for (int i = 0, length = socials.length(); i < length; i++) {
WebLink link = new WebLink(socials.getJSONObject(i)); WebLink link = new WebLink(socials.getJSONObject(i));
links.add(link); links.add(link);

View File

@ -0,0 +1,56 @@
package app.revanced.extension.tiktok.settings.preference;
import android.content.Context;
import android.util.AttributeSet;
import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
@SuppressWarnings("unused")
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
/**
* Because resources cannot be added to TikTok,
* these strings are copied from the shared strings.xml file.
*
* Changes here must also be made in strings.xml
*/
private final Map<String, String> aboutStrings = Map.of(
"revanced_settings_about_links_body", "You are using ReVanced Patches version <i>%s</i>",
"revanced_settings_about_links_dev_header", "Note",
"revanced_settings_about_links_dev_body", "This version is a pre-release and you may experience unexpected issues",
"revanced_settings_about_links_header", "Official links"
);
{
//noinspection deprecation
setTitle("About");
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ReVancedTikTokAboutPreference(Context context) {
super(context);
}
@Override
protected String getString(String key, Object ... args) {
String format = aboutStrings.get(key);
if (format == null) {
Logger.printException(() -> "Unknown key: " + key);
return "";
}
return String.format(format, args);
}
}

View File

@ -4,13 +4,14 @@ import android.content.Context;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.tiktok.settings.preference.ReVancedTikTokAboutPreference;
import app.revanced.extension.tiktok.settings.preference.TogglePreference; import app.revanced.extension.tiktok.settings.preference.TogglePreference;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory { public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory {
public ExtensionPreferenceCategory(Context context, PreferenceScreen screen) { public ExtensionPreferenceCategory(Context context, PreferenceScreen screen) {
super(context, screen); super(context, screen);
setTitle("Extension"); setTitle("Miscellaneous");
} }
@Override @Override
@ -20,6 +21,8 @@ public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory {
@Override @Override
public void addPreferences(Context context) { public void addPreferences(Context context) {
addPreference(new ReVancedTikTokAboutPreference(context));
addPreference(new TogglePreference(context, addPreference(new TogglePreference(context,
"Enable debug log", "Enable debug log",
"Show extension debug log.", "Show extension debug log.",

View File

@ -60,7 +60,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_settings_about_links_dev_header">Note</string> <string name="revanced_settings_about_links_dev_header">Note</string>
<string name="revanced_settings_about_links_dev_body">This version is a pre-release and you may experience unexpected issues</string> <string name="revanced_settings_about_links_dev_body">This version is a pre-release and you may experience unexpected issues</string>
<string name="revanced_settings_about_links_header">Official links</string> <string name="revanced_settings_about_links_header">Official links</string>
<string name="revanced_settings_about_links_donate">Donate</string> <!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
and changes made here must also be made there. -->
</patch> </patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch"> <patch id="misc.gms.gmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. --> <!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->