Skip to content

Commit 255c943

Browse files
authored
Update BatteryHook.java
1 parent c4e9a20 commit 255c943

1 file changed

Lines changed: 124 additions & 7 deletions

File tree

app/src/main/java/com/github/dhangofa/batteryremapper/BatteryHook.java

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package com.github.dhangofa.batteryremapper;
22

3+
import android.app.AlertDialog;
4+
import android.app.AndroidAppHelper;
5+
import android.content.Context;
36
import android.content.Intent;
47
import android.os.BatteryManager;
8+
import android.os.Bundle;
9+
import android.os.CountDownTimer;
10+
import android.os.Handler;
11+
import android.os.Looper;
12+
import android.view.WindowManager;
513
import de.robv.android.xposed.IXposedHookLoadPackage;
614
import de.robv.android.xposed.XC_MethodHook;
715
import de.robv.android.xposed.XposedBridge;
@@ -10,40 +18,149 @@
1018

1119
public class BatteryHook implements IXposedHookLoadPackage {
1220

21+
// Keep track of state so we don't spawn 100 dialogs at once
22+
private static boolean isShuttingDown = false;
23+
private static AlertDialog shutdownDialog = null;
24+
private static CountDownTimer shutdownTimer = null;
25+
1326
@Override
1427
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
15-
// We only want to trick the System UI into drawing the new number.
16-
// We leave the rest of the OS alone so charging logic doesn't break.
1728
if (!lpparam.packageName.equals("com.android.systemui")) {
1829
return;
1930
}
2031

2132
try {
22-
// Hook the exact moment SystemUI reads an integer from ANY Intent
2333
XposedHelpers.findAndHookMethod(Intent.class, "getIntExtra", String.class, int.class, new XC_MethodHook() {
2434
@Override
2535
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
2636
String key = (String) param.args[0];
2737

28-
// If SystemUI is asking for the Battery Level...
2938
if (BatteryManager.EXTRA_LEVEL.equals(key)) {
30-
// Get the real, physical battery level the system just read
3139
int originalLevel = (Integer) param.getResult();
3240

33-
// Run our math and overwrite the result being returned to crDroid
41+
Intent intent = (Intent) param.thisObject;
42+
Bundle extras = intent.getExtras();
43+
44+
// Check if phone is plugged in (0 means on battery)
45+
int plugged = extras != null ? extras.getInt(BatteryManager.EXTRA_PLUGGED, 0) : 0;
46+
47+
// 1. SHUTDOWN LOGIC
48+
if (originalLevel <= 20) {
49+
if (plugged == 0) {
50+
// Hit 20% and not charging: Start the countdown once
51+
if (!isShuttingDown) {
52+
isShuttingDown = true;
53+
startCountdown();
54+
}
55+
} else {
56+
// Hit 20% but charger is connected: Abort shutdown
57+
if (isShuttingDown) {
58+
cancelCountdown();
59+
}
60+
}
61+
} else {
62+
// Battery is safely above 20%: Abort shutdown if running
63+
if (isShuttingDown) {
64+
cancelCountdown();
65+
}
66+
}
67+
68+
// 2. UI LOGIC: Overwrite the result for the status bar
3469
int displayedLevel = remapBattery(originalLevel);
3570
param.setResult(displayedLevel);
3671
}
3772
}
3873
});
3974

40-
XposedBridge.log("BatteryRemapper: Successfully hooked Intent.getIntExtra for crDroid!");
75+
XposedBridge.log("BatteryRemapper: Hook initialized with 30s Countdown Feature!");
4176

4277
} catch (Throwable t) {
4378
XposedBridge.log("BatteryRemapper Hook Critical Failure: " + t.getMessage());
4479
}
4580
}
4681

82+
private void startCountdown() {
83+
// UI elements MUST run on the main Android UI thread
84+
new Handler(Looper.getMainLooper()).post(new Runnable() {
85+
@Override
86+
public void run() {
87+
try {
88+
Context context = AndroidAppHelper.currentApplication();
89+
if (context == null) return;
90+
91+
// Build the Warning Box
92+
AlertDialog.Builder builder = new AlertDialog.Builder(context, android.R.style.Theme_DeviceDefault_Dialog_Alert);
93+
builder.setTitle("Battery Depleted");
94+
builder.setMessage("Device will shut down in 30 seconds.\nPlug in charger to cancel.");
95+
builder.setCancelable(false); // Prevents dismissing by tapping outside
96+
97+
shutdownDialog = builder.create();
98+
// TYPE_SYSTEM_ERROR (2010) bypasses all screens to draw directly over everything
99+
shutdownDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
100+
shutdownDialog.show();
101+
102+
// Start the 30 second timer (30000ms total, updating every 1000ms)
103+
shutdownTimer = new CountDownTimer(30000, 1000) {
104+
@Override
105+
public void onTick(long millisUntilFinished) {
106+
if (shutdownDialog != null && shutdownDialog.isShowing()) {
107+
shutdownDialog.setMessage("Device will shut down in " + (millisUntilFinished / 1000) + " seconds.\nPlug in charger to cancel.");
108+
}
109+
}
110+
111+
@Override
112+
public void onFinish() {
113+
cancelCountdown(); // Cleanup the UI
114+
triggerShutdown(); // Kill the power
115+
}
116+
}.start();
117+
118+
XposedBridge.log("BatteryRemapper: 30-second shutdown countdown started.");
119+
} catch (Throwable t) {
120+
XposedBridge.log("BatteryRemapper UI Failure: " + t.getMessage());
121+
// Failsafe: If the custom ROM blocks the dialog from drawing, shut down gracefully anyway
122+
triggerShutdown();
123+
}
124+
}
125+
});
126+
}
127+
128+
private void cancelCountdown() {
129+
isShuttingDown = false;
130+
new Handler(Looper.getMainLooper()).post(new Runnable() {
131+
@Override
132+
public void run() {
133+
if (shutdownTimer != null) {
134+
shutdownTimer.cancel();
135+
shutdownTimer = null;
136+
}
137+
if (shutdownDialog != null) {
138+
if (shutdownDialog.isShowing()) {
139+
shutdownDialog.dismiss();
140+
}
141+
shutdownDialog = null;
142+
}
143+
XposedBridge.log("BatteryRemapper: Shutdown cancelled! Charger detected.");
144+
}
145+
});
146+
}
147+
148+
private void triggerShutdown() {
149+
try {
150+
Context context = AndroidAppHelper.currentApplication();
151+
if (context != null) {
152+
Object powerManager = context.getSystemService(Context.POWER_SERVICE);
153+
if (powerManager != null) {
154+
// Call the hidden OS-level power off command
155+
XposedHelpers.callMethod(powerManager, "shutdown", false, "battery_remapper_empty", false);
156+
}
157+
}
158+
} catch (Throwable t) {
159+
XposedBridge.log("BatteryRemapper Shutdown Failure: " + t.getMessage());
160+
isShuttingDown = false;
161+
}
162+
}
163+
47164
private int remapBattery(int physicalLevel) {
48165
if (physicalLevel <= 20) return 0;
49166
if (physicalLevel >= 80) return 100;

0 commit comments

Comments
 (0)