Skip to content

Commit 6d9efda

Browse files
committed
Adjust resolution/density/language change handling
1 parent b6f8c76 commit 6d9efda

File tree

10 files changed

+311
-193
lines changed

10 files changed

+311
-193
lines changed

app/src/main/java/eu/chainfire/holeylight/Application.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import eu.chainfire.holeylight.misc.Settings;
1010

1111
public class Application extends android.app.Application {
12+
public static volatile String defaultLocale = "";
13+
1214
@Override
1315
public void onCreate() {
1416
super.onCreate();
@@ -20,6 +22,8 @@ public void onCreate() {
2022

2123
@Override
2224
protected void attachBaseContext(Context context) {
25+
defaultLocale = context.getResources().getConfiguration().getLocales().get(0).getLanguage();
26+
if (defaultLocale == null) defaultLocale = "";
2327
super.attachBaseContext(LocaleHelper.getContext(context));
2428
}
2529

app/src/main/java/eu/chainfire/holeylight/animation/Overlay.java

Lines changed: 75 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,22 @@
3030
import android.content.IntentFilter;
3131
import android.graphics.Color;
3232
import android.graphics.PixelFormat;
33-
import android.graphics.Point;
3433
import android.graphics.Rect;
3534
import android.graphics.drawable.Drawable;
35+
import android.os.Build;
3636
import android.os.Handler;
3737
import android.os.IBinder;
3838
import android.os.Looper;
3939
import android.os.PowerManager;
4040
import android.os.SystemClock;
41-
import android.util.DisplayMetrics;
4241
import android.view.Gravity;
4342
import android.view.WindowManager;
4443

45-
import java.lang.reflect.Method;
46-
4744
import eu.chainfire.holeylight.BuildConfig;
4845
import eu.chainfire.holeylight.misc.AODControl;
4946
import eu.chainfire.holeylight.misc.Battery;
5047
import eu.chainfire.holeylight.misc.Display;
51-
import eu.chainfire.holeylight.misc.Manufacturer;
48+
import eu.chainfire.holeylight.misc.ResolutionTracker;
5249
import eu.chainfire.holeylight.misc.Settings;
5350
import eu.chainfire.holeylight.misc.Slog;
5451
import eu.chainfire.holeylight.service.AccessibilityService;
@@ -90,46 +87,42 @@ public void onReceive(Context context, Intent intent) {
9087
case BuildConfig.APPLICATION_ID + ".ACTION_CONFIGURATION_CHANGED":
9188
case Intent.ACTION_CONFIGURATION_CHANGED:
9289
boolean force = !intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED);
93-
Point resolutionNow = getResolution();
94-
int densityNow = getDensity();
95-
float densityMult = getDensityMultiplier();
96-
if ((
97-
((resolutionNow.x != resolution.x) || (resolutionNow.y != resolution.y)) &&
98-
((resolutionNow.x != resolution.y) || (resolutionNow.y != resolution.x))
99-
) || (
100-
densityNow != density
101-
) || (
102-
force
103-
)) {
104-
Slog.d("Broadcast", "Resolution: %dx%d --> %dx%d, Density: %d --> %d [%.5f]", resolution.x, resolution.y, resolutionNow.x, resolutionNow.y, density, densityNow, densityMult);
105-
106-
// Resolution changed
107-
// This is an extremely ugly hack, don't try this at home
108-
// There are some internal states that are hard to figure out, including
109-
// oddities with Lottie's renderer. We just hard exit and let Android
110-
// restart us.
111-
resolution = resolutionNow;
112-
density = densityNow;
113-
Intent start = new Intent(context, DetectCutoutActivity.class);
114-
start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
115-
context.startActivity(start);
116-
handler.postDelayed(() -> {
117-
Context context1 = spritePlayer.getContext();
118-
Intent intent1 = new Intent(context1, DetectCutoutActivity.class);
119-
intent1.putExtra(BuildConfig.APPLICATION_ID + "/notifications", NotificationTracker.getInstance().saveToBytes());
120-
AlarmManager alarmManager = (AlarmManager) context1.getSystemService(Service.ALARM_SERVICE);
121-
alarmManager.setExactAndAllowWhileIdle(
122-
AlarmManager.ELAPSED_REALTIME,
123-
SystemClock.elapsedRealtime() + 1000,
124-
PendingIntent.getActivity(
125-
context1,
126-
0,
127-
intent1,
128-
0
129-
)
130-
);
131-
System.exit(0);
132-
}, 1000);
90+
if (resolutionTracker.changed() || force) {
91+
if (Build.VERSION.SDK_INT < 30) {
92+
// Resolution changed
93+
// This is an extremely ugly hack, don't try this at home
94+
// There are some internal states that are hard to figure out, including
95+
// oddities with Lottie's renderer. We just hard exit and let Android
96+
// restart us. This can certainly cause issues, hence using the
97+
// shutdown() call on Android 11+. There's no way for me to extensively
98+
// test this on older Android versions though, hence the API level
99+
// split.
100+
Intent start = new Intent(context, DetectCutoutActivity.class);
101+
start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
102+
context.startActivity(start);
103+
handler.postDelayed(() -> {
104+
Context context1 = spritePlayer.getContext();
105+
Intent intent1 = new Intent(context1, DetectCutoutActivity.class);
106+
intent1.putExtra(BuildConfig.APPLICATION_ID + "/notifications", NotificationTracker.getInstance().saveToBytes());
107+
AlarmManager alarmManager = (AlarmManager) context1.getSystemService(Service.ALARM_SERVICE);
108+
alarmManager.setExactAndAllowWhileIdle(
109+
AlarmManager.ELAPSED_REALTIME,
110+
SystemClock.elapsedRealtime() + 1000,
111+
PendingIntent.getActivity(
112+
context1,
113+
0,
114+
intent1,
115+
0
116+
)
117+
);
118+
System.exit(0);
119+
}, 1000);
120+
} else {
121+
// AccessibilityService/NotifificationListenerService should create a
122+
// new overlay with updated resources/configuration/etc as needed
123+
// within a few seconds
124+
shutdown();
125+
}
133126
} else {
134127
updateParams();
135128
}
@@ -184,11 +177,10 @@ public void onReceive(Context context, Intent intent) {
184177
private boolean lastHideAOD = false;
185178
private boolean lastWantAOD = false;
186179
private boolean added = false;
187-
private Point resolution;
188-
private int density;
189-
private final float densityMultiplier;
180+
private final ResolutionTracker resolutionTracker;
190181
private IBinder windowToken;
191182
private long lastVisibleTime;
183+
private volatile boolean terminated = false;
192184

193185
private Overlay(Context context) {
194186
windowManager = (WindowManager)context.getSystemService(Activity.WINDOW_SERVICE);
@@ -197,9 +189,7 @@ private Overlay(Context context) {
197189
drawWakeLock = ((PowerManager)context.getSystemService(POWER_SERVICE)).newWakeLock(0x00000080 | 0x40000000, BuildConfig.APPLICATION_ID + ":draw"); /* DRAW_WAKE_LOCK | UNIMPORTANT_FOR_LOGGING */
198190
handler = new Handler(Looper.getMainLooper());
199191
settings = Settings.getInstance(context);
200-
resolution = getResolution();
201-
density = getDensity();
202-
densityMultiplier = getDensityMultiplier();
192+
resolutionTracker = new ResolutionTracker("Overlay", context);
203193
}
204194

205195
@SuppressWarnings("all")
@@ -219,37 +209,6 @@ private void pokeWakeLocks(int timeout_ms) {
219209
drawWakeLock.acquire(timeout_ms);
220210
}
221211

222-
private Point getResolution() {
223-
DisplayMetrics metrics = new DisplayMetrics();
224-
windowManager.getDefaultDisplay().getRealMetrics(metrics);
225-
return new Point(metrics.widthPixels, metrics.heightPixels);
226-
}
227-
228-
private int getDensity() {
229-
DisplayMetrics metrics = new DisplayMetrics();
230-
windowManager.getDefaultDisplay().getRealMetrics(metrics);
231-
return metrics.densityDpi;
232-
}
233-
234-
private float getDensityMultiplier() {
235-
float ret = 1.0f;
236-
if (Manufacturer.isGoogle()) {
237-
// things work differently on Samsung
238-
try {
239-
@SuppressLint("PrivateApi") Class<?> c = Class.forName("android.os.SystemProperties");
240-
Method get = c.getMethod("get", String.class);
241-
String d = (String)get.invoke(null, "ro.sf.lcd_density");
242-
if (d != null) {
243-
int i = Integer.parseInt(d);
244-
ret = (float)i / (float)density;
245-
}
246-
} catch (Exception e) {
247-
e.printStackTrace();
248-
}
249-
}
250-
return ret;
251-
}
252-
253212
private void initActualOverlay(Context context, IBinder windowToken) {
254213
synchronized (this) {
255214
if (this.windowToken == null && windowToken != null) this.windowToken = windowToken;
@@ -258,7 +217,7 @@ private void initActualOverlay(Context context, IBinder windowToken) {
258217
spritePlayer = new SpritePlayer(context);
259218

260219
initParams();
261-
animation = new NotificationAnimation(context, spritePlayer, densityMultiplier, new NotificationAnimation.OnNotificationAnimationListener() {
220+
animation = new NotificationAnimation(context, spritePlayer, resolutionTracker.getDensityMultiplier(), new NotificationAnimation.OnNotificationAnimationListener() {
262221
private int skips = 0;
263222

264223
@Override
@@ -339,13 +298,17 @@ public boolean onAnimationComplete(SpritePlayer view) {
339298
@Override
340299
protected void finalize() throws Throwable {
341300
if (spritePlayer != null) {
342-
spritePlayer.getContext().getApplicationContext().unregisterReceiver(broadcastReceiver);
301+
try {
302+
spritePlayer.getContext().getApplicationContext().unregisterReceiver(broadcastReceiver);
303+
} catch (Exception ignored) {
304+
}
343305
}
344306
super.finalize();
345307
}
346308

347309
@SuppressLint("RtlHardcoded")
348310
private void initParams() {
311+
if (terminated) return;
349312
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
350313
0,
351314
0,
@@ -380,23 +343,31 @@ private void initParams() {
380343
}
381344

382345
private void updateParams() {
346+
if (terminated) return;
383347
animation.applyDimensions();
384348
}
385349

386350
private void createOverlay() {
351+
if (terminated) return;
387352
if (added) return;
388353
try {
389354
updateParams();
390355
added = true; // had a case of a weird exception that caused this to run in a loop if placed after addView
391356
windowManager.addView(spritePlayer, spritePlayer.getLayoutParams());
392357
} catch (Exception e) {
393358
e.printStackTrace();
394-
if (e.toString().contains("BadTokenException")) System.exit(0); // we will not recover from this without restart
359+
if (e.toString().contains("BadTokenException")) {
360+
// we will not recover from this without restart
361+
// unfortunately this may cause the AccessibilityService to get excessive delays
362+
// in processing until reboot or reinstall :/
363+
System.exit(0);
364+
}
395365
}
396366
test_lastVisible = added;
397367
}
398368

399369
private void updateOverlay() {
370+
if (terminated) return;
400371
if (!added) return;
401372
try {
402373
updateParams();
@@ -439,6 +410,8 @@ public void evaluate(boolean refreshAll) {
439410
}
440411

441412
public void evaluate(boolean refreshAll, boolean isDelayed) {
413+
if (terminated) return;
414+
442415
if (spritePlayer == null) {
443416
if (wanted) handler.postDelayed(evaluateLoop, 500);
444417
return;
@@ -576,6 +549,7 @@ public void evaluate(boolean refreshAll, boolean isDelayed) {
576549
}
577550

578551
public void show(int[] colors, Drawable[] icons, boolean forceRefresh) {
552+
if (terminated) return;
579553
handler.removeCallbacks(evaluateLoop);
580554
this.colors = colors;
581555
this.icons = icons;
@@ -586,18 +560,34 @@ public void show(int[] colors, Drawable[] icons, boolean forceRefresh) {
586560
}
587561

588562
public void hide(boolean immediately) {
563+
if (terminated) return;
589564
handler.removeCallbacks(evaluateLoop);
590565
wanted = false;
591566
kill = immediately;
592567
evaluate(true);
593568
}
594569

595570
public void updateTSPRect(Rect rect, Rect clockRect, int overlayBottom) {
571+
if (terminated) return;
596572
boolean apply = Display.isOff(spritePlayer.getContext(), true);
597573
Slog.d("AOD_TSP", "Overlay " + rect.toString() + " clock " + clockRect + " bottom:" + overlayBottom + " apply:" + apply);
598574
if (apply) {
599575
pokeWakeLocks(250);
600576
animation.updateTSPRect(rect, clockRect, overlayBottom);
601577
}
602578
}
579+
580+
public void shutdown() {
581+
if (Build.VERSION.SDK_INT >= 30) {
582+
synchronized (Overlay.class) {
583+
instance = null;
584+
terminated = true;
585+
}
586+
if (animation.isPlaying()) animation.stop(true);
587+
removeOverlay();
588+
animation = null;
589+
spritePlayer.getContext().getApplicationContext().unregisterReceiver(broadcastReceiver);
590+
spritePlayer = null;
591+
}
592+
}
603593
}

app/src/main/java/eu/chainfire/holeylight/misc/LocaleHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ public static Locale getLocale(Context context) {
2929
try {
3030
Configuration config = context.getResources().getConfiguration();
3131

32-
String lang = Settings.getInstance(context).getLocale();
33-
if (!"".equals(lang) && !config.getLocales().get(0).getLanguage().replace('-', '_').equals(lang.replace('-', '_'))) {
32+
String lang = Settings.getInstance(context).getLocale(true);
33+
if (lang != null && !"".equals(lang) && !config.getLocales().get(0).getLanguage().replace('-', '_').equals(lang.replace('-', '_'))) {
3434
if (lang.contains("_")) {
3535
locale = new Locale(lang.substring(0, lang.indexOf("_")), lang.substring(lang.indexOf("_") + 1));
3636
} else {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package eu.chainfire.holeylight.misc;
2+
3+
import android.annotation.SuppressLint;
4+
import android.content.Context;
5+
import android.graphics.Point;
6+
import android.util.DisplayMetrics;
7+
import android.view.WindowManager;
8+
9+
import java.lang.reflect.Method;
10+
11+
public class ResolutionTracker {
12+
private final String TAG;
13+
private final WindowManager windowManager;
14+
private Point resolution;
15+
private int density;
16+
17+
public ResolutionTracker(String tag, Context context) {
18+
this.TAG = tag;
19+
windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
20+
resolution = getResolution();
21+
density = getDensity();
22+
}
23+
24+
public Point getResolution() {
25+
DisplayMetrics metrics = new DisplayMetrics();
26+
windowManager.getDefaultDisplay().getRealMetrics(metrics);
27+
return new Point(metrics.widthPixels, metrics.heightPixels);
28+
}
29+
30+
public int getDensity() {
31+
DisplayMetrics metrics = new DisplayMetrics();
32+
windowManager.getDefaultDisplay().getRealMetrics(metrics);
33+
return metrics.densityDpi;
34+
}
35+
36+
public float getDensityMultiplier() {
37+
float ret = 1.0f;
38+
if (Manufacturer.isGoogle()) {
39+
// things work differently on Samsung
40+
try {
41+
@SuppressLint("PrivateApi") Class<?> c = Class.forName("android.os.SystemProperties");
42+
Method get = c.getMethod("get", String.class);
43+
String d = (String)get.invoke(null, "ro.sf.lcd_density");
44+
if (d != null) {
45+
int i = Integer.parseInt(d);
46+
ret = (float)i / (float)density;
47+
}
48+
} catch (Exception e) {
49+
e.printStackTrace();
50+
}
51+
}
52+
return ret;
53+
}
54+
55+
public boolean changed() {
56+
Point resolutionNow = getResolution();
57+
int densityNow = getDensity();
58+
float densityMult = getDensityMultiplier();
59+
if ((
60+
((resolutionNow.x != resolution.x) || (resolutionNow.y != resolution.y)) &&
61+
((resolutionNow.x != resolution.y) || (resolutionNow.y != resolution.x))
62+
) || (
63+
densityNow != density
64+
)) {
65+
Slog.d(TAG + "/Resolution", "Resolution: %dx%d --> %dx%d, Density: %d --> %d [%.5f]", resolution.x, resolution.y, resolutionNow.x, resolutionNow.y, density, densityNow, densityMult);
66+
density = densityNow;
67+
resolution = resolutionNow;
68+
return true;
69+
}
70+
return false;
71+
}
72+
}

0 commit comments

Comments
 (0)