You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Android: Stabilize camera lifecycle handling
- Pause camera feeds during lifecycle transitions to avoid crashes - Refresh camera metadata after rotation to keep orientation accurate
This commit is contained in:
@@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
#include "platform/android/display_server_android.h"
|
#include "platform/android/display_server_android.h"
|
||||||
|
#include "platform/android/java_godot_io_wrapper.h"
|
||||||
|
#include "platform/android/os_android.h"
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Helper functions
|
// Helper functions
|
||||||
@@ -93,9 +95,58 @@ CameraFeedAndroid::~CameraFeedAndroid() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CameraFeedAndroid::refresh_camera_metadata() {
|
||||||
|
ERR_FAIL_NULL_MSG(manager, vformat("Camera %s: Cannot refresh metadata, manager is null.", camera_id));
|
||||||
|
|
||||||
|
if (metadata != nullptr) {
|
||||||
|
ACameraMetadata_free(metadata);
|
||||||
|
metadata = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_status_t status = ACameraManager_getCameraCharacteristics(manager, camera_id.utf8().get_data(), &metadata);
|
||||||
|
if (status != ACAMERA_OK || metadata == nullptr) {
|
||||||
|
ERR_FAIL_MSG(vformat("Camera %s: Failed to refresh metadata (status: %d).", camera_id, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
ACameraMetadata_const_entry orientation_entry;
|
||||||
|
status = ACameraMetadata_getConstEntry(metadata, ACAMERA_SENSOR_ORIENTATION, &orientation_entry);
|
||||||
|
if (status == ACAMERA_OK) {
|
||||||
|
orientation = orientation_entry.data.i32[0];
|
||||||
|
print_verbose(vformat("Camera %s: Orientation updated to %d.", camera_id, orientation));
|
||||||
|
} else {
|
||||||
|
ERR_PRINT(vformat("Camera %s: Failed to get sensor orientation after refresh (status: %d).", camera_id, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
formats.clear();
|
||||||
|
_add_formats();
|
||||||
|
|
||||||
|
print_verbose(vformat("Camera %s: Metadata refreshed successfully.", camera_id));
|
||||||
|
}
|
||||||
|
|
||||||
void CameraFeedAndroid::_set_rotation() {
|
void CameraFeedAndroid::_set_rotation() {
|
||||||
|
if (!metadata) {
|
||||||
|
print_verbose(vformat("Camera %s: Metadata is null in _set_rotation, attempting refresh.", camera_id));
|
||||||
|
refresh_camera_metadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
float image_rotation = 0.0f;
|
||||||
|
std::optional<int> result;
|
||||||
|
|
||||||
|
if (metadata) {
|
||||||
|
CameraRotationParams params;
|
||||||
|
params.sensor_orientation = orientation;
|
||||||
|
params.camera_facing = (position == CameraFeed::FEED_FRONT) ? CameraFacing::FRONT : CameraFacing::BACK;
|
||||||
|
params.display_rotation = get_app_orientation();
|
||||||
|
|
||||||
|
result = calculate_rotation(params);
|
||||||
|
} else {
|
||||||
|
ERR_PRINT(vformat("Camera %s: Cannot update rotation, metadata unavailable after refresh, using fallback.", camera_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
image_rotation = static_cast<float>(result.value());
|
||||||
|
} else {
|
||||||
int display_rotation = DisplayServerAndroid::get_singleton()->get_display_rotation();
|
int display_rotation = DisplayServerAndroid::get_singleton()->get_display_rotation();
|
||||||
// reverse rotation
|
|
||||||
switch (display_rotation) {
|
switch (display_rotation) {
|
||||||
case 90:
|
case 90:
|
||||||
display_rotation = 270;
|
display_rotation = 270;
|
||||||
@@ -108,8 +159,11 @@ void CameraFeedAndroid::_set_rotation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int sign = position == CameraFeed::FEED_FRONT ? 1 : -1;
|
int sign = position == CameraFeed::FEED_FRONT ? 1 : -1;
|
||||||
float imageRotation = (orientation - display_rotation * sign + 360) % 360;
|
image_rotation = (orientation - display_rotation * sign + 360) % 360;
|
||||||
transform.set_rotation(real_t(Math::deg_to_rad(imageRotation)));
|
}
|
||||||
|
|
||||||
|
transform = Transform2D();
|
||||||
|
transform = transform.rotated(Math::deg_to_rad(image_rotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraFeedAndroid::_add_formats() {
|
void CameraFeedAndroid::_add_formats() {
|
||||||
@@ -142,7 +196,9 @@ void CameraFeedAndroid::_add_formats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CameraFeedAndroid::activate_feed() {
|
bool CameraFeedAndroid::activate_feed() {
|
||||||
ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating.");
|
ERR_FAIL_COND_V_MSG(formats.is_empty(), false, "No camera formats available.");
|
||||||
|
ERR_FAIL_INDEX_V_MSG(selected_format, formats.size(), false,
|
||||||
|
vformat("CameraFeed format needs to be set before activating. Selected format index: %d (formats size: %d)", selected_format, formats.size()));
|
||||||
if (is_active()) {
|
if (is_active()) {
|
||||||
deactivate_feed();
|
deactivate_feed();
|
||||||
};
|
};
|
||||||
@@ -278,11 +334,52 @@ Array CameraFeedAndroid::get_formats() const {
|
|||||||
|
|
||||||
CameraFeed::FeedFormat CameraFeedAndroid::get_format() const {
|
CameraFeed::FeedFormat CameraFeedAndroid::get_format() const {
|
||||||
CameraFeed::FeedFormat feed_format = {};
|
CameraFeed::FeedFormat feed_format = {};
|
||||||
return selected_format == -1 ? feed_format : formats[selected_format];
|
ERR_FAIL_INDEX_V_MSG(selected_format, formats.size(), feed_format,
|
||||||
|
vformat("Invalid format index: %d (formats size: %d)", selected_format, formats.size()));
|
||||||
|
return formats[selected_format];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraFeedAndroid::handle_pause() {
|
||||||
|
if (is_active()) {
|
||||||
|
was_active_before_pause = true;
|
||||||
|
print_verbose(vformat("Camera %s: Pausing (was active).", camera_id));
|
||||||
|
deactivate_feed();
|
||||||
|
} else {
|
||||||
|
was_active_before_pause = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraFeedAndroid::handle_resume() {
|
||||||
|
if (was_active_before_pause) {
|
||||||
|
print_verbose(vformat("Camera %s: Resuming.", camera_id));
|
||||||
|
activate_feed();
|
||||||
|
was_active_before_pause = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraFeedAndroid::handle_rotation_change() {
|
||||||
|
if (!is_active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_verbose(vformat("Camera %s: Handling rotation change.", camera_id));
|
||||||
|
refresh_camera_metadata();
|
||||||
|
_set_rotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraFeedAndroid::onImage(void *context, AImageReader *p_reader) {
|
void CameraFeedAndroid::onImage(void *context, AImageReader *p_reader) {
|
||||||
CameraFeedAndroid *feed = static_cast<CameraFeedAndroid *>(context);
|
CameraFeedAndroid *feed = static_cast<CameraFeedAndroid *>(context);
|
||||||
|
|
||||||
|
MutexLock lock(feed->callback_mutex);
|
||||||
|
|
||||||
|
if (!feed->is_active()) {
|
||||||
|
AImage *pending_image = nullptr;
|
||||||
|
if (AImageReader_acquireNextImage(p_reader, &pending_image) == AMEDIA_OK) {
|
||||||
|
AImage_delete(pending_image);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<uint8_t> data_y = feed->data_y;
|
Vector<uint8_t> data_y = feed->data_y;
|
||||||
Vector<uint8_t> data_uv = feed->data_uv;
|
Vector<uint8_t> data_uv = feed->data_uv;
|
||||||
Ref<Image> image_y = feed->image_y;
|
Ref<Image> image_y = feed->image_y;
|
||||||
@@ -363,8 +460,17 @@ void CameraFeedAndroid::onImage(void *context, AImageReader *p_reader) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotation
|
if (!feed->formats.is_empty()) {
|
||||||
|
if (feed->metadata != nullptr) {
|
||||||
feed->_set_rotation();
|
feed->_set_rotation();
|
||||||
|
} else {
|
||||||
|
print_verbose(vformat("Camera %s: Metadata invalidated in onImage, attempting refresh.", feed->camera_id));
|
||||||
|
feed->refresh_camera_metadata();
|
||||||
|
if (feed->metadata != nullptr && !feed->formats.is_empty()) {
|
||||||
|
feed->_set_rotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Release image
|
// Release image
|
||||||
AImage_delete(image);
|
AImage_delete(image);
|
||||||
@@ -389,9 +495,16 @@ void CameraFeedAndroid::deactivate_feed() {
|
|||||||
session = nullptr;
|
session = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request != nullptr) {
|
if (reader != nullptr) {
|
||||||
ACaptureRequest_free(request);
|
AImageReader_setImageListener(reader, nullptr);
|
||||||
request = nullptr;
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLock lock(callback_mutex);
|
||||||
|
|
||||||
|
if (device != nullptr) {
|
||||||
|
ACameraDevice_close(device);
|
||||||
|
device = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader != nullptr) {
|
if (reader != nullptr) {
|
||||||
@@ -399,9 +512,10 @@ void CameraFeedAndroid::deactivate_feed() {
|
|||||||
reader = nullptr;
|
reader = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device != nullptr) {
|
if (request != nullptr) {
|
||||||
ACameraDevice_close(device);
|
ACaptureRequest_free(request);
|
||||||
device = nullptr;
|
request = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +619,75 @@ void CameraAndroid::set_monitoring_feeds(bool p_monitoring_feeds) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CameraAndroid::handle_pause() {
|
||||||
|
for (int i = 0; i < feeds.size(); i++) {
|
||||||
|
Ref<CameraFeedAndroid> feed = feeds[i];
|
||||||
|
if (feed.is_valid()) {
|
||||||
|
feed->handle_pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraAndroid::handle_resume() {
|
||||||
|
for (int i = 0; i < feeds.size(); i++) {
|
||||||
|
Ref<CameraFeedAndroid> feed = feeds[i];
|
||||||
|
if (feed.is_valid()) {
|
||||||
|
feed->handle_resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraAndroid::handle_rotation_change() {
|
||||||
|
for (int i = 0; i < feeds.size(); i++) {
|
||||||
|
Ref<CameraFeedAndroid> feed = feeds[i];
|
||||||
|
if (feed.is_valid()) {
|
||||||
|
feed->handle_rotation_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CameraAndroid::~CameraAndroid() {
|
CameraAndroid::~CameraAndroid() {
|
||||||
remove_all_feeds();
|
remove_all_feeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<int> CameraFeedAndroid::calculate_rotation(const CameraRotationParams &p_params) {
|
||||||
|
if (p_params.sensor_orientation < 0 || p_params.sensor_orientation > 270 || p_params.sensor_orientation % 90 != 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rotation_angle = p_params.sensor_orientation - p_params.display_rotation;
|
||||||
|
return normalize_angle(rotation_angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CameraFeedAndroid::normalize_angle(int p_angle) {
|
||||||
|
while (p_angle < 0) {
|
||||||
|
p_angle += 360;
|
||||||
|
}
|
||||||
|
return p_angle % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CameraFeedAndroid::get_display_rotation() {
|
||||||
|
return DisplayServerAndroid::get_singleton()->get_display_rotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CameraFeedAndroid::get_app_orientation() {
|
||||||
|
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||||
|
ERR_FAIL_NULL_V(godot_io_java, 0);
|
||||||
|
|
||||||
|
int orientation = godot_io_java->get_screen_orientation();
|
||||||
|
switch (orientation) {
|
||||||
|
case 0: // SCREEN_LANDSCAPE
|
||||||
|
return 90;
|
||||||
|
case 1: // SCREEN_PORTRAIT
|
||||||
|
return 0;
|
||||||
|
case 2: // SCREEN_REVERSE_LANDSCAPE
|
||||||
|
return 270;
|
||||||
|
case 3: // SCREEN_REVERSE_PORTRAIT
|
||||||
|
return 180;
|
||||||
|
case 4: // SCREEN_SENSOR_LANDSCAPE
|
||||||
|
case 5: // SCREEN_SENSOR_PORTRAIT
|
||||||
|
case 6: // SCREEN_SENSOR
|
||||||
|
default:
|
||||||
|
return get_display_rotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,18 @@
|
|||||||
#include <camera/NdkCameraManager.h>
|
#include <camera/NdkCameraManager.h>
|
||||||
#include <camera/NdkCameraMetadataTags.h>
|
#include <camera/NdkCameraMetadataTags.h>
|
||||||
#include <media/NdkImageReader.h>
|
#include <media/NdkImageReader.h>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
enum class CameraFacing {
|
||||||
|
BACK = 0,
|
||||||
|
FRONT = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraRotationParams {
|
||||||
|
int sensor_orientation;
|
||||||
|
CameraFacing camera_facing;
|
||||||
|
int display_rotation;
|
||||||
|
};
|
||||||
|
|
||||||
class CameraFeedAndroid : public CameraFeed {
|
class CameraFeedAndroid : public CameraFeed {
|
||||||
GDSOFTCLASS(CameraFeedAndroid, CameraFeed);
|
GDSOFTCLASS(CameraFeedAndroid, CameraFeed);
|
||||||
@@ -56,9 +68,16 @@ private:
|
|||||||
AImageReader *reader = nullptr;
|
AImageReader *reader = nullptr;
|
||||||
ACameraCaptureSession *session = nullptr;
|
ACameraCaptureSession *session = nullptr;
|
||||||
ACaptureRequest *request = nullptr;
|
ACaptureRequest *request = nullptr;
|
||||||
|
Mutex callback_mutex;
|
||||||
|
bool was_active_before_pause = false;
|
||||||
|
|
||||||
void _add_formats();
|
void _add_formats();
|
||||||
void _set_rotation();
|
void _set_rotation();
|
||||||
|
void refresh_camera_metadata();
|
||||||
|
static std::optional<int> calculate_rotation(const CameraRotationParams &p_params);
|
||||||
|
static int normalize_angle(int p_angle);
|
||||||
|
static int get_display_rotation();
|
||||||
|
static int get_app_orientation();
|
||||||
|
|
||||||
static void onError(void *context, ACameraDevice *p_device, int error);
|
static void onError(void *context, ACameraDevice *p_device, int error);
|
||||||
static void onDisconnected(void *context, ACameraDevice *p_device);
|
static void onDisconnected(void *context, ACameraDevice *p_device);
|
||||||
@@ -74,6 +93,9 @@ public:
|
|||||||
bool set_format(int p_index, const Dictionary &p_parameters) override;
|
bool set_format(int p_index, const Dictionary &p_parameters) override;
|
||||||
Array get_formats() const override;
|
Array get_formats() const override;
|
||||||
FeedFormat get_format() const override;
|
FeedFormat get_format() const override;
|
||||||
|
void handle_pause();
|
||||||
|
void handle_resume();
|
||||||
|
void handle_rotation_change();
|
||||||
|
|
||||||
CameraFeedAndroid(ACameraManager *manager, ACameraMetadata *metadata, const char *id,
|
CameraFeedAndroid(ACameraManager *manager, ACameraMetadata *metadata, const char *id,
|
||||||
CameraFeed::FeedPosition position, int32_t orientation);
|
CameraFeed::FeedPosition position, int32_t orientation);
|
||||||
@@ -91,6 +113,9 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void set_monitoring_feeds(bool p_monitoring_feeds) override;
|
void set_monitoring_feeds(bool p_monitoring_feeds) override;
|
||||||
|
void handle_pause();
|
||||||
|
void handle_resume();
|
||||||
|
void handle_rotation_change();
|
||||||
|
|
||||||
~CameraAndroid();
|
~CameraAndroid();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ class Godot private constructor(val context: Context) {
|
|||||||
*/
|
*/
|
||||||
private var renderViewInitialized = false
|
private var renderViewInitialized = false
|
||||||
private var primaryHost: GodotHost? = null
|
private var primaryHost: GodotHost? = null
|
||||||
|
private var currentConfig = context.resources.configuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks whether we're in the RESUMED lifecycle state.
|
* Tracks whether we're in the RESUMED lifecycle state.
|
||||||
@@ -757,6 +758,13 @@ class Godot private constructor(val context: Context) {
|
|||||||
darkMode = newDarkMode
|
darkMode = newDarkMode
|
||||||
GodotLib.onNightModeChanged()
|
GodotLib.onNightModeChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentConfig.orientation != newConfig.orientation) {
|
||||||
|
runOnRenderThread {
|
||||||
|
GodotLib.onScreenRotationChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentConfig = newConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -295,6 +295,11 @@ public class GodotLib {
|
|||||||
*/
|
*/
|
||||||
public static native void onRendererPaused();
|
public static native void onRendererPaused();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the screen orientation changes.
|
||||||
|
*/
|
||||||
|
static native void onScreenRotationChange();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if input must be dispatched from the render thread. If false, input is
|
* @return true if input must be dispatched from the render thread. If false, input is
|
||||||
* dispatched from the UI thread.
|
* dispatched from the UI thread.
|
||||||
|
|||||||
@@ -52,6 +52,13 @@
|
|||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
#include "servers/rendering/rendering_server.h"
|
#include "servers/rendering/rendering_server.h"
|
||||||
|
|
||||||
|
#include "modules/modules_enabled.gen.h" // For camera.
|
||||||
|
|
||||||
|
#ifdef MODULE_CAMERA_ENABLED
|
||||||
|
#include "modules/camera/camera_android.h"
|
||||||
|
#include "servers/camera/camera_server.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef XR_DISABLED
|
#ifndef XR_DISABLED
|
||||||
#include "servers/xr/xr_server.h"
|
#include "servers/xr/xr_server.h"
|
||||||
#endif // XR_DISABLED
|
#endif // XR_DISABLED
|
||||||
@@ -593,6 +600,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNI
|
|||||||
|
|
||||||
// We force redraw to ensure we render at least once when resuming the app.
|
// We force redraw to ensure we render at least once when resuming the app.
|
||||||
Main::force_redraw();
|
Main::force_redraw();
|
||||||
|
#ifdef MODULE_CAMERA_ENABLED
|
||||||
|
CameraAndroid *camera_android = Object::cast_to<CameraAndroid>(CameraServer::get_singleton());
|
||||||
|
if (camera_android) {
|
||||||
|
camera_android->handle_resume();
|
||||||
|
}
|
||||||
|
#endif // MODULE_CAMERA_ENABLED
|
||||||
if (os_android->get_main_loop()) {
|
if (os_android->get_main_loop()) {
|
||||||
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
|
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
|
||||||
}
|
}
|
||||||
@@ -603,11 +616,31 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MODULE_CAMERA_ENABLED
|
||||||
|
CameraAndroid *camera_android = Object::cast_to<CameraAndroid>(CameraServer::get_singleton());
|
||||||
|
if (camera_android) {
|
||||||
|
camera_android->handle_pause();
|
||||||
|
}
|
||||||
|
#endif // MODULE_CAMERA_ENABLED
|
||||||
|
|
||||||
if (os_android->get_main_loop()) {
|
if (os_android->get_main_loop()) {
|
||||||
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
|
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onScreenRotationChange(JNIEnv *env, jclass clazz) {
|
||||||
|
if (step.get() <= STEP_SETUP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MODULE_CAMERA_ENABLED
|
||||||
|
CameraAndroid *camera_android = Object::cast_to<CameraAndroid>(CameraServer::get_singleton());
|
||||||
|
if (camera_android) {
|
||||||
|
camera_android->handle_rotation_change();
|
||||||
|
}
|
||||||
|
#endif // MODULE_CAMERA_ENABLED
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz) {
|
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz) {
|
||||||
Input *input = Input::get_singleton();
|
Input *input = Input::get_singleton();
|
||||||
if (input) {
|
if (input) {
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConne
|
|||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onScreenRotationChange(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
|
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getProjectResourceDir(JNIEnv *env, jclass clazz);
|
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getProjectResourceDir(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_isEditorHint(JNIEnv *env, jclass clazz);
|
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_isEditorHint(JNIEnv *env, jclass clazz);
|
||||||
|
|||||||
Reference in New Issue
Block a user