Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019 Gluon
* Copyright (c) 2016, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -30,6 +30,7 @@
import com.gluonhq.attach.util.Services;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;

import java.util.Optional;

Expand Down Expand Up @@ -162,4 +163,15 @@ static Optional<DisplayService> create() {
* @since 3.8.0
*/
ReadOnlyObjectProperty<Notch> notchProperty();

/**
* Property that contains the insets of the system bars (typically
* the status bar at the top and the navigation bar at the bottom).
* These insets can be used to add the necessary padding so the application
* doesn't get underneath the content shown at the system bars.
*
* @return A read only property with the insets of the system bars
*/
ReadOnlyObjectProperty<Insets> systemBarsInsetsProperty();

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020 Gluon
* Copyright (c) 2016, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -29,9 +29,11 @@

import com.gluonhq.attach.display.DisplayService;
import com.gluonhq.attach.util.Util;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;

Expand All @@ -42,6 +44,8 @@ public class AndroidDisplayService implements DisplayService {

private static final Logger LOG = Logger.getLogger(AndroidDisplayService.class.getName());

private static final ReadOnlyObjectWrapper<Insets> insetsProperty = new ReadOnlyObjectWrapper<>();

private static final boolean debug = Util.DEBUG;

static {
Expand Down Expand Up @@ -118,8 +122,18 @@ public ReadOnlyObjectProperty<Notch> notchProperty() {
return new ReadOnlyObjectWrapper<>(Notch.UNKNOWN).getReadOnlyProperty();
}

@Override
public ReadOnlyObjectProperty<Insets> systemBarsInsetsProperty() {
return insetsProperty.getReadOnlyProperty();
}

// native
private native static boolean isPhoneFactor();
private native static double[] screenSize();
private native static boolean screenRound();

// callback
private static void notifyInsets(double top, double right, double bottom, double left) {
Platform.runLater(() -> insetsProperty.set(new Insets(top, right, bottom, left)));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019 Gluon
* Copyright (c) 2016, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -31,6 +31,7 @@
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;

Expand All @@ -41,6 +42,8 @@ public class DesktopDisplayService implements DisplayService {

private static final Logger LOG = Logger.getLogger(DesktopDisplayService.class.getName());

private static final ReadOnlyObjectWrapper<Insets> insetsProperty = new ReadOnlyObjectWrapper<>();

private final Dimension2D dimensions;

public DesktopDisplayService() {
Expand Down Expand Up @@ -101,6 +104,11 @@ public ReadOnlyObjectProperty<Notch> notchProperty() {
return new ReadOnlyObjectWrapper<>(Notch.UNKNOWN).getReadOnlyProperty();
}

@Override
public ReadOnlyObjectProperty<Insets> systemBarsInsetsProperty() {
return insetsProperty.getReadOnlyProperty();
}

private static void log(String message, Throwable cause) {
LOG.log(Level.FINE, message);
if (LOG.isLoggable(Level.FINE)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019 Gluon
* Copyright (c) 2016, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -34,6 +34,7 @@
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;

public class IOSDisplayService implements DisplayService {

Expand All @@ -44,6 +45,8 @@ public class IOSDisplayService implements DisplayService {

private static ReadOnlyObjectWrapper<DisplayService.Notch> notch;

private static final ReadOnlyObjectWrapper<Insets> insetsProperty = new ReadOnlyObjectWrapper<>();

public IOSDisplayService() {
notch = new ReadOnlyObjectWrapper<>(Notch.UNKNOWN);
LifecycleService.create().ifPresent(l -> {
Expand Down Expand Up @@ -100,6 +103,11 @@ public ReadOnlyObjectProperty<Notch> notchProperty() {
return notch.getReadOnlyProperty();
}

@Override
public ReadOnlyObjectProperty<Insets> systemBarsInsetsProperty() {
return insetsProperty.getReadOnlyProperty();
}

// native
private static native void initDisplay();

Expand All @@ -119,4 +127,8 @@ private static void notifyDisplay(String o) {
Platform.runLater(() -> notch.setValue(d));
}
}

private static void notifyInsets(double top, double right, double bottom, double left) {
Platform.runLater(() -> insetsProperty.set(new Insets(top, right, bottom, left)));
}
}
26 changes: 25 additions & 1 deletion modules/display/src/main/native/android/c/display.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021, Gluon
* Copyright (c) 2020, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -34,6 +34,10 @@ static jmethodID jDisplayServiceHeightMethod;
static jmethodID jDisplayServiceFactorMethod;
static jmethodID jDisplayServiceRoundMethod;

// Graal handles
static jclass jGraalDisplayClass;
static jmethodID jGraalNotifyInsetsMethod;

static void initializeDisplayDalvikHandles() {
jDisplayServiceClass = GET_REGISTER_DALVIK_CLASS(jDisplayServiceClass, "com/gluonhq/helloandroid/DalvikDisplayService");
ATTACH_DALVIK();
Expand All @@ -49,6 +53,11 @@ static void initializeDisplayDalvikHandles() {
DETACH_DALVIK();
}

static void initializeGraalHandles(JNIEnv* env) {
jGraalDisplayClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/gluonhq/attach/display/impl/AndroidDisplayService"));
jGraalNotifyInsetsMethod = (*env)->GetStaticMethodID(env, jGraalDisplayClass, "notifyInsets", "(DDDD)V");
}

//////////////////////////
// From Graal to native //
//////////////////////////
Expand All @@ -65,6 +74,7 @@ JNI_OnLoad_display(JavaVM *vm, void *reserved)
return JNI_FALSE;
}
ATTACH_LOG_FINE("[Display Service] Initializing native Display from OnLoad");
initializeGraalHandles(graalEnv);
initializeDisplayDalvikHandles();
return JNI_VERSION_1_8;
#else
Expand Down Expand Up @@ -109,3 +119,17 @@ JNIEXPORT jboolean JNICALL Java_com_gluonhq_attach_display_impl_AndroidDisplaySe
DETACH_DALVIK();
return answer;
}

///////////////////////////
// From Dalvik to native //
///////////////////////////

JNIEXPORT void JNICALL Java_com_gluonhq_helloandroid_DalvikDisplayService_notifyInsets(
JNIEnv *env, jobject service, jdouble top, jdouble right, jdouble bottom, jdouble left) {
if (isDebugAttach()) {
ATTACH_LOG_FINE("Native layer got new inset: %.1f,%.1f,%.1f,%.1f\n", top, right, bottom, left);
}
ATTACH_GRAAL();
(*graalEnv)->CallStaticVoidMethod(graalEnv, jGraalDisplayClass, jGraalNotifyInsetsMethod, top, right, bottom, left);
DETACH_GRAAL();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Gluon
* Copyright (c) 2020, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -33,8 +33,14 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class DalvikDisplayService {

private static final String TAG = Util.TAG;
Expand All @@ -53,6 +59,16 @@ public DalvikDisplayService(Activity activity) {
float yInches = metrics.heightPixels / metrics.ydpi;
float xInches = metrics.widthPixels / metrics.xdpi;
diagonalInches = Math.sqrt(xInches * xInches + yInches * yInches);

Window window = activity.getWindow();
View decorView = window.getDecorView();
ViewCompat.setOnApplyWindowInsetsListener(decorView, (v, insets) -> {
notifyInsets(insets, metrics.density);
return insets;
});
// Get initial insets
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decorView);
notifyInsets(insets, metrics.density);
}

private boolean isPhoneFactor() {
Expand All @@ -73,4 +89,15 @@ private boolean isScreenRound() {
}
return activity.getResources().getConfiguration().isScreenRound();
}

private void notifyInsets(WindowInsetsCompat insets, double density) {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
if (Util.isDebug()) {
Log.v(TAG, "Display got new insets: " + systemBars);
}
notifyInsets(systemBars.top / density, systemBars.right / density,
systemBars.bottom / density, systemBars.left / density);
}

private native void notifyInsets(double top, double right, double bottom, double left);
}
6 changes: 4 additions & 2 deletions modules/display/src/main/native/ios/Display.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022 Gluon
* Copyright (c) 2018, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -36,6 +36,8 @@
- (void) startObserver;
- (void) stopObserver;
- (NSString*) getNotch;
- (UIEdgeInsets) getInsets;
@end

void sendNotch();
void sendNotch();
void sendInsets(UIEdgeInsets insets);
41 changes: 33 additions & 8 deletions modules/display/src/main/native/ios/Display.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Gluon
* Copyright (c) 2016, 2025, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -48,6 +48,7 @@
// Display
jclass mat_jDisplayServiceClass;
jmethodID mat_jDisplayService_notifyDisplay = 0;
jmethodID mat_jDisplayService_notifyInsets = 0;
Display *_display;

bool iPhoneX;
Expand All @@ -63,6 +64,7 @@

mat_jDisplayServiceClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/gluonhq/attach/display/impl/IOSDisplayService"));
mat_jDisplayService_notifyDisplay = (*env)->GetStaticMethodID(env, mat_jDisplayServiceClass, "notifyDisplay", "(Ljava/lang/String;)V");
mat_jDisplayService_notifyInsets = (*env)->GetStaticMethodID(env, mat_jDisplayServiceClass, "notifyInsets", "(DDDD)V");

_display = [[Display alloc] init];
[_display isIPhoneX];
Expand Down Expand Up @@ -155,6 +157,13 @@ void sendNotch() {
(*env)->DeleteLocalRef(env, arg);
}

void sendInsets(UIEdgeInsets insets) {
if (debugAttach) {
AttachLog(@"Insets are %.3f %.3f %.3f %.3f", insets.top, insets.right, insets.bottom, insets.left);
}
(*env)->CallStaticVoidMethod(env, mat_jDisplayServiceClass, mat_jDisplayService_notifyInsets, insets.top, insets.right, insets.bottom, insets.left);
}

@implementation Display

NSString * GetDeviceModel(void)
Expand Down Expand Up @@ -190,7 +199,8 @@ - (void) isIPhoneX
@"iPhone14,2", @"iPhone14,3", @"iPhone14,4", @"iPhone14,5", // iPhone 13 Pro, 13 Pro Max, 13 Mini, 13
@"iPhone14,7", @"iPhone14,8", @"iPhone15,2", @"iPhone15,3", // iPhone 14, 14 Plus, 14 Pro, 14 Pro Max
@"iPhone15,4", @"iPhone15,5", @"iPhone16,1", @"iPhone16,2", // iPhone 15, 15 Plus, 15 Pro, 15 Pro Max
@"iPhone17,3", @"iPhone17,4", @"iPhone17,1", @"iPhone17,2", // iPhone 16, 16 Plus, 16 Pro, 16 Pro Max
@"iPhone17,3", @"iPhone17,4", @"iPhone17,1", @"iPhone17,2", @"iPhone17,5", // iPhone 16, 16 Plus, 16 Pro, 16 Pro Max, 16e
@"iPhone18,3", @"iPhone18,1", @"iPhone18,2", @"iPhone18,4", // iPhone 17, 17 Pro, 17 Pro Max, 17 Pro Air
];

if ([modelsWithNotch containsObject:GetDeviceModel()]) {
Expand Down Expand Up @@ -234,19 +244,17 @@ - (void) isIPhoneX

- (void) startObserver
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
sendInsets([self getInsets]);
if (iPhoneX)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
sendNotch();
}
}

- (void) stopObserver
{
if (iPhoneX)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (NSString*) getNotch
Expand All @@ -271,9 +279,26 @@ - (NSString*) getNotch
return value;
}

- (UIEdgeInsets) getInsets
{
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
if (!window) {
AttachLog(@"key window was nil");
return UIEdgeInsetsMake(0, 0, 0, 0);
}
return window.safeAreaInsets;
}
return UIEdgeInsetsMake(0, 0, 0, 0);
}

-(void)OrientationDidChange:(NSNotification*)notification
{
sendNotch();
sendInsets([self getInsets]);
if (iPhoneX)
{
sendNotch();
}
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"name" : "com.gluonhq.attach.display.impl.AndroidDisplayService",
"methods":[{"name":"notifyInsets","parameterTypes":["double", "double", "double", "double"] }]
}
]
Loading