mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-06-01 19:06:29 +02:00
5d14f53acd
Previously, the announcement content was hashed and compared with the locally saved hash. Sometimes announcements are fixed up, altering the hash, so instead, compare the ID.
168 lines
6.6 KiB
Java
168 lines
6.6 KiB
Java
package app.revanced.integrations.youtube.patches.announcements;
|
|
|
|
import android.app.Activity;
|
|
import android.os.Build;
|
|
import android.text.Html;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.widget.TextView;
|
|
import androidx.annotation.RequiresApi;
|
|
import app.revanced.integrations.shared.Logger;
|
|
import app.revanced.integrations.shared.Utils;
|
|
import app.revanced.integrations.youtube.patches.announcements.requests.AnnouncementsRoutes;
|
|
import app.revanced.integrations.youtube.requests.Requester;
|
|
import app.revanced.integrations.youtube.settings.Settings;
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.IOException;
|
|
import java.net.HttpURLConnection;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.MessageDigest;
|
|
import java.util.Locale;
|
|
import java.util.UUID;
|
|
|
|
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
|
import static app.revanced.integrations.shared.StringRef.str;
|
|
import static app.revanced.integrations.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENT;
|
|
|
|
@SuppressWarnings("unused")
|
|
public final class AnnouncementsPatch {
|
|
private final static String CONSUMER = getOrSetConsumer();
|
|
|
|
private AnnouncementsPatch() {
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
public static void showAnnouncement(final Activity context) {
|
|
if (!Settings.ANNOUNCEMENTS.get()) return;
|
|
|
|
// Check if there is internet connection
|
|
if (!Utils.isNetworkConnected()) return;
|
|
|
|
Utils.runOnBackgroundThread(() -> {
|
|
try {
|
|
HttpURLConnection connection = AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(
|
|
GET_LATEST_ANNOUNCEMENT, CONSUMER, Locale.getDefault().toLanguageTag());
|
|
|
|
Logger.printDebug(() -> "Get latest announcement route connection url: " + connection.getURL());
|
|
|
|
try {
|
|
// Do not show the announcement if the request failed.
|
|
if (connection.getResponseCode() != 200) {
|
|
if (Settings.ANNOUNCEMENT_LAST_ID.isSetToDefault())
|
|
return;
|
|
|
|
Settings.ANNOUNCEMENT_LAST_ID.resetToDefault();
|
|
Utils.showToastLong(str("revanced_announcements_connection_failed"));
|
|
|
|
return;
|
|
}
|
|
} catch (IOException ex) {
|
|
final var message = "Failed connecting to announcements provider";
|
|
|
|
Logger.printException(() -> message, ex);
|
|
return;
|
|
}
|
|
|
|
var jsonString = Requester.parseInputStreamAndClose(connection.getInputStream(), false);
|
|
|
|
|
|
// Parse the announcement. Fall-back to raw string if it fails.
|
|
int id = Settings.ANNOUNCEMENT_LAST_ID.defaultValue;
|
|
String title;
|
|
String message;
|
|
Level level = Level.INFO;
|
|
try {
|
|
final var announcement = new JSONObject(jsonString);
|
|
|
|
id = announcement.getInt("id");
|
|
title = announcement.getString("title");
|
|
message = announcement.getJSONObject("content").getString("message");
|
|
if (!announcement.isNull("level")) level = Level.fromInt(announcement.getInt("level"));
|
|
|
|
} catch (Throwable ex) {
|
|
Logger.printException(() -> "Failed to parse announcement. Fall-backing to raw string", ex);
|
|
|
|
title = "Announcement";
|
|
message = jsonString;
|
|
}
|
|
|
|
// TODO: Remove this migration code after a few months.
|
|
if (!Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.isSetToDefault()){
|
|
final byte[] hashBytes = MessageDigest
|
|
.getInstance("SHA-256")
|
|
.digest(jsonString.getBytes(StandardCharsets.UTF_8));
|
|
|
|
final var hash = java.util.Base64.getEncoder().encodeToString(hashBytes);
|
|
|
|
// Migrate to saving the id instead of the hash.
|
|
if (hash.equals(Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.get())) {
|
|
Settings.ANNOUNCEMENT_LAST_ID.save(id);
|
|
}
|
|
|
|
Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.resetToDefault();
|
|
}
|
|
|
|
// Do not show the announcement, if the last announcement id is the same as the current one.
|
|
if (Settings.ANNOUNCEMENT_LAST_ID.get() == id) return;
|
|
|
|
|
|
|
|
int finalId = id;
|
|
final var finalTitle = title;
|
|
final var finalMessage = Html.fromHtml(message, FROM_HTML_MODE_COMPACT);
|
|
final Level finalLevel = level;
|
|
|
|
Utils.runOnMainThread(() -> {
|
|
// Show the announcement.
|
|
var alertDialog = new android.app.AlertDialog.Builder(context)
|
|
.setTitle(finalTitle)
|
|
.setMessage(finalMessage)
|
|
.setIcon(finalLevel.icon)
|
|
.setPositiveButton("Ok", (dialog, which) -> {
|
|
Settings.ANNOUNCEMENT_LAST_ID.save(finalId);
|
|
dialog.dismiss();
|
|
}).setNegativeButton("Dismiss", (dialog, which) -> {
|
|
dialog.dismiss();
|
|
})
|
|
.setCancelable(false)
|
|
.show();
|
|
|
|
// Make links clickable.
|
|
((TextView)alertDialog.findViewById(android.R.id.message))
|
|
.setMovementMethod(LinkMovementMethod.getInstance());
|
|
});
|
|
} catch (Exception e) {
|
|
final var message = "Failed to get announcement";
|
|
|
|
Logger.printException(() -> message, e);
|
|
}
|
|
});
|
|
}
|
|
|
|
private static String getOrSetConsumer() {
|
|
final var consumer = Settings.ANNOUNCEMENT_CONSUMER.get();
|
|
if (!consumer.isEmpty()) return consumer;
|
|
|
|
final var uuid = UUID.randomUUID().toString();
|
|
Settings.ANNOUNCEMENT_CONSUMER.save(uuid);
|
|
return uuid;
|
|
}
|
|
|
|
// TODO: Use better icons.
|
|
private enum Level {
|
|
INFO(android.R.drawable.ic_dialog_info),
|
|
WARNING(android.R.drawable.ic_dialog_alert),
|
|
SEVERE(android.R.drawable.ic_dialog_alert);
|
|
|
|
public final int icon;
|
|
|
|
Level(int icon) {
|
|
this.icon = icon;
|
|
}
|
|
|
|
public static Level fromInt(int value) {
|
|
return values()[Math.min(value, values().length - 1)];
|
|
}
|
|
}
|
|
}
|