Skip to content

Commit 2375e36

Browse files
authored
feat: [3888] Add support for safe areas in android (#3889)
* feat: [3888] Add support for safe areas in android #3888 * fixed lambda not supported * fixed lambda not supported * change to use reflection so we can compile on older sdk * fixed compile errors * made variables final for 1.6 compatibility
1 parent 6566bf9 commit 2375e36

File tree

6 files changed

+113
-0
lines changed

6 files changed

+113
-0
lines changed

Ports/Android/src/com/codename1/impl/android/AndroidAsyncView.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public LayoutParams(int x, int y, int w, int h, PeerComponent pc) {
8080
public boolean dirty;
8181
}
8282

83+
public Rect getSafeAreaInsets() {
84+
return cn1View.getSafeArea();
85+
}
86+
8387
@Override
8488
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
8589
final int count = getChildCount();

Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,23 @@ public int getDeviceDensity() {
12921292
return Display.DENSITY_MEDIUM;
12931293
}
12941294

1295+
public Rectangle getDisplaySafeArea(Rectangle rect) {
1296+
if (rect == null) {
1297+
rect = new Rectangle();
1298+
}
1299+
if (this.myView != null) {
1300+
rect.setBounds(
1301+
this.myView.getSafeAreaInsets().left,
1302+
this.myView.getSafeAreaInsets().top,
1303+
getDisplayWidth() - this.myView.getSafeAreaInsets().right - this.myView.getSafeAreaInsets().left,
1304+
getDisplayHeight() - this.myView.getSafeAreaInsets().top - this.myView.getSafeAreaInsets().bottom
1305+
);
1306+
return rect;
1307+
}
1308+
1309+
return super.getDisplaySafeArea(rect);
1310+
}
1311+
12951312
/**
12961313
* A status flag to indicate that CN1 is in the process of deinitializing.
12971314
*/

Ports/Android/src/com/codename1/impl/android/AndroidSurfaceView.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public void surfaceDestroyed(SurfaceHolder arg0) {
7070

7171
}
7272

73+
@Override
74+
public Rect getSafeAreaInsets() {
75+
return cn1View.getSafeArea();
76+
}
77+
7378
public boolean isOpaque() {
7479
return true;
7580
}

Ports/Android/src/com/codename1/impl/android/AndroidTextureView.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
7272
});
7373
}
7474

75+
@Override
76+
public Rect getSafeAreaInsets() {
77+
return cn1View.getSafeArea();
78+
}
79+
7580
public boolean isOpaque() {
7681
return true;
7782
}

Ports/Android/src/com/codename1/impl/android/CodenameOneSurface.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ public interface CodenameOneSurface {
4949
public void setLayoutParams(ViewGroup.LayoutParams lp);
5050

5151
public boolean alwaysRepaintAll();
52+
53+
public Rect getSafeAreaInsets();
5254
}

Ports/Android/src/com/codename1/impl/android/CodenameOneView.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@
3030
import android.util.Log;
3131
import android.view.*;
3232
import android.view.inputmethod.EditorInfo;
33+
import android.os.Build;
3334
import com.codename1.ui.Component;
3435
import com.codename1.ui.Display;
3536
import com.codename1.ui.Form;
3637
import com.codename1.ui.PeerComponent;
3738
import com.codename1.ui.TextArea;
3839
import com.codename1.ui.events.ActionEvent;
3940
import com.codename1.ui.events.ActionListener;
41+
import java.lang.reflect.Method;
42+
4043

