30
30
import android .content .IntentFilter ;
31
31
import android .graphics .Color ;
32
32
import android .graphics .PixelFormat ;
33
- import android .graphics .Point ;
34
33
import android .graphics .Rect ;
35
34
import android .graphics .drawable .Drawable ;
35
+ import android .os .Build ;
36
36
import android .os .Handler ;
37
37
import android .os .IBinder ;
38
38
import android .os .Looper ;
39
39
import android .os .PowerManager ;
40
40
import android .os .SystemClock ;
41
- import android .util .DisplayMetrics ;
42
41
import android .view .Gravity ;
43
42
import android .view .WindowManager ;
44
43
45
- import java .lang .reflect .Method ;
46
-
47
44
import eu .chainfire .holeylight .BuildConfig ;
48
45
import eu .chainfire .holeylight .misc .AODControl ;
49
46
import eu .chainfire .holeylight .misc .Battery ;
50
47
import eu .chainfire .holeylight .misc .Display ;
51
- import eu .chainfire .holeylight .misc .Manufacturer ;
48
+ import eu .chainfire .holeylight .misc .ResolutionTracker ;
52
49
import eu .chainfire .holeylight .misc .Settings ;
53
50
import eu .chainfire .holeylight .misc .Slog ;
54
51
import eu .chainfire .holeylight .service .AccessibilityService ;
@@ -90,46 +87,42 @@ public void onReceive(Context context, Intent intent) {
90
87
case BuildConfig .APPLICATION_ID + ".ACTION_CONFIGURATION_CHANGED" :
91
88
case Intent .ACTION_CONFIGURATION_CHANGED :
92
89
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
+ }
133
126
} else {
134
127
updateParams ();
135
128
}
@@ -184,11 +177,10 @@ public void onReceive(Context context, Intent intent) {
184
177
private boolean lastHideAOD = false ;
185
178
private boolean lastWantAOD = false ;
186
179
private boolean added = false ;
187
- private Point resolution ;
188
- private int density ;
189
- private final float densityMultiplier ;
180
+ private final ResolutionTracker resolutionTracker ;
190
181
private IBinder windowToken ;
191
182
private long lastVisibleTime ;
183
+ private volatile boolean terminated = false ;
192
184
193
185
private Overlay (Context context ) {
194
186
windowManager = (WindowManager )context .getSystemService (Activity .WINDOW_SERVICE );
@@ -197,9 +189,7 @@ private Overlay(Context context) {
197
189
drawWakeLock = ((PowerManager )context .getSystemService (POWER_SERVICE )).newWakeLock (0x00000080 | 0x40000000 , BuildConfig .APPLICATION_ID + ":draw" ); /* DRAW_WAKE_LOCK | UNIMPORTANT_FOR_LOGGING */
198
190
handler = new Handler (Looper .getMainLooper ());
199
191
settings = Settings .getInstance (context );
200
- resolution = getResolution ();
201
- density = getDensity ();
202
- densityMultiplier = getDensityMultiplier ();
192
+ resolutionTracker = new ResolutionTracker ("Overlay" , context );
203
193
}
204
194
205
195
@ SuppressWarnings ("all" )
@@ -219,37 +209,6 @@ private void pokeWakeLocks(int timeout_ms) {
219
209
drawWakeLock .acquire (timeout_ms );
220
210
}
221
211
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
-
253
212
private void initActualOverlay (Context context , IBinder windowToken ) {
254
213
synchronized (this ) {
255
214
if (this .windowToken == null && windowToken != null ) this .windowToken = windowToken ;
@@ -258,7 +217,7 @@ private void initActualOverlay(Context context, IBinder windowToken) {
258
217
spritePlayer = new SpritePlayer (context );
259
218
260
219
initParams ();
261
- animation = new NotificationAnimation (context , spritePlayer , densityMultiplier , new NotificationAnimation .OnNotificationAnimationListener () {
220
+ animation = new NotificationAnimation (context , spritePlayer , resolutionTracker . getDensityMultiplier () , new NotificationAnimation .OnNotificationAnimationListener () {
262
221
private int skips = 0 ;
263
222
264
223
@ Override
@@ -339,13 +298,17 @@ public boolean onAnimationComplete(SpritePlayer view) {
339
298
@ Override
340
299
protected void finalize () throws Throwable {
341
300
if (spritePlayer != null ) {
342
- spritePlayer .getContext ().getApplicationContext ().unregisterReceiver (broadcastReceiver );
301
+ try {
302
+ spritePlayer .getContext ().getApplicationContext ().unregisterReceiver (broadcastReceiver );
303
+ } catch (Exception ignored ) {
304
+ }
343
305
}
344
306
super .finalize ();
345
307
}
346
308
347
309
@ SuppressLint ("RtlHardcoded" )
348
310
private void initParams () {
311
+ if (terminated ) return ;
349
312
WindowManager .LayoutParams params = new WindowManager .LayoutParams (
350
313
0 ,
351
314
0 ,
@@ -380,23 +343,31 @@ private void initParams() {
380
343
}
381
344
382
345
private void updateParams () {
346
+ if (terminated ) return ;
383
347
animation .applyDimensions ();
384
348
}
385
349
386
350
private void createOverlay () {
351
+ if (terminated ) return ;
387
352
if (added ) return ;
388
353
try {
389
354
updateParams ();
390
355
added = true ; // had a case of a weird exception that caused this to run in a loop if placed after addView
391
356
windowManager .addView (spritePlayer , spritePlayer .getLayoutParams ());
392
357
} catch (Exception e ) {
393
358
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
+ }
395
365
}
396
366
test_lastVisible = added ;
397
367
}
398
368
399
369
private void updateOverlay () {
370
+ if (terminated ) return ;
400
371
if (!added ) return ;
401
372
try {
402
373
updateParams ();
@@ -439,6 +410,8 @@ public void evaluate(boolean refreshAll) {
439
410
}
440
411
441
412
public void evaluate (boolean refreshAll , boolean isDelayed ) {
413
+ if (terminated ) return ;
414
+
442
415
if (spritePlayer == null ) {
443
416
if (wanted ) handler .postDelayed (evaluateLoop , 500 );
444
417
return ;
@@ -576,6 +549,7 @@ public void evaluate(boolean refreshAll, boolean isDelayed) {
576
549
}
577
550
578
551
public void show (int [] colors , Drawable [] icons , boolean forceRefresh ) {
552
+ if (terminated ) return ;
579
553
handler .removeCallbacks (evaluateLoop );
580
554
this .colors = colors ;
581
555
this .icons = icons ;
@@ -586,18 +560,34 @@ public void show(int[] colors, Drawable[] icons, boolean forceRefresh) {
586
560
}
587
561
588
562
public void hide (boolean immediately ) {
563
+ if (terminated ) return ;
589
564
handler .removeCallbacks (evaluateLoop );
590
565
wanted = false ;
591
566
kill = immediately ;
592
567
evaluate (true );
593
568
}
594
569
595
570
public void updateTSPRect (Rect rect , Rect clockRect , int overlayBottom ) {
571
+ if (terminated ) return ;
596
572
boolean apply = Display .isOff (spritePlayer .getContext (), true );
597
573
Slog .d ("AOD_TSP" , "Overlay " + rect .toString () + " clock " + clockRect + " bottom:" + overlayBottom + " apply:" + apply );
598
574
if (apply ) {
599
575
pokeWakeLocks (250 );
600
576
animation .updateTSPRect (rect , clockRect , overlayBottom );
601
577
}
602
578
}
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
+ }
603
593
}
0 commit comments