Skip to content
This repository was archived by the owner on Oct 26, 2024. It is now read-only.

feat(YouTube - GmsCore): Require ignoring battery optimizations #599

Merged
merged 13 commits into from
Mar 30, 2024
Merged
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package app.revanced.integrations.shared;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import androidx.annotation.RequiresApi;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;

import static app.revanced.integrations.shared.StringRef.str;

Expand All @@ -18,15 +21,13 @@
*/
public class GmsCoreSupport {
private static final String GMS_CORE_PACKAGE_NAME
= getGmsCoreVendor() + ".android.gms";
= getGmsCoreVendorGroupId() + ".android.gms";
private static final Uri GMS_CORE_PROVIDER
= Uri.parse("content://" + getGmsCoreVendorGroupId() + ".android.gsf.gservices/prefix");
private static final String DONT_KILL_MY_APP_LINK
= "https://dontkillmyapp.com";
private static final Uri GMS_CORE_PROVIDER
= Uri.parse("content://" + getGmsCoreVendor() + ".android.gsf.gservices/prefix");

private static void open(String queryOrLink, String message) {
Utils.showToastLong(message);

private static void open(String queryOrLink) {
Intent intent;
try {
// Check if queryOrLink is a valid URL.
Expand All @@ -40,48 +41,93 @@ private static void open(String queryOrLink, String message) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Utils.getContext().startActivity(intent);

// Gracefully exit, otherwise without Gms the app crashes and Android can nag the user.
// Gracefully exit, otherwise the broken app will continue to run.
System.exit(0);
}

private static void showToastOrDialog(Context context, String toastMessageKey, String dialogMessageKey, String link) {
if (!(context instanceof Activity)) {
// Context is for the application and cannot show a dialog using it.
Utils.showToastLong(str(toastMessageKey));
open(link);
return;
}

// Use a delay to allow the activity to finish initializing.
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
Utils.runOnMainThreadDelayed(() -> {
new AlertDialog.Builder(context)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageKey))
.setPositiveButton(str("gms_core_dialog_ok_button_text"), (dialog, id) -> {
open(link);
})
// Manually allow using the back button to dismiss the dialog with the back button,
// if troubleshooting and somehow the GmsCore verification checks always fail.
.setCancelable(true)
.show();
}, 100);
}

/**
* Injection point.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public static void checkAvailability() {
var context = Objects.requireNonNull(Utils.getContext());

public static void checkGmsCore(Context context) {
try {
context.getPackageManager().getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException exception) {
Logger.printInfo(() -> "GmsCore was not found", exception);
open(getGmsCoreDownload(), str("gms_core_not_installed_warning"));
return;
}
// Verify GmsCore is installed.
try {
PackageManager manager = context.getPackageManager();
manager.getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException exception) {
Logger.printDebug(() -> "GmsCore was not found");
// Cannot show a dialog and must show a toast,
// because on some installations the app crashes before the dialog can display.
Utils.showToastLong(str("gms_core_toast_not_installed_message"));
open(getGmsCoreDownload());
return;
}

try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
if (client != null) return;
// Check if GmsCore is whitelisted from battery optimizations.
var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (!powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME)) {
Logger.printDebug(() -> "GmsCore is not whitelisted from battery optimizations");
showToastOrDialog(context,
"gms_core_toast_not_whitelisted_message",
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
DONT_KILL_MY_APP_LINK);
return;
}

Logger.printInfo(() -> "GmsCore is not running in the background");
open(DONT_KILL_MY_APP_LINK, str("gms_core_not_running_warning"));
// Check if GmsCore is running in the background.
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
if (client == null) {
Logger.printDebug(() -> "GmsCore is not running in the background");
showToastOrDialog(context,
"gms_core_toast_not_whitelisted_message",
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
DONT_KILL_MY_APP_LINK);
}
}
} catch (Exception ex) {
Logger.printException(() -> "Could not check GmsCore background task", ex);
Logger.printException(() -> "checkGmsCore failure", ex);
}
}

private static String getGmsCoreDownload() {
final var vendor = getGmsCoreVendor();
final var vendorGroupId = getGmsCoreVendorGroupId();
//noinspection SwitchStatementWithTooFewBranches
switch (vendor) {
switch (vendorGroupId) {
case "app.revanced":
return "https://github.com/revanced/gmscore/releases/latest";
default:
return vendor + ".android.gms";
return vendorGroupId + ".android.gms";
}
}

// Modified by a patch. Do not touch.
private static String getGmsCoreVendor() {
private static String getGmsCoreVendorGroupId() {
return "app.revanced";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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;
Expand Down Expand Up @@ -115,10 +116,10 @@ public static void showAnnouncement(final Activity context) {
.setTitle(finalTitle)
.setMessage(finalMessage)
.setIcon(finalLevel.icon)
.setPositiveButton("Ok", (dialog, which) -> {
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Settings.ANNOUNCEMENT_LAST_ID.save(finalId);
dialog.dismiss();
}).setNegativeButton("Dismiss", (dialog, which) -> {
}).setNegativeButton(str("revanced_announcements_dialog_dismiss"), (dialog, which) -> {
dialog.dismiss();
})
.setCancelable(false)
Expand Down
Loading