4144
/**
4245
*
@@ -56,6 +59,11 @@ public class CodenameOneView {
5659
//private volatile boolean created = false;
5760
private boolean drawing;
5861

62+
private final Rect safeArea = new Rect();
63+
64+
private static final int VERSION_CODE_P = 28;
65+
private static final int VERSION_CODE_M = 23;
66+
5967
public CodenameOneView(Activity activity, View androidView, AndroidImplementation implementation, boolean drawing) {
6068

6169
this.implementation = implementation;
@@ -94,6 +102,12 @@ public CodenameOneView(Activity activity, View androidView, AndroidImplementatio
94102
android.view.Display androidDisplay = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
95103
width = androidDisplay.getWidth();
96104
height = androidDisplay.getHeight();
105+
View rootView = activity.getWindow().getDecorView();
106+
rootView.post(new Runnable() {
107+
public void run() {
108+
updateSafeArea();
109+
}
110+
});
97111
initBitmaps(width, height);
98112
}
99113

@@ -148,6 +162,65 @@ public void visibilityChangedTo(boolean visible) {
148162
//flushGraphics();
149163
}
150164

165+
private void updateSafeArea() {
166+
final Activity activity = CodenameOneView.this.implementation.getActivity();
167+
final Rect rect = this.safeArea;
168+
final View rootView = activity.getWindow().getDecorView();
169+
if (Build.VERSION.SDK_INT >= VERSION_CODE_P) {
170+
try {
171+
Method getRootWindowInsetsMethod = View.class.getMethod("getRootWindowInsets");
172+
Object insets = getRootWindowInsetsMethod.invoke(rootView);
173+
if (insets != null) {
174+
Class<?> windowInsetsClass = Class.forName("android.view.WindowInsets");
175+
Method getDisplayCutoutMethod = windowInsetsClass.getMethod("getDisplayCutout");
176+
Object cutout = getDisplayCutoutMethod.invoke(insets);
177+
178+
if (cutout != null) {
179+
Class<?> displayCutoutClass = Class.forName("android.view.DisplayCutout");
180+
Method getSafeInsetLeft = displayCutoutClass.getMethod("getSafeInsetLeft");
181+
Method getSafeInsetTop = displayCutoutClass.getMethod("getSafeInsetTop");
182+
Method getSafeInsetRight = displayCutoutClass.getMethod("getSafeInsetRight");
183+
Method getSafeInsetBottom = displayCutoutClass.getMethod("getSafeInsetBottom");
184+
185+
rect.left = ((Integer) getSafeInsetLeft.invoke(cutout)).intValue();
186+
rect.top = ((Integer) getSafeInsetTop.invoke(cutout)).intValue();
187+
rect.right = ((Integer) getSafeInsetRight.invoke(cutout)).intValue();
188+
rect.bottom = ((Integer) getSafeInsetBottom.invoke(cutout)).intValue();
189+
}
190+
}
191+
} catch (Exception e) {
192+
rect.top = 0;
193+
rect.left = 0;
194+
rect.right = 0;
195+
rect.bottom = 0;
196+
}
197+
198+
} else if (Build.VERSION.SDK_INT >= VERSION_CODE_M) {
199+
rootView.post(new Runnable() {
200+
public void run() {
201+
WindowInsets insets = rootView.getRootWindowInsets();
202+
if (insets != null) {
203+
rect.top = insets.getSystemWindowInsetTop();
204+
rect.left = insets.getSystemWindowInsetLeft();;
205+
rect.right = insets.getSystemWindowInsetRight();
206+
rect.bottom = insets.getSystemWindowInsetBottom();
207+
} else {
208+
rect.top = 0;
209+
rect.left = 0;
210+
rect.right = 0;
211+
rect.bottom = 0;
212+
}
213+
}
214+
});
215+
} else {
216+
// For pre-Marshmallow (API < 23), assume full screen
217+
rect.top = 0;
218+
rect.left = 0;
219+
rect.right = 0;
220+
rect.bottom = 0;
221+
}
222+
}
223+
151224
public void handleSizeChange(int w, int h) {
152225

153226
if(!drawing) {
@@ -161,6 +234,9 @@ public void handleSizeChange(int w, int h) {
161234
}
162235
this.width = w;
163236
this.height = h;
237+
238+
updateSafeArea();
239+
164240
Log.d("Codename One", "sizechanged: " + width + " " + height + " " + this);
165241
if (this.implementation.getCurrentForm() == null) {
166242
/**
@@ -494,6 +570,10 @@ public int getViewWidth() {
494570
return width;
495571
}
496572

573+
public Rect getSafeArea() {
574+
return safeArea;
575+
}
576+
497577
public void setInputType(EditorInfo editorInfo) {
498578

499579
/**

0 commit comments

Comments
 (0)