1089 lines
36 KiB
C++
1089 lines
36 KiB
C++
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
|
|
|
#if PLATFORM_ANDROID
|
|
|
|
#include "AndroidPlatform.h"
|
|
#include "AndroidWindow.h"
|
|
#include "AndroidFileSystem.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Core/Types/Guid.h"
|
|
#include "Engine/Core/Types/String.h"
|
|
#include "Engine/Core/Collections/HashFunctions.h"
|
|
#include "Engine/Core/Collections/Array.h"
|
|
#include "Engine/Core/Math/Math.h"
|
|
#include "Engine/Core/Math/Rectangle.h"
|
|
#include "Engine/Platform/CPUInfo.h"
|
|
#include "Engine/Platform/MemoryStats.h"
|
|
#include "Engine/Platform/MessageBox.h"
|
|
#include "Engine/Platform/WindowsManager.h"
|
|
#include "Engine/Utilities/StringConverter.h"
|
|
#include "Engine/Graphics/GPUSwapChain.h"
|
|
#include "Engine/Threading/Threading.h"
|
|
#include "Engine/Engine/Engine.h"
|
|
#include "Engine/Input/Input.h"
|
|
#include "Engine/Input/Gamepad.h"
|
|
#include "Engine/Input/Keyboard.h"
|
|
#include "Engine/Input/Mouse.h"
|
|
#include <sys/resource.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/time.h>
|
|
#include <sys/file.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/unistd.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
#include <sched.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <net/if.h>
|
|
#include <errno.h>
|
|
#include <locale.h>
|
|
#include <inttypes.h>
|
|
#include <pwd.h>
|
|
#include <jni.h>
|
|
#if !BUILD_RELEASE
|
|
#include <android/log.h>
|
|
#endif
|
|
#include "Engine/Main/Android/android_native_app_glue.h"
|
|
#include <android/window.h>
|
|
#include <android/versioning.h>
|
|
|
|
struct AndroidKeyEventType
|
|
{
|
|
uint32_t KeyCode;
|
|
KeyboardKeys KeyboardKey;
|
|
GamepadButton GamepadButton;
|
|
};
|
|
|
|
AndroidKeyEventType AndroidKeyEventTypes[] =
|
|
{
|
|
{ AKEYCODE_HOME, KeyboardKeys::Home, GamepadButton::Start },
|
|
{ AKEYCODE_BACK, KeyboardKeys::None, GamepadButton::Back },
|
|
{ AKEYCODE_0, KeyboardKeys::Alpha0, GamepadButton::None },
|
|
{ AKEYCODE_1, KeyboardKeys::Alpha1, GamepadButton::None },
|
|
{ AKEYCODE_2, KeyboardKeys::Alpha2, GamepadButton::None },
|
|
{ AKEYCODE_3, KeyboardKeys::Alpha3, GamepadButton::None },
|
|
{ AKEYCODE_4, KeyboardKeys::Alpha4, GamepadButton::None },
|
|
{ AKEYCODE_5, KeyboardKeys::Alpha5, GamepadButton::None },
|
|
{ AKEYCODE_6, KeyboardKeys::Alpha6, GamepadButton::None },
|
|
{ AKEYCODE_7, KeyboardKeys::Alpha7, GamepadButton::None },
|
|
{ AKEYCODE_8, KeyboardKeys::Alpha8, GamepadButton::None },
|
|
{ AKEYCODE_9, KeyboardKeys::Alpha9, GamepadButton::None },
|
|
{ AKEYCODE_STAR, KeyboardKeys::NumpadMultiply, GamepadButton::None },
|
|
{ AKEYCODE_DPAD_UP, KeyboardKeys::None, GamepadButton::DPadUp },
|
|
{ AKEYCODE_DPAD_DOWN, KeyboardKeys::None, GamepadButton::DPadDown },
|
|
{ AKEYCODE_DPAD_LEFT, KeyboardKeys::None, GamepadButton::DPadLeft },
|
|
{ AKEYCODE_DPAD_RIGHT, KeyboardKeys::None, GamepadButton::DPadRight },
|
|
{ AKEYCODE_VOLUME_UP, KeyboardKeys::VolumeUp, GamepadButton::None },
|
|
{ AKEYCODE_VOLUME_DOWN, KeyboardKeys::VolumeDown, GamepadButton::None },
|
|
{ AKEYCODE_CLEAR, KeyboardKeys::Clear, GamepadButton::None },
|
|
{ AKEYCODE_A, KeyboardKeys::A, GamepadButton::None },
|
|
{ AKEYCODE_B, KeyboardKeys::B, GamepadButton::None },
|
|
{ AKEYCODE_C, KeyboardKeys::C, GamepadButton::None },
|
|
{ AKEYCODE_D, KeyboardKeys::D, GamepadButton::None },
|
|
{ AKEYCODE_E, KeyboardKeys::E, GamepadButton::None },
|
|
{ AKEYCODE_F, KeyboardKeys::F, GamepadButton::None },
|
|
{ AKEYCODE_G, KeyboardKeys::G, GamepadButton::None },
|
|
{ AKEYCODE_H, KeyboardKeys::H, GamepadButton::None },
|
|
{ AKEYCODE_I, KeyboardKeys::I, GamepadButton::None },
|
|
{ AKEYCODE_J, KeyboardKeys::J, GamepadButton::None },
|
|
{ AKEYCODE_K, KeyboardKeys::K, GamepadButton::None },
|
|
{ AKEYCODE_L, KeyboardKeys::L, GamepadButton::None },
|
|
{ AKEYCODE_M, KeyboardKeys::M, GamepadButton::None },
|
|
{ AKEYCODE_N, KeyboardKeys::N, GamepadButton::None },
|
|
{ AKEYCODE_O, KeyboardKeys::O, GamepadButton::None },
|
|
{ AKEYCODE_P, KeyboardKeys::P, GamepadButton::None },
|
|
{ AKEYCODE_Q, KeyboardKeys::Q, GamepadButton::None },
|
|
{ AKEYCODE_R, KeyboardKeys::R, GamepadButton::None },
|
|
{ AKEYCODE_S, KeyboardKeys::S, GamepadButton::None },
|
|
{ AKEYCODE_T, KeyboardKeys::T, GamepadButton::None },
|
|
{ AKEYCODE_U, KeyboardKeys::U, GamepadButton::None },
|
|
{ AKEYCODE_V, KeyboardKeys::V, GamepadButton::None },
|
|
{ AKEYCODE_W, KeyboardKeys::W, GamepadButton::None },
|
|
{ AKEYCODE_Y, KeyboardKeys::Y, GamepadButton::None },
|
|
{ AKEYCODE_Z, KeyboardKeys::Z, GamepadButton::None },
|
|
{ AKEYCODE_COMMA, KeyboardKeys::Comma, GamepadButton::None },
|
|
{ AKEYCODE_PERIOD, KeyboardKeys::Period, GamepadButton::None },
|
|
{ AKEYCODE_ALT_LEFT, KeyboardKeys::Alt, GamepadButton::None },
|
|
{ AKEYCODE_ALT_RIGHT, KeyboardKeys::Alt, GamepadButton::None },
|
|
{ AKEYCODE_SHIFT_LEFT, KeyboardKeys::Shift, GamepadButton::None },
|
|
{ AKEYCODE_SHIFT_RIGHT, KeyboardKeys::Shift, GamepadButton::None },
|
|
{ AKEYCODE_TAB, KeyboardKeys::Tab, GamepadButton::None },
|
|
{ AKEYCODE_SPACE, KeyboardKeys::Spacebar, GamepadButton::None },
|
|
{ AKEYCODE_ENTER, KeyboardKeys::Return, GamepadButton::None },
|
|
{ AKEYCODE_DEL, KeyboardKeys::Delete, GamepadButton::None },
|
|
{ AKEYCODE_GRAVE, KeyboardKeys::BackQuote, GamepadButton::None },
|
|
{ AKEYCODE_MINUS, KeyboardKeys::Minus, GamepadButton::None },
|
|
{ AKEYCODE_PLUS, KeyboardKeys::Plus, GamepadButton::None },
|
|
{ AKEYCODE_LEFT_BRACKET, KeyboardKeys::LeftBracket, GamepadButton::None },
|
|
{ AKEYCODE_RIGHT_BRACKET, KeyboardKeys::RightBracket, GamepadButton::None },
|
|
{ AKEYCODE_BACKSLASH, KeyboardKeys::Backslash, GamepadButton::None },
|
|
{ AKEYCODE_SEMICOLON, KeyboardKeys::Colon, GamepadButton::None },
|
|
{ AKEYCODE_SLASH, KeyboardKeys::Slash, GamepadButton::None },
|
|
{ AKEYCODE_NUM, KeyboardKeys::Numlock, GamepadButton::None },
|
|
{ AKEYCODE_MENU, KeyboardKeys::LeftMenu, GamepadButton::None },
|
|
{ AKEYCODE_MEDIA_PLAY_PAUSE, KeyboardKeys::MediaPlayPause, GamepadButton::None },
|
|
{ AKEYCODE_MEDIA_STOP, KeyboardKeys::MediaStop, GamepadButton::None },
|
|
{ AKEYCODE_MEDIA_NEXT, KeyboardKeys::MediaNextTrack, GamepadButton::None },
|
|
{ AKEYCODE_MEDIA_PREVIOUS, KeyboardKeys::MediaPrevTrack, GamepadButton::None },
|
|
{ AKEYCODE_MUTE, KeyboardKeys::VolumeMute, GamepadButton::None },
|
|
{ AKEYCODE_PAGE_UP, KeyboardKeys::PageUp, GamepadButton::None },
|
|
{ AKEYCODE_PAGE_DOWN, KeyboardKeys::PageDown, GamepadButton::None },
|
|
{ AKEYCODE_BUTTON_A, KeyboardKeys::None, GamepadButton::A },
|
|
{ AKEYCODE_BUTTON_A, KeyboardKeys::None, GamepadButton::B },
|
|
{ AKEYCODE_BUTTON_X, KeyboardKeys::None, GamepadButton::X },
|
|
{ AKEYCODE_BUTTON_Y, KeyboardKeys::None, GamepadButton::Y },
|
|
{ AKEYCODE_BUTTON_L1, KeyboardKeys::None, GamepadButton::LeftShoulder },
|
|
{ AKEYCODE_BUTTON_R1, KeyboardKeys::None, GamepadButton::RightShoulder },
|
|
{ AKEYCODE_BUTTON_L2, KeyboardKeys::None, GamepadButton::LeftTrigger },
|
|
{ AKEYCODE_BUTTON_R2, KeyboardKeys::None, GamepadButton::RightTrigger },
|
|
{ AKEYCODE_BUTTON_THUMBL, KeyboardKeys::None, GamepadButton::LeftThumb },
|
|
{ AKEYCODE_BUTTON_THUMBR, KeyboardKeys::None, GamepadButton::RightThumb },
|
|
{ AKEYCODE_BUTTON_START, KeyboardKeys::None, GamepadButton::Start },
|
|
{ AKEYCODE_BUTTON_SELECT, KeyboardKeys::None, GamepadButton::Start },
|
|
{ AKEYCODE_ESCAPE, KeyboardKeys::Escape, GamepadButton::None },
|
|
{ AKEYCODE_CTRL_LEFT, KeyboardKeys::Control, GamepadButton::None },
|
|
{ AKEYCODE_CTRL_RIGHT, KeyboardKeys::Control, GamepadButton::None },
|
|
{ AKEYCODE_SCROLL_LOCK, KeyboardKeys::Scroll, GamepadButton::None },
|
|
{ AKEYCODE_BREAK, KeyboardKeys::Pause, GamepadButton::None },
|
|
{ AKEYCODE_MOVE_HOME, KeyboardKeys::Home, GamepadButton::None },
|
|
{ AKEYCODE_MOVE_END, KeyboardKeys::End, GamepadButton::None },
|
|
{ AKEYCODE_INSERT, KeyboardKeys::Insert, GamepadButton::None },
|
|
{ AKEYCODE_MEDIA_EJECT, KeyboardKeys::LaunchMediaSelect, GamepadButton::None },
|
|
{ AKEYCODE_F1, KeyboardKeys::F1, GamepadButton::None },
|
|
{ AKEYCODE_F2, KeyboardKeys::F2, GamepadButton::None },
|
|
{ AKEYCODE_F3, KeyboardKeys::F3, GamepadButton::None },
|
|
{ AKEYCODE_F4, KeyboardKeys::F4, GamepadButton::None },
|
|
{ AKEYCODE_F5, KeyboardKeys::F5, GamepadButton::None },
|
|
{ AKEYCODE_F6, KeyboardKeys::F6, GamepadButton::None },
|
|
{ AKEYCODE_F7, KeyboardKeys::F7, GamepadButton::None },
|
|
{ AKEYCODE_F8, KeyboardKeys::F8, GamepadButton::None },
|
|
{ AKEYCODE_F9, KeyboardKeys::F9, GamepadButton::None },
|
|
{ AKEYCODE_F10, KeyboardKeys::F10, GamepadButton::None },
|
|
{ AKEYCODE_F11, KeyboardKeys::F11, GamepadButton::None },
|
|
{ AKEYCODE_F12, KeyboardKeys::F12, GamepadButton::None },
|
|
{ AKEYCODE_NUM_LOCK, KeyboardKeys::Numlock, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_0, KeyboardKeys::Numpad0, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_1, KeyboardKeys::Numpad1, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_2, KeyboardKeys::Numpad2, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_3, KeyboardKeys::Numpad3, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_4, KeyboardKeys::Numpad4, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_5, KeyboardKeys::Numpad5, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_6, KeyboardKeys::Numpad6, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_7, KeyboardKeys::Numpad7, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_8, KeyboardKeys::Numpad8, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_9, KeyboardKeys::Numpad9, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_DIVIDE, KeyboardKeys::NumpadDivide, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_MULTIPLY, KeyboardKeys::NumpadMultiply, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_SUBTRACT, KeyboardKeys::NumpadSubtract, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_ADD, KeyboardKeys::NumpadAdd, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_DOT, KeyboardKeys::NumpadSeparator, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_COMMA, KeyboardKeys::NumpadDecimal, GamepadButton::None },
|
|
{ AKEYCODE_NUMPAD_ENTER, KeyboardKeys::Return, GamepadButton::None },
|
|
{ AKEYCODE_VOLUME_MUTE, KeyboardKeys::VolumeMute, GamepadButton::None },
|
|
{ AKEYCODE_HELP, KeyboardKeys::Help, GamepadButton::None },
|
|
{ AKEYCODE_KANA, KeyboardKeys::Kana, GamepadButton::None },
|
|
};
|
|
|
|
class AndroidKeyboard : public Keyboard
|
|
{
|
|
public:
|
|
|
|
explicit AndroidKeyboard()
|
|
: Keyboard()
|
|
{
|
|
}
|
|
};
|
|
|
|
class AndroidDeviceGamepad : public Gamepad
|
|
{
|
|
public:
|
|
|
|
explicit AndroidDeviceGamepad()
|
|
: Gamepad(Guid(0, 0, 0, 1), TEXT("Android"))
|
|
{
|
|
CachedState.Clear();
|
|
}
|
|
|
|
~AndroidDeviceGamepad()
|
|
{
|
|
}
|
|
|
|
State CachedState;
|
|
|
|
public:
|
|
|
|
void SetVibration(const GamepadVibrationState& state) override;
|
|
|
|
bool UpdateState() override
|
|
{
|
|
Platform::MemoryCopy(&_state, &CachedState, sizeof(_state));
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class AndroidTouchScreen : public InputDevice
|
|
{
|
|
public:
|
|
|
|
explicit AndroidTouchScreen()
|
|
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Android Touch Screen"))
|
|
{
|
|
}
|
|
|
|
void OnTouch(EventType type, float x, float y, int32 pointerId)
|
|
{
|
|
Event& e = _queue.AddOne();
|
|
e.Type = type;
|
|
e.Target = nullptr;
|
|
e.TouchData.Position.X = x;
|
|
e.TouchData.Position.Y = y;
|
|
e.TouchData.PointerId = pointerId;
|
|
}
|
|
};
|
|
|
|
namespace
|
|
{
|
|
android_app* App = nullptr;
|
|
ANativeWindow* AppWindow = nullptr;
|
|
CPUInfo AndroidCpu;
|
|
int ClockSource;
|
|
bool HasFocus = false;
|
|
bool IsStarted = false;
|
|
bool IsPaused = true;
|
|
bool IsVibrating = false;
|
|
int32 ScreenWidth = 0, ScreenHeight = 0;
|
|
Guid DeviceId;
|
|
String AppPackageName, DeviceManufacturer, DeviceModel, DeviceBuildNumber;
|
|
String SystemVersion, SystemLanguage, CacheDir, ExecutablePath;
|
|
byte MacAddress[6];
|
|
AndroidKeyboard KeyboardImpl;
|
|
AndroidDeviceGamepad* GamepadImpl;
|
|
AndroidTouchScreen* TouchScreenImpl;
|
|
ScreenOrientationType Orientation;
|
|
|
|
void UnixGetMacAddress(byte result[6])
|
|
{
|
|
struct ifreq ifr;
|
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
strncpy((char*)ifr.ifr_name, "eth0", IFNAMSIZ - 1);
|
|
ioctl(fd, SIOCGIFHWADDR, &ifr);
|
|
close(fd);
|
|
Platform::MemoryCopy(result, ifr.ifr_hwaddr.sa_data, 6);
|
|
}
|
|
|
|
ScreenOrientationType getOrientation()
|
|
{
|
|
JNIEnv* jni;
|
|
App->activity->vm->AttachCurrentThread(&jni, nullptr);
|
|
ASSERT(jni);
|
|
const jclass clazz = jni->GetObjectClass(App->activity->clazz);
|
|
ASSERT(clazz);
|
|
const jmethodID methodID = jni->GetMethodID(clazz, "getRotation", "()I");
|
|
ASSERT(methodID);
|
|
const jint rotation = jni->CallIntMethod(App->activity->clazz, methodID);
|
|
App->activity->vm->DetachCurrentThread();
|
|
ScreenOrientationType orientation;
|
|
switch (rotation)
|
|
{
|
|
case 0:
|
|
orientation = ScreenOrientationType::Portrait;
|
|
break;
|
|
case 1:
|
|
orientation = ScreenOrientationType::LandscapeLeft;
|
|
break;
|
|
case 2:
|
|
orientation = ScreenOrientationType::PortraitUpsideDown;
|
|
break;
|
|
case 3:
|
|
orientation = ScreenOrientationType::LandscapeRight;
|
|
break;
|
|
default:
|
|
orientation = ScreenOrientationType::Unknown;
|
|
}
|
|
return orientation;
|
|
}
|
|
|
|
Vector2 GetWindowSize()
|
|
{
|
|
const float width = (float)ANativeWindow_getWidth(AppWindow);
|
|
const float height = (float)ANativeWindow_getHeight(AppWindow);
|
|
return Vector2(width, height);
|
|
}
|
|
|
|
void UpdateOrientation()
|
|
{
|
|
Orientation = getOrientation();
|
|
if (AppWindow && Engine::MainWindow)
|
|
{
|
|
Engine::MainWindow->SetClientSize(GetWindowSize());
|
|
}
|
|
}
|
|
|
|
void OnAppCmd(android_app* app, int32_t cmd)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case APP_CMD_START:
|
|
LOG(Info, "[Android] APP_CMD_START");
|
|
IsStarted = true;
|
|
UpdateOrientation();
|
|
break;
|
|
case APP_CMD_RESUME:
|
|
LOG(Info, "[Android] APP_CMD_RESUME");
|
|
IsPaused = false;
|
|
UpdateOrientation();
|
|
break;
|
|
case APP_CMD_PAUSE:
|
|
LOG(Info, "[Android] APP_CMD_PAUSE");
|
|
IsPaused = true;
|
|
break;
|
|
case APP_CMD_STOP:
|
|
LOG(Info, "[Android] APP_CMD_STOP");
|
|
IsStarted = false;
|
|
//Engine::OnExit();
|
|
break;
|
|
case APP_CMD_DESTROY:
|
|
LOG(Info, "[Android] APP_CMD_DESTROY");
|
|
break;
|
|
case APP_CMD_INIT_WINDOW:
|
|
LOG(Info, "[Android] APP_CMD_INIT_WINDOW");
|
|
AppWindow = app->window;
|
|
ANativeWindow_acquire(AppWindow);
|
|
UpdateOrientation();
|
|
if (Engine::MainWindow)
|
|
{
|
|
Engine::MainWindow->InitSwapChain();
|
|
}
|
|
break;
|
|
case APP_CMD_WINDOW_RESIZED:
|
|
LOG(Info, "[Android] APP_CMD_WINDOW_RESIZED");
|
|
if (Engine::MainWindow)
|
|
{
|
|
Engine::MainWindow->SetClientSize(GetWindowSize());
|
|
}
|
|
break;
|
|
case APP_CMD_TERM_WINDOW:
|
|
LOG(Info, "[Android] APP_CMD_TERM_WINDOW");
|
|
if (Engine::MainWindow && Engine::MainWindow->GetSwapChain())
|
|
{
|
|
Engine::MainWindow->GetSwapChain()->ReleaseGPU();
|
|
}
|
|
ANativeWindow_release(AppWindow);
|
|
AppWindow = nullptr;
|
|
break;
|
|
case APP_CMD_CONFIG_CHANGED:
|
|
{
|
|
LOG(Info, "[Android] APP_CMD_CONFIG_CHANGED");
|
|
UpdateOrientation();
|
|
break;
|
|
}
|
|
case APP_CMD_GAINED_FOCUS:
|
|
LOG(Info, "[Android] APP_CMD_GAINED_FOCUS");
|
|
HasFocus = true;
|
|
if (Engine::MainWindow)
|
|
Engine::MainWindow->OnGotFocus();
|
|
break;
|
|
case APP_CMD_LOST_FOCUS:
|
|
LOG(Info, "[Android] APP_CMD_LOST_FOCUS");
|
|
HasFocus = false;
|
|
if (Engine::MainWindow)
|
|
Engine::MainWindow->OnLostFocus();
|
|
break;
|
|
#if !BUILD_RELEASE
|
|
default:
|
|
__android_log_print(ANDROID_LOG_INFO, "Flax", "App Cmd not handled: %d", cmd);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int32_t OnAppInput(android_app* app, AInputEvent* inputEvent)
|
|
{
|
|
switch (AInputEvent_getType(inputEvent))
|
|
{
|
|
case AINPUT_EVENT_TYPE_MOTION:
|
|
{
|
|
const int32_t action = AMotionEvent_getAction(inputEvent);
|
|
switch (action & AMOTION_EVENT_ACTION_MASK)
|
|
{
|
|
case AMOTION_EVENT_ACTION_DOWN:
|
|
{
|
|
const int32 pointerCount = (int32)AMotionEvent_getPointerCount(inputEvent);
|
|
for (int32 i = 0; i < pointerCount; i++)
|
|
{
|
|
const int32 pointerId = AMotionEvent_getPointerId(inputEvent, i);
|
|
const float x = AMotionEvent_getX(inputEvent, i);
|
|
const float y = AMotionEvent_getY(inputEvent, i);
|
|
TouchScreenImpl->OnTouch(InputDevice::EventType::TouchDown, x, y, pointerId);
|
|
}
|
|
break;
|
|
}
|
|
case AMOTION_EVENT_ACTION_UP:
|
|
{
|
|
const int32 pointerCount = (int32)AMotionEvent_getPointerCount(inputEvent);
|
|
for (int32 i = 0; i < pointerCount; i++)
|
|
{
|
|
const int32 pointerId = AMotionEvent_getPointerId(inputEvent, i);
|
|
const float x = AMotionEvent_getX(inputEvent, i);
|
|
const float y = AMotionEvent_getY(inputEvent, i);
|
|
TouchScreenImpl->OnTouch(InputDevice::EventType::TouchUp, x, y, pointerId);
|
|
}
|
|
break;
|
|
}
|
|
case AMOTION_EVENT_ACTION_MOVE:
|
|
{
|
|
const int32 pointerCount = (int32)AMotionEvent_getPointerCount(inputEvent);
|
|
for (int32 i = 0; i < pointerCount; i++)
|
|
{
|
|
const int32 pointerId = AMotionEvent_getPointerId(inputEvent, i);
|
|
const float x = AMotionEvent_getX(inputEvent, i);
|
|
const float y = AMotionEvent_getY(inputEvent, i);
|
|
TouchScreenImpl->OnTouch(InputDevice::EventType::TouchMove, x, y, pointerId);
|
|
}
|
|
break;
|
|
}
|
|
case AMOTION_EVENT_ACTION_POINTER_DOWN:
|
|
{
|
|
const int32 pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
|
const int32 pointerId = AMotionEvent_getPointerId(inputEvent, pointerIndex);
|
|
const float x = AMotionEvent_getX(inputEvent, pointerIndex);
|
|
const float y = AMotionEvent_getY(inputEvent, pointerIndex);
|
|
TouchScreenImpl->OnTouch(InputDevice::EventType::TouchDown, x, y, pointerId);
|
|
break;
|
|
}
|
|
case AMOTION_EVENT_ACTION_POINTER_UP:
|
|
{
|
|
const int32 pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
|
const int32 pointerId = AMotionEvent_getPointerId(inputEvent, pointerIndex);
|
|
const float x = AMotionEvent_getX(inputEvent, pointerIndex);
|
|
const float y = AMotionEvent_getY(inputEvent, pointerIndex);
|
|
TouchScreenImpl->OnTouch(InputDevice::EventType::TouchUp, x, y, pointerId);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case AINPUT_EVENT_TYPE_KEY:
|
|
{
|
|
const int32_t keyCode = AKeyEvent_getKeyCode(inputEvent);
|
|
for (int32 i = 0; i < ARRAY_COUNT(AndroidKeyEventTypes); i++)
|
|
{
|
|
auto& eventType = AndroidKeyEventTypes[i];
|
|
if (eventType.KeyCode == keyCode)
|
|
{
|
|
bool isDown;
|
|
const int32_t action = AKeyEvent_getAction(inputEvent);
|
|
switch (action)
|
|
{
|
|
case AKEY_EVENT_ACTION_DOWN:
|
|
isDown = true;
|
|
break;
|
|
case AKEY_EVENT_ACTION_MULTIPLE:
|
|
isDown = (AKeyEvent_getRepeatCount(inputEvent) % 2) == 0;
|
|
break;
|
|
case AKEY_EVENT_ACTION_UP:
|
|
default:
|
|
isDown = false;
|
|
break;
|
|
}
|
|
LOG(Warning, "Input Event: KeyCode={}, KeyboardKey={}, GamepadButton={}, IsDown={}", eventType.KeyCode, (int32)eventType.KeyboardKey, (int32)eventType.GamepadButton, isDown);
|
|
|
|
// Keyboard
|
|
if (eventType.KeyboardKey != KeyboardKeys::None)
|
|
{
|
|
if (isDown)
|
|
KeyboardImpl.OnKeyDown(eventType.KeyboardKey);
|
|
else
|
|
KeyboardImpl.OnKeyUp(eventType.KeyboardKey);
|
|
}
|
|
|
|
// Gamepad
|
|
if (eventType.GamepadButton != GamepadButton::None)
|
|
{
|
|
GamepadImpl->CachedState.Buttons[(int32)eventType.GamepadButton] = isDown;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
String ToString(JNIEnv* env, jstring str)
|
|
{
|
|
const char* chars = env->GetStringUTFChars(str, nullptr);
|
|
String result(chars);
|
|
env->ReleaseStringUTFChars(str, chars);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
JNIEXPORT
|
|
void JNICALL Java_com_flaxengine_GameActivity_nativeSetPlatformInfo(JNIEnv* env, jobject thiz, jstring appPackageName, jstring deviceManufacturer, jstring deviceModel, jstring deviceBuildNumber, jstring systemVersion, jstring systemLanguage, int32 screenWidth, int32 screenHeight, jstring cacheDir, jstring executablePath)
|
|
{
|
|
AppPackageName = ToString(env, appPackageName);
|
|
DeviceManufacturer = ToString(env, deviceManufacturer);
|
|
DeviceModel = ToString(env, deviceModel);
|
|
DeviceBuildNumber = ToString(env, deviceBuildNumber);
|
|
SystemVersion = ToString(env, systemVersion);
|
|
SystemLanguage = ToString(env, systemLanguage);
|
|
ScreenWidth = screenWidth;
|
|
ScreenHeight = screenHeight;
|
|
CacheDir = ToString(env, cacheDir);
|
|
ExecutablePath = ToString(env, executablePath);
|
|
}
|
|
}
|
|
|
|
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
JNIEnv* jni;
|
|
App->activity->vm->AttachCurrentThread(&jni, nullptr);
|
|
ASSERT(jni);
|
|
const jclass clazz = jni->GetObjectClass(App->activity->clazz);
|
|
ASSERT(clazz);
|
|
const jmethodID methodID = jni->GetMethodID(clazz, "showAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
|
|
ASSERT(methodID);
|
|
const StringAsANSI<> textAnsi(*text, text.Length());
|
|
const jstring jtext = jni->NewStringUTF(textAnsi.Get());
|
|
const StringAsANSI<> captionAnsi(*caption, caption.Length());
|
|
const jstring jcaption = jni->NewStringUTF(captionAnsi.Get());
|
|
|
|
jni->CallVoidMethod(App->activity->clazz, methodID, jtext, jcaption);
|
|
|
|
jni->DeleteLocalRef(jtext);
|
|
jni->DeleteLocalRef(jcaption);
|
|
App->activity->vm->DetachCurrentThread();
|
|
|
|
// TODO: implement message box buttons and handle it better in Java
|
|
|
|
return DialogResult::OK;
|
|
}
|
|
|
|
void AndroidFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result)
|
|
{
|
|
switch (type)
|
|
{
|
|
case SpecialFolder::Desktop:
|
|
result = TEXT("/storage/self/primary");
|
|
break;
|
|
case SpecialFolder::Documents:
|
|
result = TEXT("/storage/self/primary/Documents");
|
|
break;
|
|
case SpecialFolder::Pictures:
|
|
result = TEXT("/storage/self/primary/DCIM");
|
|
break;
|
|
case SpecialFolder::AppData:
|
|
result = TEXT("/usr/share");
|
|
break;
|
|
case SpecialFolder::LocalAppData:
|
|
case SpecialFolder::ProgramData:
|
|
result = String(AndroidPlatform::GetApp()->activity->externalDataPath);
|
|
break;
|
|
case SpecialFolder::Temporary:
|
|
result = CacheDir;
|
|
break;
|
|
default:
|
|
CRASH;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AndroidDeviceGamepad::SetVibration(const GamepadVibrationState& state)
|
|
{
|
|
Gamepad::SetVibration(state);
|
|
|
|
JNIEnv* jni;
|
|
App->activity->vm->AttachCurrentThread(&jni, nullptr);
|
|
ASSERT(jni);
|
|
const jclass clazz = jni->GetObjectClass(App->activity->clazz);
|
|
ASSERT(clazz);
|
|
const jmethodID methodID = jni->GetMethodID(clazz, "vibrate", "(I)V");
|
|
ASSERT(methodID);
|
|
const float max = Math::Max(Math::Max(state.LeftLarge, state.LeftSmall), Math::Max(state.RightLarge, state.RightSmall));
|
|
if (IsVibrating)
|
|
{
|
|
if (max < 0.25f)
|
|
{
|
|
jni->CallVoidMethod(App->activity->clazz, methodID, 0);
|
|
IsVibrating = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (max >= 0.25f)
|
|
{
|
|
jni->CallVoidMethod(App->activity->clazz, methodID, 30000);
|
|
IsVibrating = true;
|
|
}
|
|
}
|
|
App->activity->vm->DetachCurrentThread();
|
|
}
|
|
|
|
android_app* AndroidPlatform::GetApp()
|
|
{
|
|
return App;
|
|
}
|
|
|
|
String AndroidPlatform::GetAppPackageName()
|
|
{
|
|
return AppPackageName;
|
|
}
|
|
|
|
String AndroidPlatform::GetDeviceManufacturer()
|
|
{
|
|
return DeviceManufacturer;
|
|
}
|
|
|
|
String AndroidPlatform::GetDeviceModel()
|
|
{
|
|
return DeviceModel;
|
|
}
|
|
|
|
String AndroidPlatform::GetDeviceBuildNumber()
|
|
{
|
|
return DeviceBuildNumber;
|
|
}
|
|
|
|
String AndroidPlatform::GetSystemVersion()
|
|
{
|
|
return SystemVersion;
|
|
}
|
|
|
|
void AndroidPlatform::PreInit(android_app* app)
|
|
{
|
|
App = app;
|
|
app->onAppCmd = OnAppCmd;
|
|
app->onInputEvent = OnAppInput;
|
|
ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_KEEP_SCREEN_ON | AWINDOW_FLAG_TURN_SCREEN_ON | AWINDOW_FLAG_FULLSCREEN | AWINDOW_FLAG_DISMISS_KEYGUARD, 0);
|
|
ANativeActivity_setWindowFormat(app->activity, WINDOW_FORMAT_RGBA_8888);
|
|
}
|
|
|
|
bool AndroidPlatform::Is64BitPlatform()
|
|
{
|
|
#ifdef PLATFORM_64BITS
|
|
return true;
|
|
#else
|
|
#error "Implement AndroidPlatform::Is64BitPlatform for 32-bit builds."
|
|
#endif
|
|
}
|
|
|
|
CPUInfo AndroidPlatform::GetCPUInfo()
|
|
{
|
|
return AndroidCpu;
|
|
}
|
|
|
|
int32 AndroidPlatform::GetCacheLineSize()
|
|
{
|
|
return AndroidCpu.CacheLineSize;
|
|
}
|
|
|
|
MemoryStats AndroidPlatform::GetMemoryStats()
|
|
{
|
|
const uint64 pageSize = getpagesize();
|
|
const uint64 totalPages = get_phys_pages();
|
|
const uint64 availablePages = get_avphys_pages();
|
|
MemoryStats result;
|
|
result.TotalPhysicalMemory = totalPages * pageSize;
|
|
result.UsedPhysicalMemory = (totalPages - availablePages) * pageSize;
|
|
result.TotalVirtualMemory = result.TotalPhysicalMemory;
|
|
result.UsedVirtualMemory = result.UsedPhysicalMemory;
|
|
return result;
|
|
}
|
|
|
|
ProcessMemoryStats AndroidPlatform::GetProcessMemoryStats()
|
|
{
|
|
struct rusage usage;
|
|
getrusage(RUSAGE_SELF, &usage);
|
|
ProcessMemoryStats result;
|
|
result.UsedPhysicalMemory = usage.ru_maxrss;
|
|
result.UsedVirtualMemory = result.UsedPhysicalMemory;
|
|
return result;
|
|
}
|
|
|
|
uint64 AndroidPlatform::GetCurrentThreadID()
|
|
{
|
|
return static_cast<uint64>(pthread_self());
|
|
}
|
|
|
|
void AndroidPlatform::SetThreadPriority(ThreadPriority priority)
|
|
{
|
|
// TODO: impl this
|
|
}
|
|
|
|
void AndroidPlatform::SetThreadAffinityMask(uint64 affinityMask)
|
|
{
|
|
pid_t tid = gettid();
|
|
int mask = (int)affinityMask;
|
|
syscall(__NR_sched_setaffinity, tid, sizeof(mask), &mask);
|
|
}
|
|
|
|
void AndroidPlatform::Sleep(int32 milliseconds)
|
|
{
|
|
usleep(milliseconds * 1000);
|
|
}
|
|
|
|
double AndroidPlatform::GetTimeSeconds()
|
|
{
|
|
struct timespec ts;
|
|
clock_gettime(ClockSource, &ts);
|
|
return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1e9;
|
|
}
|
|
|
|
uint64 AndroidPlatform::GetTimeCycles()
|
|
{
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
|
return static_cast<uint64>(static_cast<uint64>(ts.tv_sec) * 1000000ULL + static_cast<uint64>(ts.tv_nsec) / 1000ULL);
|
|
}
|
|
|
|
void AndroidPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
|
{
|
|
// Get the calendar time
|
|
struct timeval time;
|
|
gettimeofday(&time, nullptr);
|
|
|
|
// Convert calendar time to local time
|
|
struct tm localTime;
|
|
localtime_r(&time.tv_sec, &localTime);
|
|
|
|
// Extract time from Unix date
|
|
year = localTime.tm_year + 1900;
|
|
month = localTime.tm_mon + 1;
|
|
dayOfWeek = localTime.tm_wday;
|
|
day = localTime.tm_mday;
|
|
hour = localTime.tm_hour;
|
|
minute = localTime.tm_min;
|
|
second = localTime.tm_sec;
|
|
millisecond = time.tv_usec / 1000;
|
|
}
|
|
|
|
void AndroidPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
|
{
|
|
// Get the calendar time
|
|
struct timeval time;
|
|
gettimeofday(&time, nullptr);
|
|
|
|
// Convert to UTC time
|
|
struct tm localTime;
|
|
gmtime_r(&time.tv_sec, &localTime);
|
|
|
|
// Extract time
|
|
year = localTime.tm_year + 1900;
|
|
month = localTime.tm_mon + 1;
|
|
dayOfWeek = localTime.tm_wday;
|
|
day = localTime.tm_mday;
|
|
hour = localTime.tm_hour;
|
|
minute = localTime.tm_min;
|
|
second = localTime.tm_sec;
|
|
millisecond = time.tv_usec / 1000;
|
|
}
|
|
|
|
bool AndroidPlatform::Init()
|
|
{
|
|
if (UnixPlatform::Init())
|
|
return true;
|
|
|
|
// Init timing
|
|
struct timespec ts;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
|
|
{
|
|
ClockSource = CLOCK_REALTIME;
|
|
}
|
|
else
|
|
{
|
|
ClockSource = CLOCK_MONOTONIC;
|
|
}
|
|
|
|
// Set info about the CPU
|
|
cpu_set_t cpus;
|
|
CPU_ZERO(&cpus);
|
|
if (sched_getaffinity(0, sizeof(cpus), &cpus) == 0)
|
|
{
|
|
AndroidCpu.ProcessorCoreCount = AndroidCpu.LogicalProcessorCount = CPU_COUNT(&cpus);
|
|
}
|
|
else
|
|
{
|
|
AndroidCpu.ProcessorCoreCount = AndroidCpu.LogicalProcessorCount = 1;
|
|
}
|
|
AndroidCpu.ProcessorPackageCount = 1;
|
|
AndroidCpu.L1CacheSize = 0;
|
|
AndroidCpu.L2CacheSize = 0;
|
|
AndroidCpu.L3CacheSize = 0;
|
|
AndroidCpu.PageSize = sysconf(_SC_PAGESIZE);
|
|
AndroidCpu.ClockSpeed = GetClockFrequency();
|
|
AndroidCpu.CacheLineSize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
|
if (!AndroidCpu.CacheLineSize)
|
|
{
|
|
AndroidCpu.CacheLineSize = PLATFORM_CACHE_LINE_SIZE;
|
|
}
|
|
|
|
UnixGetMacAddress(MacAddress);
|
|
|
|
// Generate unique device ID
|
|
{
|
|
DeviceId = Guid::Empty;
|
|
|
|
// A - Computer Name and User Name
|
|
uint32 hash = GetHash(Platform::GetComputerName());
|
|
CombineHash(hash, GetHash(Platform::GetUserName()));
|
|
DeviceId.A = hash;
|
|
|
|
// B - MAC address
|
|
hash = MacAddress[0];
|
|
for (uint32 i = 0; i < 6; i++)
|
|
CombineHash(hash, MacAddress[i]);
|
|
DeviceId.B = hash;
|
|
|
|
// C - memory
|
|
DeviceId.C = (uint32)Platform::GetMemoryStats().TotalPhysicalMemory;
|
|
|
|
// D - cpuid
|
|
DeviceId.D = (uint32)AndroidCpu.ClockSpeed * AndroidCpu.LogicalProcessorCount * AndroidCpu.ProcessorCoreCount * AndroidCpu.CacheLineSize;
|
|
}
|
|
|
|
// Setup native platform input devices
|
|
Input::Keyboard = &KeyboardImpl;
|
|
Input::Gamepads.Add(GamepadImpl = New<AndroidDeviceGamepad>());
|
|
Input::OnGamepadsChanged();
|
|
Input::CustomDevices.Add(TouchScreenImpl = New<AndroidTouchScreen>());
|
|
|
|
// Perform initial app messages pump
|
|
Tick();
|
|
|
|
return false;
|
|
}
|
|
|
|
void AndroidPlatform::LogInfo()
|
|
{
|
|
UnixPlatform::LogInfo();
|
|
|
|
LOG(Info, "App Package Name: {0}", AppPackageName);
|
|
LOG(Info, "System Version: {0}", SystemVersion);
|
|
LOG(Info, "Device: {0} {1}, {2}", DeviceManufacturer, DeviceModel, DeviceBuildNumber);
|
|
}
|
|
|
|
void AndroidPlatform::BeforeRun()
|
|
{
|
|
// Perform initial app messages pump
|
|
Tick();
|
|
}
|
|
|
|
void AndroidPlatform::Tick()
|
|
{
|
|
UnixPlatform::Tick();
|
|
|
|
// Pool app events
|
|
int events;
|
|
android_poll_source* source;
|
|
while (ALooper_pollAll(0, nullptr, &events, reinterpret_cast<void**>(&source)) >= 0)
|
|
{
|
|
// Process event
|
|
if (source != nullptr)
|
|
{
|
|
source->process(App, source);
|
|
}
|
|
|
|
// Check if exit
|
|
if (App->destroyRequested != 0)
|
|
{
|
|
Engine::RequestExit();
|
|
return;
|
|
}
|
|
}
|
|
|
|
UpdateOrientation();
|
|
}
|
|
|
|
void AndroidPlatform::BeforeExit()
|
|
{
|
|
}
|
|
|
|
void AndroidPlatform::Exit()
|
|
{
|
|
}
|
|
|
|
#if !BUILD_RELEASE
|
|
|
|
void AndroidPlatform::Log(const StringView& msg)
|
|
{
|
|
const StringAsANSI<512> msgAnsi(*msg, msg.Length());
|
|
const char* str = msgAnsi.Get();
|
|
__android_log_write(ANDROID_LOG_INFO, "Flax", str);
|
|
}
|
|
|
|
#endif
|
|
|
|
int32 AndroidPlatform::GetDpi()
|
|
{
|
|
return AConfiguration_getScreenWidthDp(App->config);
|
|
}
|
|
|
|
NetworkConnectionType AndroidPlatform::GetNetworkConnectionType()
|
|
{
|
|
JNIEnv* jni;
|
|
App->activity->vm->AttachCurrentThread(&jni, nullptr);
|
|
ASSERT(jni);
|
|
const jclass clazz = jni->GetObjectClass(App->activity->clazz);
|
|
ASSERT(clazz);
|
|
const jmethodID methodID = jni->GetMethodID(clazz, "getNetworkConnectionType", "()I");
|
|
ASSERT(methodID);
|
|
const jint type = jni->CallIntMethod(App->activity->clazz, methodID);
|
|
App->activity->vm->DetachCurrentThread();
|
|
return (NetworkConnectionType)type;
|
|
}
|
|
|
|
ScreenOrientationType AndroidPlatform::GetScreenOrientationType()
|
|
{
|
|
return Orientation;
|
|
}
|
|
|
|
String AndroidPlatform::GetUserLocaleName()
|
|
{
|
|
return SystemLanguage;
|
|
}
|
|
|
|
String AndroidPlatform::GetComputerName()
|
|
{
|
|
return DeviceModel;
|
|
}
|
|
|
|
String AndroidPlatform::GetUserName()
|
|
{
|
|
// TODO: add support for username on Android
|
|
return String::Empty;
|
|
}
|
|
|
|
bool AndroidPlatform::GetHasFocus()
|
|
{
|
|
return HasFocus;
|
|
}
|
|
|
|
bool AndroidPlatform::GetIsPaused()
|
|
{
|
|
return IsPaused || App->window == nullptr;
|
|
}
|
|
|
|
bool AndroidPlatform::CanOpenUrl(const StringView& url)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void AndroidPlatform::OpenUrl(const StringView& url)
|
|
{
|
|
JNIEnv* jni;
|
|
App->activity->vm->AttachCurrentThread(&jni, nullptr);
|
|
ASSERT(jni);
|
|
const jclass clazz = jni->GetObjectClass(App->activity->clazz);
|
|
ASSERT(clazz);
|
|
const jmethodID methodID = jni->GetMethodID(clazz, "openUrl", "(Ljava/lang/String;)V");
|
|
ASSERT(methodID);
|
|
const StringAsANSI<> urlAnsi(*url, url.Length());
|
|
const jstring jurl = jni->NewStringUTF(urlAnsi.Get());
|
|
jni->CallVoidMethod(App->activity->clazz, methodID, jurl);
|
|
jni->DeleteLocalRef(jurl);
|
|
App->activity->vm->DetachCurrentThread();
|
|
}
|
|
|
|
Vector2 AndroidPlatform::GetMousePosition()
|
|
{
|
|
return Vector2::Zero;
|
|
}
|
|
|
|
void AndroidPlatform::SetMousePosition(const Vector2& pos)
|
|
{
|
|
}
|
|
|
|
Vector2 AndroidPlatform::GetDesktopSize()
|
|
{
|
|
return Vector2((float)ScreenWidth, (float)ScreenHeight);
|
|
}
|
|
|
|
Rectangle AndroidPlatform::GetMonitorBounds(const Vector2& screenPos)
|
|
{
|
|
return Rectangle(Vector2::Zero, GetDesktopSize());
|
|
}
|
|
|
|
Rectangle AndroidPlatform::GetVirtualDesktopBounds()
|
|
{
|
|
return Rectangle(Vector2::Zero, GetDesktopSize());
|
|
}
|
|
|
|
String AndroidPlatform::GetMainDirectory()
|
|
{
|
|
return String(App->activity->internalDataPath);
|
|
}
|
|
|
|
String AndroidPlatform::GetExecutableFilePath()
|
|
{
|
|
return ExecutablePath;
|
|
}
|
|
|
|
Guid AndroidPlatform::GetUniqueDeviceId()
|
|
{
|
|
return DeviceId;
|
|
}
|
|
|
|
String AndroidPlatform::GetWorkingDirectory()
|
|
{
|
|
char buffer[256];
|
|
getcwd(buffer, ARRAY_COUNT(buffer));
|
|
return String(buffer);
|
|
}
|
|
|
|
bool AndroidPlatform::SetWorkingDirectory(const String& path)
|
|
{
|
|
return chdir(StringAsANSI<>(*path, path.Length()).Get()) != 0;
|
|
}
|
|
|
|
Window* AndroidPlatform::CreateWindow(const CreateWindowSettings& settings)
|
|
{
|
|
return New<AndroidWindow>(settings);
|
|
}
|
|
|
|
bool AndroidPlatform::GetEnvironmentVariable(const String& name, String& value)
|
|
{
|
|
char* env = getenv(StringAsANSI<>(*name, name.Length()).Get());
|
|
if (env)
|
|
{
|
|
value = String(env);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AndroidPlatform::SetEnvironmentVariable(const String& name, const String& value)
|
|
{
|
|
return setenv(StringAsANSI<>(*name, name.Length()).Get(), StringAsANSI<>(*value, value.Length()).Get(), true) != 0;
|
|
}
|
|
|
|
void* AndroidPlatform::LoadLibrary(const Char* filename)
|
|
{
|
|
const StringAsANSI<> filenameANSI(filename);
|
|
void* result = dlopen(filenameANSI.Get(), RTLD_LAZY);
|
|
if (!result)
|
|
{
|
|
LOG(Error, "Failed to load {0} because {1}", filename, String(dlerror()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AndroidPlatform::FreeLibrary(void* handle)
|
|
{
|
|
dlclose(handle);
|
|
}
|
|
|
|
void* AndroidPlatform::GetProcAddress(void* handle, const char* symbol)
|
|
{
|
|
return dlsym(handle, symbol);
|
|
}
|
|
|
|
#endif
|