package app.revanced.integrations.shared; import static app.revanced.integrations.shared.StringRef.str; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.SearchManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.PowerManager; import android.provider.Settings; import androidx.annotation.RequiresApi; import java.net.MalformedURLException; import java.net.URL; /** * @noinspection unused */ public class GmsCoreSupport { private static final String GMS_CORE_PACKAGE_NAME = 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 void open(String queryOrLink) { Intent intent; try { // Check if queryOrLink is a valid URL. new URL(queryOrLink); intent = new Intent(Intent.ACTION_VIEW, Uri.parse(queryOrLink)); } catch (MalformedURLException e) { intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.putExtra(SearchManager.QUERY, queryOrLink); } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Utils.getContext().startActivity(intent); // Gracefully exit, otherwise the broken app will continue to run. System.exit(0); } private static void showBatteryOptimizationDialog(Activity context, String dialogMessageRef, String positiveButtonStringRef, DialogInterface.OnClickListener onPositiveClickListener) { // 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(dialogMessageRef)) .setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener) .setCancelable(false) .show(); }, 100); } /** * Injection point. */ @RequiresApi(api = Build.VERSION_CODES.N) public static void checkGmsCore(Activity context) { try { // Verify GmsCore is installed. try { PackageManager manager = context.getPackageManager(); manager.getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES); } catch (PackageManager.NameNotFoundException exception) { Logger.printInfo(() -> "GmsCore was not found"); // Cannot show a dialog and must show a toast, // because on some installations the app crashes before a dialog can be displayed. Utils.showToastLong(str("gms_core_toast_not_installed_message")); open(getGmsCoreDownload()); return; } // Check if GmsCore is running in the background. try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) { if (client == null) { Logger.printInfo(() -> "GmsCore is not running in the background"); showBatteryOptimizationDialog(context, "gms_core_dialog_not_whitelisted_not_allowed_in_background_message", "gms_core_dialog_open_website_text", (dialog, id) -> open(DONT_KILL_MY_APP_LINK)); return; } } // Check if GmsCore is whitelisted from battery optimizations. if (batteryOptimizationsEnabled(context)) { Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations"); showBatteryOptimizationDialog(context, "gms_core_dialog_not_whitelisted_using_battery_optimizations_message", "gms_core_dialog_continue_text", (dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context)); } } catch (Exception ex) { Logger.printException(() -> "checkGmsCore failure", ex); } } @SuppressLint("BatteryLife") // Permission is part of GmsCore private static void openGmsCoreDisableBatteryOptimizationsIntent(Activity activity) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.fromParts("package", GMS_CORE_PACKAGE_NAME, null)); activity.startActivityForResult(intent, 0); } /** * @return If GmsCore is not whitelisted from battery optimizations. */ private static boolean batteryOptimizationsEnabled(Context context) { var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME); } private static String getGmsCoreDownload() { final var vendorGroupId = getGmsCoreVendorGroupId(); //noinspection SwitchStatementWithTooFewBranches switch (vendorGroupId) { case "app.revanced": return "https://github.com/revanced/gmscore/releases/latest"; default: return vendorGroupId + ".android.gms"; } } // Modified by a patch. Do not touch. private static String getGmsCoreVendorGroupId() { return "app.revanced"; } }