Compare commits

..

No commits in common. "master" and "2025.04.05" have entirely different histories.

488 changed files with 36694 additions and 71950 deletions

View file

@ -1,2 +0,0 @@
# Contributing to Principia
Contributions are welcome! Please see [Contributing to the Game](https://principia-web.se/wiki/Contributing_to_the_Game) on the Principia Wiki for more information.

View file

@ -4,8 +4,8 @@ labels: ["Bug"]
body:
- type: markdown
attributes:
value: Thank you for helping to make the game better by reporting issues that you discover. Make sure to describe the necessary details about the bug such that it can be reproduced by others. Additionally please refrain from using LLMs ("AI") to write or "improve" your report, as they typically just make it more verbose without adding any useful information.
- type: input
value: Thank you for helping to make the game better by reporting issues that you discover. Please make sure to describe the necessary details about the bug such that it can be reproduced by others and hopefully be fixed.
- type: textarea
attributes:
label: Principia version
description: Describe the version you are running here. Are you on a stable release or on a nightly build, or have you built from source? To determine where a non-release build was built off of you can press the version number in the bottom right corner of the main menu.

View file

@ -24,10 +24,10 @@ jobs:
android:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v5
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
@ -38,9 +38,7 @@ jobs:
./gradlew assemblerelease
- name: Save apk artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: principia-release-unsigned.apk
path: android/principia/build/outputs/apk/release/principia-release-unsigned.apk
#archive: false
if-no-files-found: error

View file

@ -1,34 +0,0 @@
name: build_utils
on:
push:
paths:
- 'src/**'
- 'utils/**'
pull_request:
paths:
- 'src/**'
- 'utils/**'
jobs:
build_utils:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install -y libjansson-dev
- name: Build
run: |
cd utils
make
- name: Upload output as artifact
uses: actions/upload-artifact@v7
with:
name: principia-utils
path: utils/bin/
if-no-files-found: error

View file

@ -25,15 +25,15 @@ jobs:
image: debian:bullseye
env: { LANG: "C.UTF-8" }
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install deps
run: |
apt-get update
apt-get install -y \
clang-16 libgtk-3-dev libgl-dev libcurl4-openssl-dev libpng-dev libjpeg-dev libfreetype6-dev cmake ninja-build desktop-file-utils ca-certificates wget file \
libasound2-dev libpulse-dev libjack-dev libsndio-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libpipewire-0.3-dev libwayland-dev liburing-dev \
--no-install-recommends
apt-get install -y clang-16 libgtk-3-dev libgl-dev libglew-dev libcurl4-openssl-dev libpng-dev libjpeg-dev libfreetype6-dev cmake ninja-build desktop-file-utils ca-certificates wget file --no-install-recommends
wget https://github.com/principia-game/linux-deps/releases/download/latest/deps.tar.gz -O linux-deps.tar.gz
tar -xaf linux-deps.tar.gz -C /
- name: Build
run: |
@ -44,16 +44,13 @@ jobs:
CXX: clang++-16
- name: Upload output as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: Principia-x86_64.AppImage
path: build/Principia-x86_64.AppImage
#archive: false
if-no-files-found: error
- name: Upload debug symbols as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: linux-appimage-dbgsym
path: build/principia.debug
if-no-files-found: error

View file

@ -18,26 +18,23 @@ on:
jobs:
linux_ss:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install -y g++ libgl-dev libpng-dev libjpeg-dev libfreetype6-dev cmake ninja-build \
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libegl1-mesa-dev \
--no-install-recommends
sudo apt-get install -y g++ libgl-dev libpng-dev libjpeg-dev libfreetype6-dev libsdl2-dev cmake ninja-build --no-install-recommends
- name: Build
run: |
mkdir build; cd build
cmake .. -DSCREENSHOT_BUILD=ON -G Ninja -DUSE_VENDORED_SDL3=ON
cmake .. -DSCREENSHOT_BUILD=ON -G Ninja
ninja -j4
- name: Upload output as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: screenshotter_artifact
path: build/principia
if-no-files-found: error

View file

@ -22,14 +22,16 @@ jobs:
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install deps
run: |
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew update --auto-update
brew install cmake ninja libpng libjpeg-turbo freetype sdl3 gtk+3
brew install cmake ninja glew libpng libjpeg-turbo freetype sdl2 gtk+3
patch /opt/homebrew/include/GL/glew.h < packaging/glew_macos_fix.patch
- name: Compile
run: |
@ -38,7 +40,8 @@ jobs:
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \
-DCMAKE_FIND_FRAMEWORK=LAST \
-DCMAKE_INSTALL_PREFIX=../build/macos/ \
-DCMAKE_EXE_LINKER_FLAGS="-L/opt/homebrew/lib/"
-DCMAKE_EXE_LINKER_FLAGS="-L/opt/homebrew/lib/" \
-DGLEW_LIBRARY_RELEASE=/opt/homebrew/lib/libGLEW.dylib
ninja
- name: Package
@ -47,9 +50,7 @@ jobs:
ninja package
- name: Upload output as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: principia-macos
path: build/*.zip
#archive: false
if-no-files-found: error

View file

@ -1,47 +0,0 @@
name: web
on:
push:
paths:
- 'src/**'
- 'lib/**'
- 'data/**'
- 'packaging/**'
- 'CMakeLists.txt'
- '.github/workflows/web.yml'
pull_request:
paths:
- 'src/**'
- 'lib/**'
- 'data/**'
- 'packaging/**'
- 'CMakeLists.txt'
- '.github/workflows/web.yml'
jobs:
web:
runs-on: ubuntu-latest
container:
image: emscripten/emsdk:latest
env: { LANG: "C.UTF-8" }
steps:
- uses: actions/checkout@v6
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
- name: Build
run: |
mkdir build; cd build
emcmake cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX= -DUSE_VENDORED_SDL3=ON
ninja
DESTDIR=../web ninja install
- name: Save apk artifact
uses: actions/upload-artifact@v7
with:
name: principia-wasm
path: web/
if-no-files-found: error

View file

@ -25,7 +25,7 @@ jobs:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
@ -37,11 +37,12 @@ jobs:
gcc:p
cmake:p
ninja:p
glew:p
zlib:p
gtk3:p
libpng:p
libjpeg-turbo:p
sdl3:p
SDL2:p
nsis:p
7zip:p
@ -62,17 +63,13 @@ jobs:
../packaging/windows_portable.sh
- name: Upload output as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: principia-setup.exe
path: build/principia-setup.exe
#archive: false
if-no-files-found: error
- name: Upload output as artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: principia-portable.7z
path: build/principia-portable.7z
#archive: false
if-no-files-found: error

View file

@ -15,8 +15,6 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
include(DownloadLib)
add_custom_target(GenerateGitVersion
COMMAND ${CMAKE_COMMAND}
-D "GENERATE_VERSION_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}"
@ -24,24 +22,19 @@ add_custom_target(GenerateGitVersion
-P "${CMAKE_SOURCE_DIR}/cmake/Modules/GenerateGitVersion.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
option(UNITY_BUILD "Experimental unity build" FALSE)
mark_as_advanced(UNITY_BUILD)
option(USE_VENDORED_SDL3 "Build with vendored SDL3 library" FALSE)
# Find core dependencies
# ----------------------
if(USE_VENDORED_SDL3)
include(cmake/SDL.cmake)
else()
find_package(SDL3 REQUIRED)
endif()
if(ANDROID)
cmake_minimum_required(VERSION 3.20)
include(PrincipiaAndroidLibs)
else()
if(NOT EMSCRIPTEN)
find_package(SDL2 REQUIRED)
endif()
set(OpenGL_GL_PREFERENCE GLVND)
find_package(OpenGL REQUIRED)
endif()
@ -63,11 +56,7 @@ else()
set(SCREENSHOT_BUILD FALSE)
endif()
if(EMSCRIPTEN)
set(BACKEND_IMGUI TRUE)
else()
option(BACKEND_IMGUI "Enable incomplete Dear Imgui dialog backend (Experimental)" FALSE)
endif()
option(BACKEND_IMGUI "Enable incomplete Dear Imgui dialog backend (Experimental)" FALSE)
set(TMS_FORMFACTOR "PC")
if(WIN32)
@ -101,7 +90,6 @@ endif()
include_directories(
lib/
lib/GLAD/include/
lib/imgui/
lib/lua/
lib/SDL_image/
@ -109,50 +97,31 @@ include_directories(
src/
${CMAKE_CURRENT_BINARY_DIR})
file(GLOB SRCS CONFIGURE_DEPENDS
lib/GLAD/src/gl.c
file(GLOB SRCS
src/tms/core/*.c
src/tms/math/*.c
src/tms/util/*.c
src/tms/bindings/cpp/cpp.cc
src/tms/modules/3ds.c
src/*.cc
src/*.c
lib/lua/*.c
lib/SDL_image/*.c
lib/Box2D/Collision/*.cc
lib/Box2D/Collision/Shapes/*.cc
lib/Box2D/Common/*.cc
lib/Box2D/Dynamics/*.cc
lib/Box2D/Dynamics/Contacts/*.cc
lib/Box2D/Dynamics/Joints/*.cc
lib/Box2D/Particle/*.cc
)
if(UNITY_BUILD)
list(APPEND SRCS
src/tms/_unity_chunk.c
src/_unity_chunk.cc
src/game.cc
src/game-gearbox-edit.cc
src/game-gui.cc
src/game-panel-edit.cc
src/menu_pkg.cc
src/repair_station.cc
src/solver_ingame.cc
lib/Box2D/_unity_chunk.cc)
else()
file(GLOB MAIN_SRCS CONFIGURE_DEPENDS
src/tms/core/*.c
src/tms/math/*.c
src/tms/cpp.cc
src/tms/modules/3ds.c
src/*.cc
src/luascript/*.cc
lib/Box2D/Collision/*.cc
lib/Box2D/Collision/Shapes/*.cc
lib/Box2D/Common/*.cc
lib/Box2D/Dynamics/*.cc
lib/Box2D/Dynamics/Contacts/*.cc
lib/Box2D/Dynamics/Joints/*.cc
lib/Box2D/Particle/*.cc)
list(APPEND SRCS ${MAIN_SRCS})
endif()
if(BACKEND_IMGUI)
file(GLOB IMGUI_SRCS CONFIGURE_DEPENDS
lib/imgui/*.cpp
lib/imgui/misc/freetype/*.cpp
src/ui/*.cc)
file(GLOB IMGUI_SRCS
lib/imgui/*.cpp)
list(APPEND SRCS ${IMGUI_SRCS})
endif()
@ -164,6 +133,8 @@ if(NOT SCREENSHOT_BUILD)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
endif()
find_package(GLEW REQUIRED)
if(NOT BACKEND_IMGUI)
include_directories(${GTK3_INCLUDE_DIRS})
endif()
@ -175,22 +146,39 @@ if(NOT SCREENSHOT_BUILD)
add_definitions(-DBUILD_CURL)
endif()
file(GLOB SDL_mixer_SRCS CONFIGURE_DEPENDS lib/SDL_mixer/*.c)
file(GLOB SDL_mixer_SRCS lib/SDL_mixer/*.c)
list(APPEND SRCS ${SDL_mixer_SRCS})
endif()
if(NOT UNITY_BUILD)
if(SCREENSHOT_BUILD)
set(BACKEND_SRC main_screenshotter.cc)
else()
set(BACKEND_SRC main.cc)
list(APPEND SRCS src/tms/backend/pipe.cc)
endif()
# Optional luasocket functionality
list(APPEND SRCS src/tms/backend/${BACKEND_SRC})
if(NOT SCREENSHOT_BUILD AND NOT ANDROID AND NOT EMSCRIPTEN)
option(USE_LUASOCKET "Build with Luasocket support" TRUE)
else()
set(USE_LUASOCKET FALSE)
endif()
if(USE_LUASOCKET)
add_definitions(-DBUILD_LUASOCKET)
file(GLOB LUASOCKET_SRCS
lib/luasocket/*.c)
list(APPEND SRCS ${LUASOCKET_SRCS})
endif()
if(SCREENSHOT_BUILD)
set(BACKEND_SRC main_screenshotter.cc)
else()
set(BACKEND_SRC main.cc)
list(APPEND SRCS src/tms/backend/pipe.cc)
endif()
list(APPEND SRCS src/tms/backend/${BACKEND_SRC})
if(WIN32)
list(APPEND SRCS packaging/principia.rc)
endif()
@ -216,17 +204,24 @@ if(NOT EMSCRIPTEN)
PNG::PNG
ZLIB::ZLIB)
if(WIN32)
list(APPEND LIBS SDL2::SDL2main)
endif()
list(APPEND LIBS SDL2::SDL2)
if(NOT SCREENSHOT_BUILD AND NOT EMSCRIPTEN)
list(APPEND LIBS ${CURL_LIBRARIES})
if(NOT ANDROID AND NOT BACKEND_IMGUI)
if(NOT ANDROID)
list(APPEND LIBS ${GTK3_LIBRARIES})
endif()
if(NOT SHOULD_USE_GLES)
list(APPEND LIBS GLEW::GLEW)
endif()
endif()
endif()
list(APPEND LIBS SDL3::SDL3)
if(SHOULD_USE_GLES)
list(APPEND LIBS GLESv2)
else()
@ -235,6 +230,8 @@ endif()
if(ANDROID)
list(APPEND LIBS android dl log OpenSLES)
elseif(WIN32)
list(APPEND LIBS ws2_32.lib version.lib shlwapi.lib winmm.lib)
endif()
target_link_libraries(${PROJECT_NAME} ${LIBS})
@ -246,24 +243,18 @@ add_dependencies(${PROJECT_NAME} GenerateGitVersion)
if(SHOULD_USE_GLES)
add_definitions(-DTMS_USE_GLES)
endif()
if(UNITY_BUILD)
add_definitions(-DUNITY_BUILD)
elseif(NOT SCREENSHOT_BUILD)
add_definitions(-DTMS_USE_GLEW)
endif()
if(WIN32)
add_definitions(-D_WIN32_WINNT=0x0501)
add_definitions(-D_WIN32_WINNT=0x0501 -DUNICODE)
elseif(SCREENSHOT_BUILD)
add_definitions(-DNO_UI -DSCREENSHOT_BUILD)
endif()
if(BACKEND_IMGUI)
add_definitions(-DPRINCIPIA_BACKEND_IMGUI -DIMGUI_DEFINE_MATH_OPERATORS)
if(SHOULD_USE_GLES)
add_definitions(-DIMGUI_IMPL_OPENGL_ES2)
endif()
endif()
add_definitions(-DTMS_BACKEND_${TMS_FORMFACTOR} -DTMS_BACKEND_${TMS_BACKEND})
@ -273,9 +264,10 @@ set(COMMON_FLAGS "-fno-math-errno -fno-trapping-math -fno-signed-zeros")
if(EMSCRIPTEN)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "--preload-file ../data/")
set(LIBRARY_FLAGS "-sUSE_FREETYPE=1 -sUSE_LIBJPEG=1 -sUSE_LIBPNG=1 -sUSE_ZLIB=1 -pthread")
set(LIBRARY_FLAGS "-sUSE_FREETYPE=1 -sUSE_LIBJPEG=1 -sUSE_LIBPNG=1 -sUSE_ZLIB=1 -sUSE_SDL=2 -pthread")
string(APPEND COMMON_FLAGS " ${LIBRARY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS " ${LIBRARY_FLAGS} -pthread -sPTHREAD_POOL_SIZE=20 -sINITIAL_MEMORY=2013265920 -sALLOW_MEMORY_GROWTH=1 -sTOTAL_STACK=16Mb -sFETCH=1")
set(CMAKE_EXE_LINKER_FLAGS " ${LIBRARY_FLAGS} -pthread -sPTHREAD_POOL_SIZE=20 -sINITIAL_MEMORY=2013265920 -sALLOW_MEMORY_GROWTH=1 -sTOTAL_STACK=16Mb")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif()
set(COMMON_FLAGS_DEBUG "${COMMON_FLAGS} -O0 -ggdb -DDEBUG=1")
@ -302,15 +294,6 @@ if(NOT APPLE)
string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE "-Wl,-s ")
endif()
# Register protocol handler util on Windows
# -----------------------------------------
if(WIN32)
add_executable(register-protocol-handler
packaging/register-protocol-handler/main.c
packaging/register-protocol-handler/windows.rc)
endif()
# Installation
# ------------
@ -326,13 +309,6 @@ if(APPLE)
install(FILES "packaging/principia.icns" DESTINATION "${SHAREDIR}")
install(FILES "packaging/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
elseif(EMSCRIPTEN)
set(BINDIR .)
install(FILES "packaging/index.html" DESTINATION .)
install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.wasm DESTINATION .)
install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.data DESTINATION .)
else()
include(GNUInstallDirs)
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}")

View file

@ -20,7 +20,7 @@ Principia runs on anything with a recent enough version of Windows, Linux or And
* Codeberg mirror: https://codeberg.org/principia/principia
* Mastodon: https://hachyderm.io/@principia
* Mastodon: https://fosstodon.org/@principia
## Binary builds
Release builds builds of Principia for Windows, Android and Linux are available on the [download page](https://principia-web.se/download).
@ -32,7 +32,7 @@ Feel free to fork this project and send in your pull requests. This is a communi
For a brief overview on how to get started with contributing to the game, see the [Contributing to the Game](https://principia-web.se/wiki/Contributing_to_the_Game) page on the wiki.
Also be sure to follow [@principia](https://hachyderm.io/@principia) on Mastodon for more updates about the project.
Also be sure to follow [@principia](https://fosstodon.org/@principia) on Fosstodon for more updates about the project.
## Building from source
See [Compiling Principia](https://principia-web.se/wiki/Compiling_Principia) on the wiki for building from source on supported platforms.

2
android/.gitignore vendored
View file

@ -1,6 +1,6 @@
.gradle/
.idea/
deps
deps/
principia/build/
*.apk
*.apk.idsig

View file

@ -12,11 +12,13 @@ android {
applicationId "com.bithack.principia"
minSdk 21
targetSdk 35
versionCode 41
versionName "2026.06.19"
versionCode 39
versionName "2025.04.05"
externalNativeBuild {
cmake.arguments "-DUSE_VENDORED_SDL3=1"
ndkBuild {
arguments '-j' + Runtime.getRuntime().availableProcessors()
}
}
ndk {

@ -1 +1 @@
Subproject commit f3e15935e822228400064b66677bd3e9d3d7a643
Subproject commit e684cb1f0410439321bef666bf3c11fb7190a6f8

View file

@ -1,933 +1,6 @@
package com.bithack.principia;
import com.bithack.principia.shared.AutosaveDialog;
import com.bithack.principia.shared.CamTargeterDialog;
import com.bithack.principia.shared.ColorChooserDialog;
import com.bithack.principia.shared.CommandPadDialog;
import com.bithack.principia.shared.CommunityDialog;
import com.bithack.principia.shared.ConfirmDialog;
import com.bithack.principia.shared.ConfirmDialog.OnOptionSelectedListener;
import com.bithack.principia.shared.ConsumableDialog;
import com.bithack.principia.shared.DigitalDisplayDialog;
import com.bithack.principia.shared.EventListenerDialog;
import com.bithack.principia.shared.ExportDialog;
import com.bithack.principia.shared.FactoryDialog;
import com.bithack.principia.shared.FrequencyDialog;
import com.bithack.principia.shared.FrequencyRangeDialog;
import com.bithack.principia.shared.FxEmitterDialog;
import com.bithack.principia.shared.InfoDialog;
import com.bithack.principia.shared.ImportDialog;
import com.bithack.principia.shared.JumperDialog;
import com.bithack.principia.shared.Level;
import com.bithack.principia.shared.LevelDialog;
import com.bithack.principia.shared.LoginDialog;
import com.bithack.principia.shared.NewLevelDialog;
import com.bithack.principia.shared.OpenDialog;
import com.bithack.principia.shared.PkgLevelDialog;
import com.bithack.principia.shared.PlayDialog;
import com.bithack.principia.shared.PromptDialog;
import com.bithack.principia.shared.PromptSettingsDialog;
import com.bithack.principia.shared.PublishDialog;
import com.bithack.principia.shared.PublishedDialog;
import com.bithack.principia.shared.QuickaddDialog;
import com.bithack.principia.shared.RegisterDialog;
import com.bithack.principia.shared.ResourceDialog;
import com.bithack.principia.shared.RobotDialog;
import com.bithack.principia.shared.RubberDialog;
import com.bithack.principia.shared.SandboxTipsDialog;
import com.bithack.principia.shared.SaveAsDialog;
import com.bithack.principia.shared.ScriptDialog;
import com.bithack.principia.shared.SequencerDialog;
import com.bithack.principia.shared.SettingsDialog;
import com.bithack.principia.shared.Sfx2Dialog;
import com.bithack.principia.shared.SfxDialog;
import com.bithack.principia.shared.ShapeExtruderDialog;
import com.bithack.principia.shared.PolygonDialog;
import com.bithack.principia.shared.KeyListenerDialog;
import com.bithack.principia.shared.EmitterDialog;
import com.bithack.principia.shared.DecorationDialog;
import com.bithack.principia.shared.AnimalDialog;
import com.bithack.principia.shared.StickyDialog;
import com.bithack.principia.shared.SynthesizerDialog;
import com.bithack.principia.shared.TimerDialog;
import com.bithack.principia.shared.ToolDialog;
import com.bithack.principia.shared.TouchFieldDialog;
import com.bithack.principia.shared.VariableDialog;
import com.bithack.principia.shared.SoundManDialog;
import com.bithack.principia.shared.MultiSelectDialog;
import com.bithack.principia.shared.VendorDialog;
public class PrincipiaActivity extends TMSActivity
{
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Locale;
import android.annotation.SuppressLint;
import android.app.*;
import android.content.*;
import android.content.DialogInterface.OnKeyListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.view.*;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.net.Uri;
import android.os.*;
import android.graphics.*;
import android.media.*;
import android.hardware.*;
import android.widget.ArrayAdapter;
import org.libsdl.app.SDLActivity;
import java.util.List;
public class PrincipiaActivity extends SDLActivity implements View.OnSystemUiVisibilityChangeListener, DialogInterface.OnDismissListener, DialogInterface.OnShowListener, OnSeekBarChangeListener {
private static final String TAG = "PrincipiaActivity";
public static Dialog wv_dialog;
public static WebView wv;
public static CookieManager wv_cm;
public static int num_dialogs = 0;
static Toast last_toast = null;
private static SettingsDialog settings_dialog;
public static final int LEVEL_LOCAL = 0;
public static final int LEVEL_DB = 1;
public static final int LEVEL_MAIN = 2;
public static final int LEVEL_SYS = 3;
public static final int LEVEL_PARTIAL = 4;
public static final int LEVEL_LOCAL_STATE = 100;
public static final int LEVEL_DB_STATE = 101;
public static final int LEVEL_MAIN_STATE = 102;
public static final int ACTION_OPEN = 1;
public static final int ACTION_RELOAD_GRAPHICS = 2;
public static final int ACTION_RELOAD_LEVEL = 3;
public static final int ACTION_SAVE = 4;
public static final int ACTION_NEW_LEVEL = 5;
public static final int ACTION_STICKY = 6;
public static final int ACTION_LOGIN = 7;
public static final int ACTION_SAVE_COPY = 8;
public static final int ACTION_CONSTRUCT_ENTITY = 9;
public static final int ACTION_OPEN_PLAY = 10;
public static final int ACTION_PUBLISH = 11;
public static final int ACTION_PLAY_PKG = 12;
public static final int ACTION_WARP = 13;
public static final int ACTION_PUBLISH_PKG = 14;
public static final int ACTION_PING = 15;
public static final int ACTION_UPGRADE_LEVEL = 16;
public static final int ACTION_DERIVE = 17;
public static final int ACTION_SET_STICKY_TEXT = 18;
public static final int ACTION_IMPORT_OBJECT = 19;
public static final int ACTION_EXPORT_OBJECT = 20;
public static final int ACTION_MULTIEMITTER_SET = 21;
public static final int ACTION_PUZZLEPLAY = 22;
public static final int ACTION_EDIT = 23;
public static final int ACTION_AUTOFIT_LEVEL_BORDERS = 24;
public static final int ACTION_RESTART_LEVEL = 25;
public static final int ACTION_BACK = 26;
public static final int ACTION_RESELECT = 27;
public static final int ACTION_HIGHLIGHT_SELECTED = 28;
public static final int ACTION_OPEN_AUTOSAVE = 31;
public static final int ACTION_REMOVE_AUTOSAVE = 32;
public static final int ACTION_GOTO_MAINMENU = 33;
public static final int ACTION_DELETE_LEVEL = 34;
public static final int ACTION_DELETE_PARTIAL = 35;
public static final int ACTION_SET_LEVEL_TYPE = 36;
public static final int ACTION_RELOAD_DISPLAY = 37;
public static final int ACTION_ENTITY_MODIFIED = 38;
public static final int ACTION_SET_MODE = 39;
public static final int ACTION_MAIN_MENU_PKG = 40;
public static final int ACTION_WORLD_PAUSE = 41;
public static final int ACTION_CONSTRUCT_ITEM = 45;
public static final int ACTION_SUBMIT_SCORE = 46;
public static final int ACTION_MULTI_DELETE = 47;
public static final int ACTION_OPEN_STATE = 48;
public static final int ACTION_AUTOSAVE = 49;
public static final int ACTION_MULTI_JOINT_STRENGTH = 50;
public static final int ACTION_MULTI_PLASTIC_COLOR = 51;
public static final int ACTION_MULTI_PLASTIC_DENSITY = 52;
public static final int ACTION_MULTI_CHANGE_CONNECTION_RENDER_TYPE = 53;
public static final int ACTION_MULTI_UNLOCK_ALL = 54;
public static final int ACTION_MULTI_DISCONNECT_ALL = 55;
public static final int ACTION_SAVE_STATE = 65;
public static final int ACTION_SELF_DESTRUCT = 71;
public static final int DIALOG_SANDBOX_MENU = 1;
public static final int DIALOG_QUICKADD = 100;
public static final int DIALOG_BEAM_COLOR = 101;
public static final int DIALOG_SAVE = 102;
public static final int DIALOG_OPEN = 103;
public static final int DIALOG_NEW_LEVEL = 104;
public static final int DIALOG_SET_FREQUENCY = 105;
public static final int DIALOG_PIXEL_COLOR = 106;
public static final int DIALOG_CONFIRM_QUIT = 107;
public static final int DIALOG_SET_COMMAND = 108;
public static final int DIALOG_STICKY = 109;
public static final int DIALOG_FXEMITTER = 110;
public static final int DIALOG_CAMTARGETER = 111;
public static final int DIALOG_SET_FREQ_RANGE = 112;
public static final int DIALOG_OPEN_OBJECT = 113;
public static final int DIALOG_EXPORT = 114;
public static final int DIALOG_SET_PKG_LEVEL = 115;
public static final int DIALOG_ROBOT = 116;
public static final int DIALOG_MULTIEMITTER = 117;
public static final int DIALOG_PUZZLE_PLAY = 118;
public static final int DIALOG_TIMER = 119;
public static final int DIALOG_EVENTLISTENER = 120;
public static final int DIALOG_SETTINGS = 121;
public static final int DIALOG_SAVE_COPY = 122;
public static final int DIALOG_LEVEL_PROPERTIES = 123;
public static final int DIALOG_LEVEL_INFO = 124;
public static final int DIALOG_DIGITAL_DISPLAY = 125;
public static final int DIALOG_PLAY_MENU = 126;
public static final int DIALOG_OPEN_AUTOSAVE = 127;
public static final int DIALOG_COMMUNITY = 128;
public static final int DIALOG_PROMPT_SETTINGS = 129;
public static final int DIALOG_PROMPT = 130;
public static final int DIALOG_SFX_EMITTER = 131;
public static final int DIALOG_VARIABLE = 132;
public static final int DIALOG_SYNTHESIZER = 133;
public static final int DIALOG_SEQUENCER = 134;
public static final int DIALOG_SHAPEEXTRUDER = 135;
public static final int DIALOG_JUMPER = 136;
public static final int DIALOG_PUBLISHED = 138;
public static final int DIALOG_TOUCHFIELD = 140;
public static final int DIALOG_ESCRIPT = 141;
public static final int DIALOG_ITEM = 142;
public static final int DIALOG_SANDBOX_MODE = 143;
public static final int DIALOG_RUBBER = 144;
public static final int DIALOG_SOUNDMAN = 148;
public static final int DIALOG_FACTORY = 149;
public static final int DIALOG_SET_FACTION = 150;
public static final int DIALOG_RESOURCE = 151;
public static final int DIALOG_VENDOR = 152;
public static final int DIALOG_ANIMAL = 153;
public static final int DIALOG_POLYGON = 154;
public static final int DIALOG_KEY_LISTENER = 155;
public static final int DIALOG_OPEN_STATE = 156;
public static final int DIALOG_POLYGON_COLOR = 157;
public static final int DIALOG_MULTI_CONFIG = 158;
public static final int DIALOG_EMITTER = 159;
public static final int DIALOG_TREASURE_CHEST = 160;
public static final int DIALOG_DECORATION = 161;
public static final int DIALOG_SFX_EMITTER_2 = 162; // New SFX Emitter dialog (1.5.1+)
public static final int DIALOG_PUBLISH = 300;
public static final int DIALOG_LOGIN = 301;
public static final int DIALOG_SANDBOX_TIPS = 302;
public static final int DIALOG_REGISTER = 303;
public static final int CLOSE_ALL_DIALOGS = 200;
public static final int CLOSE_ABSOLUTELY_ALL_DIALOGS = 201;
public static final int CLOSE_REGISTER_DIALOG = 202;
public static final int DISABLE_REGISTER_LOADER = 203;
public static final int PROMPT_RESPONSE_NONE = 0;
public static final int PROMPT_RESPONSE_A = 1;
public static final int PROMPT_RESPONSE_B = 2;
public static final int PROMPT_RESPONSE_C = 3;
@Override
protected String[] getLibraries() {
return new String[] {
"principia"
};
}
public static PrincipiaActivity mSingleton;
@Override
protected void onCreate(Bundle savedInstanceState) {
mSingleton = this;
super.onCreate(savedInstanceState);
this.init_webview();
this.handle_intent(this.getIntent());
open_adapter = new ArrayAdapter<Level>(SDLActivity.mSingleton,
android.R.layout.select_dialog_item);
QuickaddDialog.object_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line);
}
public static void message(final String s, final int longd)
{
Log.v("tes message", s);
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
if (last_toast != null) {
last_toast.setText(s);
} else {
last_toast = Toast.makeText(SDLActivity.mSingleton, s, longd==1?Toast.LENGTH_LONG:Toast.LENGTH_SHORT);
}
last_toast.show();
}
});
}
public static void emit_signal(final int signal_id)
{
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
if (signal_id == 200) { // SIGNAL_QUICKADD_REFRESH
Log.v("Principia", "Quickadd refresh.");
QuickaddDialog.object_adapter.clear();
String[] objects = PrincipiaBackend.getObjects().split(",");
Log.v("Principia", String.format("Number of objects: %d", objects.length));
for (String name : objects) {
QuickaddDialog.object_adapter.add(name);
}
}
}
});
}
public static void open_url(final String url)
{
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
String community_host = PrincipiaBackend.getCommunityHost();
if (wv_cm != null) {
String curl_token = PrincipiaBackend.getCookies();
if (curl_token != null) {
// we got relevant cookies from curl!
wv_cm.setCookie("."+community_host, "_PRINCSECURITY="+curl_token);
}
}
wv.stopLoading();
wv.loadUrl(url);
wv_dialog.show();
}
});
}
@SuppressLint("SetJavaScriptEnabled")
public void init_webview()
{
wv_dialog = new Dialog(this, android.R.style.Theme_NoTitleBar_Fullscreen) {
@Override
protected void onCreate(Bundle saved_instance) {
super.onCreate(saved_instance);
getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
};
View v = LayoutInflater.from(this).inflate(R.layout.webview, null);
LinearLayout ll = (LinearLayout)v.findViewById(R.id.wv_ll);
final ProgressBar pb = (ProgressBar)v.findViewById(R.id.wv_progress);
final TextView pb_tv = (TextView)v.findViewById(R.id.wv_progresstext);
wv = new WebView(this);
wv.getSettings().setJavaScriptEnabled(true);
int version = 0;
PackageInfo pi;
try {
pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionCode;
} catch (NameNotFoundException e) {
version = 0;
}
wv.getSettings().setUserAgentString(String.format(Locale.US, "Principia WebView/%d (Android)", version));
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
String host = uri.getHost();
if (uri.getScheme().equals("principia")) {
Log.v("Principia", "set arg "+url);
PrincipiaBackend.setarg(url);
wv_dialog.dismiss();
} else if (host.contains(PrincipiaBackend.getCommunityHost())) {
Log.v("Principia", "Load url "+url);
view.stopLoading();
view.loadUrl(url);
} else {
Log.v(TAG, "unhandled url " + url);
Log.v(TAG, "host: '" + uri.getHost()+"'");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
SDLActivity.mSingleton.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.v(TAG, "No app found to open url: " + url);
}
wv_dialog.dismiss();
}
return true;
}
@Override
public void onPageFinished(WebView view, String url)
{
Log.v("Principia", "page finished: " + url);
pb.setVisibility(View.GONE);
pb_tv.setVisibility(View.GONE);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
Log.v("Principia", "page started: " + url);
pb.setVisibility(View.VISIBLE);
pb_tv.setVisibility(View.VISIBLE);
}
});
ll.addView(wv);
Button wv_close = (Button)v.findViewById(R.id.wv_close);
wv_close.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
wv_dialog.dismiss();
}
});
Button wv_reload = (Button)v.findViewById(R.id.wv_reload);
wv_reload.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
wv.reload();
}
});
wv_dialog.setContentView(v);
wv_dialog.setOnShowListener(this);
wv_dialog.setOnDismissListener(this);
wv_cm = CookieManager.getInstance();
wv_dialog.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (wv.canGoBack()) {
wv.goBack();
} else {
wv_dialog.dismiss();
}
return true;
}
}
return false;
}
});
}
public static boolean is_cool = false;
public static void open_dialog(final int num)
{
open_dialog(num, false);
}
public static void open_dialog(final int num, final boolean is_cool)
{
PrincipiaActivity.is_cool = is_cool;
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
if (num == DIALOG_PROMPT) {
try {SDLActivity.mSingleton.removeDialog(num);} catch(Exception e){};
}
if (num == DIALOG_PLAY_MENU) {
try {SDLActivity.mSingleton.removeDialog(num);} catch(Exception e){};
}
try {SDLActivity.mSingleton.removeDialog(DIALOG_OPEN);} catch(Exception e){};
try {SDLActivity.mSingleton.removeDialog(DIALOG_OPEN_STATE);} catch(Exception e){};
try {SDLActivity.mSingleton.removeDialog(DIALOG_MULTIEMITTER);} catch(Exception e){};
try {SDLActivity.mSingleton.removeDialog(DIALOG_OPEN_OBJECT);} catch(Exception e){};
SDLActivity.mSingleton.showDialog(num);
}
});
}
private List<Dialog> open_dialogs = new ArrayList<Dialog>();
public static void showInfoDialog(final String description)
{
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
try {SDLActivity.mSingleton.removeDialog(DIALOG_LEVEL_INFO);} catch(Exception e){};
InfoDialog.description = description;
SDLActivity.mSingleton.showDialog(DIALOG_LEVEL_INFO);
}
});
}
public static void confirm(final String text, final String button1, final int action1, final long action1_data, final String button2, final int action2, final long action2_data, final String button3, final int action3, final long action3_data, final boolean dna_sandbox)
{
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
new ConfirmDialog()
.set_listener(new OnOptionSelectedListener() {
@Override
public void onOptionSelected(int option) {
if (option == ConfirmDialog.OPTION_YES) {
PrincipiaBackend.addActionAsInt(action1, action1_data);
} else if (option == ConfirmDialog.OPTION_NO) {
PrincipiaBackend.addActionAsInt(action2, action2_data);
} else if (option == ConfirmDialog.OPTION_3) {
PrincipiaBackend.addActionAsInt(action3, action3_data);
}
}
})
.run(text, button1, button2, button3, dna_sandbox);
}
});
}
public static void showSandboxTips()
{
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
try {SDLActivity.mSingleton.removeDialog(DIALOG_SANDBOX_TIPS);} catch(Exception e){};
SDLActivity.mSingleton.showDialog(DIALOG_SANDBOX_TIPS);
}
});
}
@Override
protected Dialog onCreateDialog(int num)
{
Dialog d = null;
switch (num) {
case DIALOG_SANDBOX_MENU:
{
AlertDialog.Builder bld = new AlertDialog.Builder(this);
final CharSequence[] items;
items = new CharSequence[] {
"Level properties",
"New level",
"Save",
"Save a copy",
"Open",
"Publish online",
"Settings",
"Log in",
"Help: Getting started",
"Help: Principia Wiki",
"Browse levels online",
"Back to menu",
"Quit"
};
bld.setItems(items, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
/* TODO: Use dialog fragments */
switch (which) {
case 0: showDialog(DIALOG_LEVEL_PROPERTIES); break;
case 1: showDialog(DIALOG_NEW_LEVEL); break;
case 2: if (PrincipiaBackend.getLevelName().isEmpty()) {SaveAsDialog.refresh_name=true; SaveAsDialog.copy=false; showDialog(DIALOG_SAVE);} else PrincipiaBackend.triggerSave(false); break;
case 3: SaveAsDialog.refresh_name = true; SaveAsDialog.copy = true; showDialog(DIALOG_SAVE_COPY); break;
case 4: try {SDLActivity.mSingleton.removeDialog(DIALOG_OPEN);} catch(Exception e){}; showDialog(DIALOG_OPEN); break;
case 5: showDialog(DIALOG_PUBLISH); break;
case 6: showDialog(DIALOG_SETTINGS); break;
case 7: showDialog(DIALOG_LOGIN); break;
case 8: open_url("https://principia-web.se/wiki/Getting_Started"); break;
case 9: open_url("https://principia-web.se/wiki/Main_Page"); break;
case 10: open_url("https://principia-web.se/"); break;
case 11: PrincipiaBackend.addActionAsInt(ACTION_GOTO_MAINMENU, 0); break;
// why
case 12: android.os.Process.killProcess(android.os.Process.myPid()); break;
}
dialog.dismiss();
}
});
d = bld.create();
break;
}
case DIALOG_SETTINGS:
if (settings_dialog == null) {
d = (settings_dialog = new SettingsDialog()).get_dialog();
}
break;
case DIALOG_QUICKADD: d = QuickaddDialog.get_dialog(); break;
case DIALOG_OPEN: d = (new OpenDialog(false)).get_dialog(); break;
case DIALOG_LEVEL_PROPERTIES: d = LevelDialog.get_dialog(); break;
case DIALOG_SAVE_COPY: d = SaveAsDialog.get_dialog(); break;
case DIALOG_SAVE: d = SaveAsDialog.get_dialog(); break;
case DIALOG_LEVEL_INFO: d = (new InfoDialog()).get_dialog(); break;
case DIALOG_STICKY: d = StickyDialog.get_dialog(); break;
case DIALOG_NEW_LEVEL: d = (new NewLevelDialog()).get_dialog(); break;
case DIALOG_ROBOT: d = RobotDialog.get_dialog(); break;
case DIALOG_CAMTARGETER: d = CamTargeterDialog.get_dialog(); break;
case DIALOG_SET_COMMAND: d = CommandPadDialog.get_dialog(); break;
case DIALOG_FXEMITTER: d = FxEmitterDialog.get_dialog(); break;
case DIALOG_EVENTLISTENER: d = EventListenerDialog.get_dialog(); break;
case DIALOG_SET_PKG_LEVEL: d = PkgLevelDialog.get_dialog(); break;
case DIALOG_PIXEL_COLOR: d = ColorChooserDialog.get_dialog(); break;
case DIALOG_BEAM_COLOR: d = ColorChooserDialog.get_dialog(); break;
case DIALOG_POLYGON_COLOR: d = ColorChooserDialog.get_dialog(); break;
case DIALOG_DIGITAL_DISPLAY: d = DigitalDisplayDialog.get_dialog(); break;
case DIALOG_SET_FREQUENCY: d = FrequencyDialog.get_dialog(); break;
case DIALOG_SET_FREQ_RANGE: d = FrequencyRangeDialog.get_dialog(); break;
case DIALOG_EXPORT: d = ExportDialog.get_dialog(); break;
case DIALOG_MULTIEMITTER: d = (new ImportDialog(true)).get_dialog(); break;
case DIALOG_OPEN_OBJECT: d = (new ImportDialog(false)).get_dialog(); break;
case DIALOG_TIMER: d = TimerDialog.get_dialog(); break;
case DIALOG_PLAY_MENU: d = (PlayDialog.create_dialog()); break;
case DIALOG_OPEN_AUTOSAVE: d = (new AutosaveDialog()).get_dialog(); break;
case DIALOG_COMMUNITY: d = (new CommunityDialog()).get_dialog(); break;
case DIALOG_PROMPT_SETTINGS: d = PromptSettingsDialog.get_dialog(); break;
case DIALOG_PROMPT: d = (new PromptDialog()).get_dialog(); break;
case DIALOG_SFX_EMITTER: d = SfxDialog.get_dialog(); break;
case DIALOG_SFX_EMITTER_2: d = Sfx2Dialog.get_dialog(); break;
case DIALOG_VARIABLE: d = VariableDialog.get_dialog(); break;
case DIALOG_SYNTHESIZER: d = SynthesizerDialog.get_dialog(); break;
case DIALOG_SEQUENCER: d = SequencerDialog.get_dialog(); break;
case DIALOG_JUMPER: d = JumperDialog.get_dialog(); break;
case DIALOG_TOUCHFIELD: d = TouchFieldDialog.get_dialog(); break;
case DIALOG_ESCRIPT: d = ScriptDialog.get_dialog(); break;
case DIALOG_ITEM: d = ConsumableDialog.get_dialog(); break;
case DIALOG_SANDBOX_MODE: d = (new ToolDialog()).get_dialog(); break;
case DIALOG_RUBBER: d = RubberDialog.get_dialog(); break;
case DIALOG_SHAPEEXTRUDER: d = ShapeExtruderDialog.get_dialog(); break;
case DIALOG_POLYGON: d = PolygonDialog.get_dialog(); break;
case DIALOG_KEY_LISTENER: d = KeyListenerDialog.get_dialog(); break;
case DIALOG_EMITTER: d = EmitterDialog.get_dialog(); break;
case DIALOG_DECORATION: d = DecorationDialog.get_dialog(); break;
case DIALOG_ANIMAL: d = AnimalDialog.get_dialog(); break;
case DIALOG_SOUNDMAN: d = SoundManDialog.get_dialog(); break;
case DIALOG_MULTI_CONFIG: d = MultiSelectDialog.get_dialog(); break;
case DIALOG_VENDOR: d = VendorDialog.get_dialog(); break;
case DIALOG_FACTORY: d = FactoryDialog.get_dialog(); break;
case DIALOG_RESOURCE: d = ResourceDialog.get_dialog(); break;
case DIALOG_OPEN_STATE: d = (new OpenDialog(true)).get_dialog(); break;
case DIALOG_PUBLISH: d = PublishDialog.get_dialog(); break;
case DIALOG_PUBLISHED: d = (new PublishedDialog()).get_dialog(); break;
case DIALOG_LOGIN: d = LoginDialog.get_dialog(); break;
case DIALOG_SANDBOX_TIPS: d = (new SandboxTipsDialog()).get_dialog(); break;
case DIALOG_REGISTER: d = RegisterDialog.get_dialog(); break;
case CLOSE_ALL_DIALOGS: break; /* do nothing */
case CLOSE_ABSOLUTELY_ALL_DIALOGS:
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
Log.v("Principia", "Closing all dialogs.");
for (Dialog open_dialog : open_dialogs) {
Log.v("Principia", "Closing a dialog["+open_dialog.toString()+"]");
//open_dialog.dismiss();
}
open_dialogs.clear();
}
});
break;
case CLOSE_REGISTER_DIALOG:
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
RegisterDialog.get_dialog().dismiss();
}
});
break;
case DISABLE_REGISTER_LOADER:
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
RegisterDialog.progressbar.setVisibility(View.GONE);
}
});
break;
default: Log.e("Principia", "Unhandled UI Dialog: "+num); break;
}
if (d != null) {
Log.v("Principia", "Adding dialog: "+ d);
//this.open_dialogs.add(d);
if (d.getWindow() != null) {
d.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
return d;
}
public static ArrayAdapter<Level> open_adapter;
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if (v == OpenDialog.lv) {
AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;
final Level level = open_adapter.getItem(aInfo.position);
menu.setHeaderTitle("Options for " + level.get_name());
menu.add(1, 1, 1, "Delete")
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
new ConfirmDialog()
.set_listener(new OnOptionSelectedListener() {
@Override
public void onOptionSelected(int option) {
if (option == ConfirmDialog.OPTION_YES) {
PrincipiaBackend.addActionAsTriple(ACTION_DELETE_LEVEL, level.get_level_type(), level.get_id(), level.get_save_id());
open_adapter.remove(level);
}
}
})
.run("Are you sure you want to delete this level?");
return false;
}
});
} else if (v == ImportDialog.lv) {
AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;
final Level level = ImportDialog.list_adapter.getItem(aInfo.position);
menu.setHeaderTitle("Options for " + level.get_name());
menu.add(1, 1, 1, "Delete")
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
new ConfirmDialog()
.set_listener(new OnOptionSelectedListener() {
@Override
public void onOptionSelected(int option) {
if (option == ConfirmDialog.OPTION_YES) {
PrincipiaBackend.addActionAsInt(ACTION_DELETE_PARTIAL, level.get_id());
ImportDialog.list_adapter.remove(level);
}
}
})
.run("Are you sure you want to delete this object?");
return false;
}
});
}
/* For the details option:
* Level ID
* Level name
* Date modified
*/
}
@Override
public void onPrepareDialog(int d, Dialog dialog, Bundle bundle)
{
switch (d) {
case DIALOG_SETTINGS:
if (settings_dialog != null) {
settings_dialog.load();
}
break;
case DIALOG_QUICKADD: QuickaddDialog.prepare(dialog); break;
case DIALOG_LEVEL_PROPERTIES: LevelDialog.prepare(dialog); break;
case DIALOG_SAVE: SaveAsDialog.prepare(dialog); break;
case DIALOG_SAVE_COPY: SaveAsDialog.prepare(dialog); break;
case DIALOG_ROBOT: RobotDialog.prepare(dialog); break;
case DIALOG_CAMTARGETER: CamTargeterDialog.prepare(dialog); break;
case DIALOG_SET_COMMAND: CommandPadDialog.prepare(dialog); break;
case DIALOG_FXEMITTER: FxEmitterDialog.prepare(dialog); break;
case DIALOG_EVENTLISTENER: EventListenerDialog.prepare(dialog); break;
case DIALOG_SET_PKG_LEVEL: PkgLevelDialog.prepare(dialog); break;
case DIALOG_PIXEL_COLOR: ColorChooserDialog.prepare(dialog, true); break;
case DIALOG_BEAM_COLOR: ColorChooserDialog.prepare(dialog, false); break;
case DIALOG_POLYGON_COLOR: ColorChooserDialog.prepare(dialog, false); break;
case DIALOG_DIGITAL_DISPLAY: DigitalDisplayDialog.prepare(dialog); break;
case DIALOG_SET_FREQUENCY: FrequencyDialog.prepare(dialog); break;
case DIALOG_SET_FREQ_RANGE: FrequencyRangeDialog.prepare(dialog); break;
case DIALOG_EXPORT: ExportDialog.prepare(dialog); break;
case DIALOG_STICKY: StickyDialog.prepare(dialog); break;
case DIALOG_TIMER: TimerDialog.prepare(dialog); break;
case DIALOG_PROMPT_SETTINGS: PromptSettingsDialog.prepare(dialog); break;
case DIALOG_SFX_EMITTER: SfxDialog.prepare(dialog); break;
case DIALOG_SFX_EMITTER_2: Sfx2Dialog.prepare(dialog); break;
case DIALOG_VARIABLE: VariableDialog.prepare(dialog); break;
case DIALOG_SYNTHESIZER: SynthesizerDialog.prepare(dialog); break;
case DIALOG_SEQUENCER: SequencerDialog.prepare(dialog); break;
case DIALOG_JUMPER: JumperDialog.prepare(dialog); break;
case DIALOG_TOUCHFIELD: TouchFieldDialog.prepare(dialog); break;
case DIALOG_ESCRIPT: ScriptDialog.prepare(dialog); break;
case DIALOG_ITEM: ConsumableDialog.prepare(dialog); break;
case DIALOG_RUBBER: RubberDialog.prepare(dialog); break;
case DIALOG_SHAPEEXTRUDER: ShapeExtruderDialog.prepare(dialog); break;
case DIALOG_POLYGON: PolygonDialog.prepare(dialog); break;
case DIALOG_KEY_LISTENER: KeyListenerDialog.prepare(dialog); break;
case DIALOG_EMITTER: EmitterDialog.prepare(dialog); break;
case DIALOG_DECORATION: DecorationDialog.prepare(dialog); break;
case DIALOG_ANIMAL: AnimalDialog.prepare(dialog); break;
case DIALOG_SOUNDMAN: SoundManDialog.prepare(dialog); break;
case DIALOG_MULTI_CONFIG: MultiSelectDialog.prepare(dialog); break;
case DIALOG_VENDOR: VendorDialog.prepare(dialog); break;
case DIALOG_FACTORY: FactoryDialog.prepare(dialog); break;
case DIALOG_RESOURCE: ResourceDialog.prepare(dialog); break;
case DIALOG_PUBLISH: PublishDialog.prepare(dialog); break;
case DIALOG_LOGIN: LoginDialog.prepare(dialog); break;
}
/* Dialogs that need a separate onShowListener */
switch (d) {
case DIALOG_QUICKADD:
case DIALOG_PUBLISH:
case DIALOG_LOGIN:
case DIALOG_SANDBOX_TIPS:
case DIALOG_REGISTER:
case DIALOG_PROMPT_SETTINGS:
case DIALOG_OPEN:
case DIALOG_OPEN_STATE:
case DIALOG_OPEN_OBJECT:
case DIALOG_MULTIEMITTER:
break;
default: dialog.setOnShowListener(this); break;
}
/* Dialogs that need to dismiss HARD */
switch (d) {
default: dialog.setOnDismissListener(this); break;
}
//dialog.setOnCancelListener(this);
}
public void onDismiss(DialogInterface dialog)
{
Log.v("Principia", "dialog onDismiss called");
open_dialogs.remove(dialog);
num_dialogs --;
if (num_dialogs <= 0){
num_dialogs = 0;
PrincipiaBackend.focusGL(true);
}
}
public void onShow(DialogInterface dialog) {
Log.v("Principia", "dialog onShow called");
this.open_dialogs.add((Dialog) dialog);
num_dialogs ++;
if (num_dialogs == 1) {
PrincipiaBackend.focusGL(false);
}
}
public static void on_show(DialogInterface dialog) {
Log.v("Principia", "dialog onShow called");
num_dialogs ++;
if (num_dialogs == 1) {
PrincipiaBackend.focusGL(false);
}
}
@Override
public void onProgressChanged(SeekBar sb, int progress,
boolean fromUser) {
Log.v("Principia", "Progress changed");
if (sb == SynthesizerDialog.synth_pulse_width) {
SynthesizerDialog.synth_pulse_width_tv.setText(String.format(Locale.US, "%.3f", ((float)progress) / 100.f));
} else if (sb == SynthesizerDialog.synth_bitcrushing) {
SynthesizerDialog.synth_bitcrushing_tv.setText(Integer.toString(progress));
} else if (sb == SynthesizerDialog.synth_volume_vibrato_hz) {
SynthesizerDialog.synth_volume_vibrato_hz_tv.setText(Integer.toString(progress));
} else if (sb == SynthesizerDialog.synth_volume_vibrato_extent) {
SynthesizerDialog.synth_volume_vibrato_extent_tv.setText(String.format(Locale.US, "%.3f", ((float)progress) / 100.f));
} else if (sb == SynthesizerDialog.synth_freq_vibrato_hz) {
SynthesizerDialog.synth_freq_vibrato_hz_tv.setText(Integer.toString(progress));
} else if (sb == SynthesizerDialog.synth_freq_vibrato_extent) {
SynthesizerDialog.synth_freq_vibrato_extent_tv.setText(String.format(Locale.US, "%.3f", ((float)progress) / 100.f));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
private void handle_intent(Intent i)
{
Log.v("Principia", "intent new!");
if (i != null) {
if (i.getScheme() != null && i.getScheme().equals("principia")) {
PrincipiaBackend.setarg(i.getDataString());
}
}
}
@Override
public void onNewIntent(Intent i)
{
super.onNewIntent(i);
handle_intent(i);
}
}

View file

@ -0,0 +1,8 @@
package com.bithack.principia;
import org.libsdl.app.SDLActivity;
public class TMSActivity extends SDLActivity
{
}

View file

@ -1,7 +1,9 @@
package com.bithack.principia.shared;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.PrincipiaBackend;
import com.bithack.principia.R;
import android.app.AlertDialog;
import android.app.Dialog;
@ -26,7 +28,7 @@ public class AnimalDialog {
s_animal = (Spinner)view.findViewById(R.id.s_animal);
String[] consumables = PrincipiaBackend.getAnimals().split(",.,");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
s_animal.setAdapter(spinnerArrayAdapter);
_dialog = new AlertDialog.Builder(PrincipiaActivity.mSingleton)

View file

@ -1,6 +1,7 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
@ -19,12 +20,12 @@ public class AutosaveDialog
.setMessage("Autosave file detected. Open or remove?")
.setPositiveButton("Open", new OnClickListener(){
public void onClick(DialogInterface dialog, int which){
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_OPEN_AUTOSAVE, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_OPEN_AUTOSAVE, 0);
}}
)
.setNegativeButton("Remove", new OnClickListener(){
public void onClick(DialogInterface dialog, int which){
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_REMOVE_AUTOSAVE, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_REMOVE_AUTOSAVE, 0);
}}
)
.create();

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -3,7 +3,7 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,8 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import android.app.AlertDialog;
@ -18,13 +20,13 @@ public class CommunityDialog
.setMessage("Do you want to return to the community site or to the main menu?")
.setPositiveButton("Community", new OnClickListener(){
public void onClick(DialogInterface dialog, int which){
PrincipiaActivity.wv.loadUrl(PrincipiaBackend.getCurrentCommunityUrl());
PrincipiaActivity.wv_dialog.show();
SDLActivity.wv.loadUrl(PrincipiaBackend.getCurrentCommunityUrl());
SDLActivity.wv_dialog.show();
}}
)
.setNegativeButton("Main menu", new OnClickListener(){
public void onClick(DialogInterface dialog, int which){
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_GOTO_MAINMENU, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_GOTO_MAINMENU, 0);
}}
)
.create();

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.content.DialogInterface;
@ -43,7 +44,7 @@ public class ConfirmDialog
{
final CheckBox cb;
AlertDialog dialog = new AlertDialog.Builder(PrincipiaActivity.getContext()).create();
AlertDialog dialog = new AlertDialog.Builder(SDLActivity.getContext()).create();
if (dna_sandbox_back) {
View view = LayoutInflater.from(PrincipiaActivity.mSingleton).inflate(R.layout.confirm_sandbox, null);
dialog.setView(view);
@ -54,8 +55,8 @@ public class ConfirmDialog
cb = null;
}
dialog.setCancelable(true);
dialog.setOnShowListener(PrincipiaActivity.mSingleton);
dialog.setOnDismissListener(PrincipiaActivity.mSingleton);
dialog.setOnShowListener(SDLActivity.mSingleton);
dialog.setOnDismissListener(SDLActivity.mSingleton);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, button1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
if (mListener != null) {

View file

@ -1,6 +1,8 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import android.app.AlertDialog;
@ -26,7 +28,7 @@ public class ConsumableDialog {
s_consumable = (Spinner)view.findViewById(R.id.s_consumable);
String[] consumables = PrincipiaBackend.getConsumables().split(",");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
s_consumable.setAdapter(spinnerArrayAdapter);
_dialog = new AlertDialog.Builder(PrincipiaActivity.mSingleton)

View file

@ -1,6 +1,8 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import android.app.AlertDialog;
@ -26,7 +28,7 @@ public class DecorationDialog {
s_deco = (Spinner)view.findViewById(R.id.s_deco);
String[] consumables = PrincipiaBackend.getDecorations().split(",.,");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, consumables);
s_deco.setAdapter(spinnerArrayAdapter);
_dialog = new AlertDialog.Builder(PrincipiaActivity.mSingleton)

View file

@ -4,7 +4,8 @@ import java.util.ArrayList;
import java.util.List;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -63,7 +64,7 @@ public class DigitalDisplayDialog {
ll_dd = (LinearLayout)view.findViewById(R.id.ll_dd);
ll_wrap = (LinearLayout)view.findViewById(R.id.display_ll_wrap);
np_initial_position = new com.bithack.principia.shared.NumberPicker(PrincipiaActivity.getContext());
np_initial_position = new com.bithack.principia.shared.NumberPicker(SDLActivity.getContext());
np_initial_position.setRange(MIN_INITIAL_POS, 40);
ll_dd.addView((View)np_initial_position);
@ -132,7 +133,7 @@ public class DigitalDisplayDialog {
new_str.setCharAt(y, (isChecked?'1':'0'));
symbols.set(cur_symbol, new_str.toString());
} catch (StringIndexOutOfBoundsException e) {
Log.e("Principia", "An unknown error occurred: " + e.getMessage());
Log.e("Principia", "An unknown error occured: " + e.getMessage());
}
}
});
@ -180,7 +181,7 @@ public class DigitalDisplayDialog {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (num_symbols == 40) {
PrincipiaActivity.message("Maximum number of symbols reached.", 0);
SDLActivity.message("Maximum number of symbols reached.", 0);
return false;
}
@ -199,7 +200,7 @@ public class DigitalDisplayDialog {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (num_symbols == 40) {
PrincipiaActivity.message("Maximum number of symbols reached.", 0);
SDLActivity.message("Maximum number of symbols reached.", 0);
return false;
}

View file

@ -4,7 +4,7 @@ import java.util.Locale;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -5,7 +5,7 @@ import java.util.ArrayList;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,7 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
@ -22,7 +23,7 @@ public class ImportDialog
private final String[] level_names;
public static ListView lv;
public static ArrayAdapter<Level> list_adapter = new ArrayAdapter<Level>(PrincipiaActivity.mSingleton,
public static ArrayAdapter<Level> list_adapter = new ArrayAdapter<Level>(SDLActivity.mSingleton,
android.R.layout.select_dialog_item);
public ImportDialog(final boolean is_multiemitter)
@ -31,7 +32,7 @@ public class ImportDialog
AlertDialog.Builder bld = new AlertDialog.Builder(PrincipiaActivity.mSingleton);
String level_list = PrincipiaBackend.getLevels(PrincipiaActivity.LEVEL_PARTIAL);
String level_list = PrincipiaBackend.getLevels(SDLActivity.LEVEL_PARTIAL);
String[] levels = level_list.split("\n");
level_names = new String[levels.length];
@ -81,7 +82,7 @@ public class ImportDialog
this._dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
ListView lv = _dialog.getListView();
ImportDialog.lv = lv;
if (lv != null) {
@ -103,7 +104,7 @@ public class ImportDialog
}
});
lv.setAdapter(ImportDialog.list_adapter);
PrincipiaActivity.mSingleton.registerForContextMenu(lv);
SDLActivity.mSingleton.registerForContextMenu(lv);
} else {
Log.v("Principia", "listview = null");
}

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import java.util.ArrayList;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -42,7 +43,7 @@ public class LoginDialog
_dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
Button b = _dialog.getButton(AlertDialog.BUTTON_POSITIVE);
@ -54,7 +55,7 @@ public class LoginDialog
String password = et_password.getText().toString().trim();
if (username.length() <= 0 || password.length() <= 0) {
PrincipiaActivity.message("You must enter a valid username and password.", 0);
SDLActivity.message("You must enter a valid username and password.", 0);
return;
}
@ -73,7 +74,7 @@ public class LoginDialog
btn_register_account.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PrincipiaActivity.open_dialog(PrincipiaActivity.DIALOG_REGISTER);
SDLActivity.open_dialog(SDLActivity.DIALOG_REGISTER);
_dialog.dismiss();
}
});

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import java.util.ArrayList;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.util.Log;

View file

@ -24,7 +24,7 @@ import android.widget.ToggleButton;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import java.util.ArrayList;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;

View file

@ -1,6 +1,8 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import android.app.AlertDialog;
@ -23,11 +25,11 @@ public class OpenDialog
public OpenDialog(final boolean is_state)
{
PrincipiaActivity.open_adapter.clear();
SDLActivity.open_adapter.clear();
AlertDialog.Builder bld = new AlertDialog.Builder(PrincipiaActivity.mSingleton);
String level_list = PrincipiaBackend.getLevels(is_state ? PrincipiaActivity.LEVEL_LOCAL_STATE : PrincipiaActivity.LEVEL_LOCAL);
String level_list = PrincipiaBackend.getLevels(is_state ? SDLActivity.LEVEL_LOCAL_STATE : SDLActivity.LEVEL_LOCAL);
Log.v("Principia", "Level list: " + level_list);
String[] levels = level_list.split("\n");
@ -58,7 +60,7 @@ public class OpenDialog
Log.v("Principia", "Adding "+name);
PrincipiaActivity.open_adapter.add(l);
SDLActivity.open_adapter.add(l);
level_names[x] = name;
}
@ -80,7 +82,7 @@ public class OpenDialog
@Override
public void onShow(DialogInterface dialog)
{
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
ListView lv = _dialog.getListView();
OpenDialog.lv = lv;
if (lv != null) {
@ -90,7 +92,7 @@ public class OpenDialog
int position, long id) {
Level level = (Level)parent.getAdapter().getItem(position);
if (is_state) {
PrincipiaBackend.openState(level.get_level_type(), level.get_id(), level.get_save_id(), PrincipiaActivity.is_cool); /* XXX */
PrincipiaBackend.openState(level.get_level_type(), level.get_id(), level.get_save_id(), SDLActivity.is_cool); /* XXX */
} else {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_OPEN, level.get_id());
}
@ -98,8 +100,8 @@ public class OpenDialog
}
});
lv.setAdapter(PrincipiaActivity.open_adapter);
PrincipiaActivity.mSingleton.registerForContextMenu(lv);
lv.setAdapter(SDLActivity.open_adapter);
SDLActivity.mSingleton.registerForContextMenu(lv);
}
}
});

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -26,7 +27,7 @@ public class PkgLevelDialog {
view = LayoutInflater.from(PrincipiaActivity.mSingleton).inflate(R.layout.pkg_level_id, null);
np_level_id = new com.bithack.principia.shared.NumberPicker(PrincipiaActivity.getContext());
np_level_id = new com.bithack.principia.shared.NumberPicker(SDLActivity.getContext());
np_level_id.setRange(0, 255);
np_level_id.setValue(1);

View file

@ -3,8 +3,9 @@ package com.bithack.principia.shared;
import java.util.ArrayList;
import java.util.List;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.R;
import android.app.AlertDialog;
@ -20,17 +21,17 @@ public class PlayDialog
*/
static final String[] shared_items = new String[] {
PrincipiaActivity.mSingleton.getString(R.string.open_save),
PrincipiaActivity.mSingleton.getString(R.string.back_to_sandbox),
PrincipiaActivity.mSingleton.getString(R.string.back_to_main_menu),
PrincipiaActivity.mSingleton.getString(R.string.cancel)
SDLActivity.mSingleton.getString(R.string.open_save),
SDLActivity.mSingleton.getString(R.string.back_to_sandbox),
SDLActivity.mSingleton.getString(R.string.back_to_main_menu),
SDLActivity.mSingleton.getString(R.string.cancel)
};
static final String save_state = PrincipiaActivity.mSingleton.getString(R.string.save_state);
static final String save_state = SDLActivity.mSingleton.getString(R.string.save_state);
static final String[] community_items = new String[] {
PrincipiaActivity.mSingleton.getString(R.string.restart_level),
PrincipiaActivity.mSingleton.getString(R.string.back_to_community)
SDLActivity.mSingleton.getString(R.string.restart_level),
SDLActivity.mSingleton.getString(R.string.back_to_community)
};
public static Dialog create_dialog()
@ -40,28 +41,28 @@ public class PlayDialog
if (source > 100) {
source -= 100;
}
AlertDialog.Builder bld = new AlertDialog.Builder(PrincipiaActivity.mSingleton);
AlertDialog.Builder bld = new AlertDialog.Builder(SDLActivity.mSingleton);
List<CharSequence> items = new ArrayList<CharSequence>();
if (PrincipiaBackend.getLevelFlag(33)) {
items.add(save_state);
}
items.add(PrincipiaActivity.mSingleton.getString(R.string.open_save));
items.add(SDLActivity.mSingleton.getString(R.string.open_save));
if (PrincipiaBackend.isAdventure()) {
items.add(PrincipiaActivity.mSingleton.getString(R.string.selfdestruct));
items.add(SDLActivity.mSingleton.getString(R.string.selfdestruct));
}
if (source == 1) {
items.add(PrincipiaActivity.mSingleton.getString(R.string.restart_level));
items.add(PrincipiaActivity.mSingleton.getString(R.string.back_to_community));
items.add(SDLActivity.mSingleton.getString(R.string.restart_level));
items.add(SDLActivity.mSingleton.getString(R.string.back_to_community));
} else if (source == 0) {
items.add(PrincipiaActivity.mSingleton.getString(R.string.back_to_sandbox));
items.add(SDLActivity.mSingleton.getString(R.string.back_to_sandbox));
}
items.add(PrincipiaActivity.mSingleton.getString(R.string.back_to_main_menu));
items.add(PrincipiaActivity.mSingleton.getString(R.string.cancel));
items.add(SDLActivity.mSingleton.getString(R.string.back_to_main_menu));
items.add(SDLActivity.mSingleton.getString(R.string.cancel));
final CharSequence[] real_items = items.toArray(new CharSequence[items.size()]);
@ -70,26 +71,26 @@ public class PlayDialog
String cool = real_items[which].toString();
if (cool.equalsIgnoreCase("open save")) {
PrincipiaActivity.mSingleton.runOnUiThread(new Runnable(){
SDLActivity.mSingleton.runOnUiThread(new Runnable(){
public void run() {
try { PrincipiaActivity.mSingleton.removeDialog(PrincipiaActivity.DIALOG_OPEN); } catch(Exception e) {};
try { PrincipiaActivity.mSingleton.removeDialog(PrincipiaActivity.DIALOG_OPEN_STATE); } catch(Exception e) {};
try { SDLActivity.mSingleton.removeDialog(SDLActivity.DIALOG_OPEN); } catch(Exception e) {};
try { SDLActivity.mSingleton.removeDialog(SDLActivity.DIALOG_OPEN_STATE); } catch(Exception e) {};
}
});
PrincipiaActivity.mSingleton.showDialog(PrincipiaActivity.DIALOG_OPEN_STATE);
SDLActivity.mSingleton.showDialog(SDLActivity.DIALOG_OPEN_STATE);
} else if (cool.equalsIgnoreCase("back to sandbox")) {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_BACK, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_BACK, 0);
} else if (cool.equalsIgnoreCase("back to community")) {
PrincipiaActivity.wv.loadUrl(PrincipiaBackend.getCurrentCommunityUrl());
PrincipiaActivity.wv_dialog.show();
SDLActivity.wv.loadUrl(PrincipiaBackend.getCurrentCommunityUrl());
SDLActivity.wv_dialog.show();
} else if (cool.equalsIgnoreCase("save state")) {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_SAVE_STATE, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_SAVE_STATE, 0);
} else if (cool.equalsIgnoreCase("back to main menu")) {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_GOTO_MAINMENU, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_GOTO_MAINMENU, 0);
} else if (cool.equalsIgnoreCase("restart level")) {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_RESTART_LEVEL, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_RESTART_LEVEL, 0);
} else if (cool.equalsIgnoreCase("self-destruct")) {
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_SELF_DESTRUCT, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_SELF_DESTRUCT, 0);
} else {
Log.e("PRINCIPIA", "UNKNOWN THING: " + cool);
}
@ -109,7 +110,7 @@ public class PlayDialog
.setNeutralButton("Back", new OnClickListener(){
public void onClick(DialogInterface dialog, int which)
{
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_BACK, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_BACK, 0);
}
})
.setNegativeButton("Cancel", new OnClickListener(){

View file

@ -4,7 +4,7 @@ import java.util.Locale;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -6,7 +6,7 @@ import java.util.List;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -40,7 +41,7 @@ public class PromptSettingsDialog
_dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
Button b = _dialog.getButton(AlertDialog.BUTTON_POSITIVE);
@ -58,12 +59,12 @@ public class PromptSettingsDialog
int message_len = message.length();
if (message_len <= 0) {
PrincipiaActivity.message("You must enter a message for the prompt.", 0);
SDLActivity.message("You must enter a message for the prompt.", 0);
return;
}
if (b1_len <= 0 && b2_len <= 0 && b3_len <= 0) {
PrincipiaActivity.message("You must use at least one button.", 0);
SDLActivity.message("You must use at least one button.", 0);
return;
}

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -40,7 +41,7 @@ public class PublishDialog
_dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
Button b = _dialog.getButton(AlertDialog.BUTTON_POSITIVE);
@ -52,7 +53,7 @@ public class PublishDialog
String descr = et_descr.getText().toString().trim();
if (name.length() <= 0) {
PrincipiaActivity.message("You must enter a name for your level!", 0);
SDLActivity.message("You must enter a name for your level!", 0);
return;
}
@ -60,7 +61,7 @@ public class PublishDialog
PrincipiaBackend.setLevelDescription(descr);
PrincipiaBackend.setLevelLocked(cb_locked.isChecked());
PrincipiaBackend.addActionAsInt(PrincipiaActivity.ACTION_PUBLISH, 0);
PrincipiaBackend.addActionAsInt(SDLActivity.ACTION_PUBLISH, 0);
_dialog.dismiss();
}

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -43,7 +44,7 @@ public class RegisterDialog
_dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
Button b = _dialog.getButton(AlertDialog.BUTTON_POSITIVE);
@ -57,22 +58,22 @@ public class RegisterDialog
final String email = et_email.getText().toString().trim();
if (password.length() < 6 || password.length() > 100) {
PrincipiaActivity.message("Your password must be at least 3 and at most 100 characters.", 0);
SDLActivity.message("Your password must be at least 3 and at most 100 characters.", 0);
return;
}
if (!password.equals(password_confirm)) {
PrincipiaActivity.message("The two passwords you entered don't match.", 0);
SDLActivity.message("The two passwords you entered don't match.", 0);
return;
}
if (username.length() < 3 || username.length() > 20) {
PrincipiaActivity.message("Your username must be at least 3 and at most 20 characters.", 0);
SDLActivity.message("Your username must be at least 3 and at most 20 characters.", 0);
return;
}
if (!android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
PrincipiaActivity.message("You must enter a valid email address.", 0);
SDLActivity.message("You must enter a valid email address.", 0);
return;
}

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.bithack.principia.shared.MultiSpinner.MultiSpinnerListener;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -33,13 +34,13 @@ public class SandboxTipsDialog
.setTitle("Tips & tricks")
.setPositiveButton("OK", new OnClickListener(){public void onClick(DialogInterface dialog, int which) {}})
.setNeutralButton("Next", null)
.setNegativeButton("More tips & tricks", new OnClickListener(){public void onClick(DialogInterface dialog, int which){PrincipiaActivity.open_url("https://principia-web.se/wiki/Getting_Started");}})
.setNegativeButton("More tips & tricks", new OnClickListener(){public void onClick(DialogInterface dialog, int which){SDLActivity.open_url("https://principia-web.se/wiki/Getting_Started");}})
.create();
_dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
PrincipiaActivity.on_show(dialog);
SDLActivity.on_show(dialog);
Button b = _dialog.getButton(AlertDialog.BUTTON_NEUTRAL);

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -5,7 +5,7 @@ import com.bithack.principia.R;
import com.bithack.principia.shared.ConfirmDialog.OnOptionSelectedListener;
import com.bithack.principia.shared.CustomLinearLayout.OnKeyboardStateChangedListener;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -2,7 +2,7 @@ package com.bithack.principia.shared;
import java.util.Locale;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -13,7 +13,8 @@ import android.widget.Spinner;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
public class Sfx2Dialog {
static Dialog _dialog;
@ -35,7 +36,7 @@ public class Sfx2Dialog {
s_sfx = (Spinner)view.findViewById(R.id.s_sfx);
String[] sound_effects = PrincipiaBackend.getSounds().split(",.,");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, sound_effects);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, sound_effects);
s_sfx.setAdapter(spinnerArrayAdapter);
bld.setTitle("SFX Emitter");
@ -72,6 +73,6 @@ public class Sfx2Dialog {
PrincipiaBackend.setPropertyInt8(1, sfx_global.isChecked() ? 1 : 0);
//TODO: property_index 2 (sound chunk)
PrincipiaBackend.setPropertyInt8(3, sfx_loop.isChecked() ? 1 : 0);
PrincipiaActivity.message("Saved properties for SFX Emitter.", 0);
SDLActivity.message("Saved properties for SFX Emitter.", 0);
}
}

View file

@ -1,6 +1,7 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
@ -32,7 +33,7 @@ public class SfxDialog {
s_sfx = (Spinner)view.findViewById(R.id.s_sfx);
String[] sound_effects = PrincipiaBackend.getSfxSounds().split(",");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, sound_effects);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, sound_effects);
s_sfx.setAdapter(spinnerArrayAdapter);
bld.setTitle("SFX Emitter (Old)");
@ -67,6 +68,6 @@ public class SfxDialog {
{
PrincipiaBackend.setPropertyInt(0, s_sfx.getSelectedItemId());
PrincipiaBackend.setPropertyInt8(1, sfx_global.isChecked() ? 1 : 0);
PrincipiaActivity.message("Saved properties for SFX Emitter.", 0);
SDLActivity.message("Saved properties for SFX Emitter.", 0);
}
}

View file

@ -4,7 +4,7 @@ import java.util.Locale;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -1,6 +1,7 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
@ -32,7 +33,7 @@ public class SoundManDialog {
s_sounds = (Spinner)view.findViewById(R.id.sm_sounds);
String[] array_data = PrincipiaBackend.getSounds().split(",.,");
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(PrincipiaActivity.mSingleton, android.R.layout.select_dialog_item, array_data);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(SDLActivity.mSingleton, android.R.layout.select_dialog_item, array_data);
s_sounds.setAdapter(spinnerArrayAdapter);
_dialog = new AlertDialog.Builder(PrincipiaActivity.mSingleton)

View file

@ -1,7 +1,7 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.Dialog;
import android.os.Bundle;
@ -105,12 +106,12 @@ public class SynthesizerDialog {
synth_freq_vibrato_hz_tv = (TextView)view.findViewById(R.id.synth_freq_vibrato_hz_tv);
synth_freq_vibrato_extent_tv = (TextView)view.findViewById(R.id.synth_freq_vibrato_extent_tv);
synth_pulse_width.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_bitcrushing.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_volume_vibrato_hz.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_volume_vibrato_extent.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_freq_vibrato_hz.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_freq_vibrato_extent.setOnSeekBarChangeListener(PrincipiaActivity.mSingleton);
synth_pulse_width.setOnSeekBarChangeListener(SDLActivity.mSingleton);
synth_bitcrushing.setOnSeekBarChangeListener(SDLActivity.mSingleton);
synth_volume_vibrato_hz.setOnSeekBarChangeListener(SDLActivity.mSingleton);
synth_volume_vibrato_extent.setOnSeekBarChangeListener(SDLActivity.mSingleton);
synth_freq_vibrato_hz.setOnSeekBarChangeListener(SDLActivity.mSingleton);
synth_freq_vibrato_extent.setOnSeekBarChangeListener(SDLActivity.mSingleton);
sp_waveform = (Spinner)view.findViewById(R.id.synth_waveform);
sp_waveform.setOnItemSelectedListener(new OnItemSelectedListener() {
@ -130,7 +131,7 @@ public class SynthesizerDialog {
});
String[] waveforms = PrincipiaBackend.getSynthWaveforms().split(",");
ArrayAdapter<String> spinner_aa = new ArrayAdapter<String>(PrincipiaActivity.getContext(), android.R.layout.select_dialog_item, waveforms);
ArrayAdapter<String> spinner_aa = new ArrayAdapter<String>(SDLActivity.getContext(), android.R.layout.select_dialog_item, waveforms);
sp_waveform.setAdapter(spinner_aa);
}

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;

View file

@ -6,7 +6,7 @@ import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.shared.RangeSeekBar.OnRangeSeekBarChangeListener;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;

View file

@ -2,7 +2,8 @@ package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import org.libsdl.app.SDLActivity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -81,9 +82,9 @@ public class VariableDialog {
String s = et_name.getText().toString().trim().replaceAll("[^a-zA_Z0-9_-]", "");
if (s.length() > 0 && s.length() < 50) {
PrincipiaBackend.setPropertyString(0, s);
PrincipiaActivity.message("Saved variable name.", 0);
SDLActivity.message("Saved variable name.", 0);
} else {
PrincipiaActivity.message("You must enter a valid variable name. a-zA-Z0-9_- allowed.", 0);
SDLActivity.message("You must enter a valid variable name. a-zA-Z0-9_- allowed.", 0);
}
}
}

View file

@ -1,6 +1,6 @@
package com.bithack.principia.shared;
import com.bithack.principia.PrincipiaBackend;
import org.libsdl.app.PrincipiaBackend;
import com.bithack.principia.PrincipiaActivity;
import com.bithack.principia.R;

View file

@ -13,8 +13,9 @@ interface HIDDevice
public String getProductName();
public UsbDevice getDevice();
public boolean open();
public int writeReport(byte[] report, boolean feature);
public boolean readReport(byte[] report, boolean feature);
public int sendFeatureReport(byte[] report);
public int sendOutputReport(byte[] report);
public boolean getFeatureReport(byte[] report);
public void setFrozen(boolean frozen);
public void close();
public void shutdown();

View file

@ -19,13 +19,9 @@ import android.os.*;
import java.lang.Runnable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
@ -37,19 +33,10 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private boolean mIsConnected = false;
private boolean mIsChromebook = false;
private boolean mIsReconnecting = false;
private boolean mHasEnabledNotifications = false;
private boolean mHasSeenInputUpdate = false;
private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
private int mProductId = -1;
private int mReportId = 0;
private UUID mInputCharacteristic;
private static final int D0G_BLE2_PID = 0x1106;
private static final int TRITON_BLE_PID = 0x1303;
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
@ -57,15 +44,11 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristicD0G = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristicTriton_0x45 = UUID.fromString("100F6C7A-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristicTriton_0x47 = UUID.fromString("100F6C7C-1735-4313-B402-38567131E5F3");
static final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
private HashMap<Integer, BluetoothGattCharacteristic> mOutputReportChars = new HashMap<Integer, BluetoothGattCharacteristic>();
static class GattOperation {
private enum Operation {
CHR_READ,
@ -78,7 +61,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
byte[] mValue;
BluetoothGatt mGatt;
boolean mResult = true;
int mDelayMs = 0;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt;
@ -86,13 +68,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mUuid = uuid;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, int delayMs) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mDelayMs = delayMs;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt;
mOp = operation;
@ -100,14 +75,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mValue = value;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value, int delayMs) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
mDelayMs = delayMs;
}
public void run() {
// This is executed in main thread
BluetoothGattCharacteristic chr;
@ -169,8 +136,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
return mResult;
}
public int getDelayMs() { return mDelayMs; }
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
@ -189,38 +154,32 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
}
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid, int delayMs) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid, delayMs);
}
}
HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
mManager = manager;
mDevice = device;
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
mIsRegistered = false;
mIsChromebook = SDLActivity.isChromebook();
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
mOperations = new LinkedList<GattOperation>();
mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt();
mHasEnabledNotifications = false;
mHasSeenInputUpdate = false;
// final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() {
// @Override
// void run() {
// public void run() {
// finalThis.checkConnectionForChromebookIssue();
// }
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
}
String getIdentifier() {
public String getIdentifier() {
return String.format("SteamController.%s", mDevice.getAddress());
}
BluetoothGatt getGatt() {
public BluetoothGatt getGatt() {
return mGatt;
}
@ -260,7 +219,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
}
void reconnect() {
public void reconnect() {
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
mGatt.disconnect();
@ -355,45 +314,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristicTriton_0x45)) {
Log.v(TAG, "Found Triton input characteristic 0x45");
mProductId = TRITON_BLE_PID;
mReportId = 0x45;
mInputCharacteristic = chr.getUuid();
} else if (chr.getUuid().equals(inputCharacteristicTriton_0x47)) {
Log.v(TAG, "Found Triton input characteristic 0x47");
mProductId = TRITON_BLE_PID;
mReportId = 0x47;
mInputCharacteristic = chr.getUuid();
} else if (chr.getUuid().equals(inputCharacteristicD0G)) {
Log.v(TAG, "Found D0G input characteristic");
mProductId = D0G_BLE2_PID;
mReportId = 0x03;
mInputCharacteristic = chr.getUuid();
} else {
Pattern reportPattern = Pattern.compile("100F6C([0-9A-Z]{2})", Pattern.CASE_INSENSITIVE);
Matcher matcher = reportPattern.matcher(chr.getUuid().toString());
if (matcher.find()) {
try {
int reportId = Integer.parseInt(matcher.group(1), 16);
reportId -= 0x35;
if (reportId >= 0x80) {
// This is a Triton output report characteristic that we need to care about.
Log.v(TAG, "Found Triton output report 0x" + Integer.toString(reportId, 16));
mOutputReportChars.put(reportId, chr);
}
}
catch (NumberFormatException nfe) {
Log.w(TAG, "Could not parse report characteristic " + chr.getUuid().toString() + ": " + nfe.toString());
}
}
}
}
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(mInputCharacteristic)) {
if (chr.getUuid().equals(inputCharacteristic)) {
Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
@ -450,30 +372,21 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mCurrentOperation = mOperations.removeFirst();
}
Runnable gattOperationRunnable = new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
// Run in main thread
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mOperations) {
if (mCurrentOperation == null) {
Log.e(TAG, "Current operation null in executor?");
return;
}
mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation
}
};
if (mCurrentOperation.getDelayMs() == 0) {
// Run in main thread
mHandler.post(gattOperationRunnable);
}
else {
// If we have a delay on this operation, wait before we post it.
mHandler.postDelayed(gattOperationRunnable, mCurrentOperation.getDelayMs());
}
}
});
}
private void queueGattOperation(GattOperation op) {
@ -484,47 +397,16 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
}
private void enableNotification(UUID chrUuid) {
// Add a 500ms delay to notification write for Amazon Fire TV devices, as otherwise if we do this too quickly after connecting
// it will return success and then silently drop the operation on the floor.
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid, 500);
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
queueGattOperation(op);
// Amazon Fire devices can also silently timeout on writeDescriptor, so
// set up a little delayed check that will attempt to write a second time.
//
// While this only seems to be needed on Amazon Fire TV devices at present, it
// doesn't hurt to have a retry on other devices as well.
//
final HIDDeviceBLESteamController finalThis = this;
final UUID finalUuid = chrUuid;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (!finalThis.mHasEnabledNotifications) {
if (finalThis.mHasSeenInputUpdate) {
// Amazon Five devices may have enabled notifications on the input characteristic and not given us a callback. If we've seen
// input reports, though, somewhat by definition notifications are enabled.
Log.w(TAG, "WriteDescriptor has never returned, but we've seen input reports. Moving on with controller initialization.");
finalThis.mHasEnabledNotifications = true;
finalThis.enableValveMode();
return;
}
// Give one more try.
GattOperation retry = HIDDeviceBLESteamController.GattOperation.enableNotification(finalThis.mGatt, finalUuid, 500);
finalThis.queueGattOperation(retry);
}
}
}, 1000);
}
void writeCharacteristic(UUID uuid, byte[] value) {
public void writeCharacteristic(UUID uuid, byte[] value) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
queueGattOperation(op);
}
void readCharacteristic(UUID uuid) {
public void readCharacteristic(UUID uuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
queueGattOperation(op);
}
@ -533,7 +415,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
////////////// BluetoothGattCallback overridden methods
//////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
mIsReconnecting = false;
@ -556,7 +437,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onServicesDiscovered status=" + status);
if (status == 0) {
@ -566,33 +446,23 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
} else {
if (getProductId() == TRITON_BLE_PID) {
// Android will not properly play well with Data Length Extensions without manually requesting a large MTU,
// and Triton controllers require DLE support.
//
// 517 is basically a "magic number" as far as Android's bluetooth code is concerned, so do not change
// this value. It is functionally "please enable data length extensions" on some Android builds.
mGatt.requestMtu(517);
}
}
else {
probeService(this);
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
mManager.HIDDeviceReportResponse(getId(), characteristic.getValue());
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
}
finishCurrentGattOperation();
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
@ -600,7 +470,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
// Only register controller with the native side once it has been fully configured
if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true, mReportId);
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
setRegistered();
}
}
@ -608,68 +478,44 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
finishCurrentGattOperation();
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(mInputCharacteristic) && !mFrozen) {
mHasSeenInputUpdate = true;
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Log.v(TAG, "onDescriptorRead status=" + status);
}
private void enableValveMode()
{
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return;
BluetoothGattCharacteristic reportChr = valveService.getCharacteristic(reportCharacteristic);
if (reportChr != null) {
if (getProductId() == TRITON_BLE_PID) {
// For Triton we just mark things registered.
Log.v(TAG, "Registering Triton Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true, mReportId);
setRegistered();
} else {
// For the original controller, we need to manually enter Valve mode.
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
mGatt.writeCharacteristic(reportChr);
}
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(mInputCharacteristic)) {
mHasEnabledNotifications = true;
enableValveMode();
if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
}
finishCurrentGattOperation();
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Log.v(TAG, "onReadRemoteRssi status=" + status);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//Log.v(TAG, "onMtuChanged status=" + status);
}
@ -692,20 +538,9 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
@Override
public int getProductId() {
if (mProductId > 0) {
// We've already set a product ID.
return mProductId;
}
if (mDevice.getName().startsWith("Steam Ctrl")) {
// We're a newer Triton device
mProductId = TRITON_BLE_PID;
} else {
// We're an OG Steam Controller
mProductId = D0G_BLE2_PID;
}
return mProductId;
// We don't have an easy way to query from the Bluetooth device, but we know what it is
final int D0G_BLE2_PID = 0x1106;
return D0G_BLE2_PID;
}
@Override
@ -740,64 +575,50 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
}
@Override
public int writeReport(byte[] report, boolean feature) {
public int sendFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted writeReport before Steam Controller is registered!");
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
if (feature) {
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "writeFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
} else {
// If we're an original-recipe Steam Controller we just write to the characteristic directly.
if (getProductId() == D0G_BLE2_PID) {
//Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
// If we're a Triton, we need to find the correct report characteristic.
if (report.length > 0) {
int reportId = report[0] & 0xFF;
BluetoothGattCharacteristic targetedReportCharacteristic = mOutputReportChars.get(reportId);
if (targetedReportCharacteristic != null) {
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "writeOutputReport 0x" + Integer.toString(reportId, 16) + " " + HexDump.dumpHexString(report));
writeCharacteristic(targetedReportCharacteristic.getUuid(), actual_report);
return report.length;
} else {
Log.w(TAG, "Got report write request for unknown report type 0x" + Integer.toString(reportId, 16));
}
}
}
return -1;
// We need to skip the first byte, as that doesn't go over the air
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
}
@Override
public boolean readReport(byte[] report, boolean feature) {
public int sendOutputReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted readReport before Steam Controller is registered!");
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return -1;
}
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
writeCharacteristic(reportCharacteristic, report);
return report.length;
}
@Override
public boolean getFeatureReport(byte[] report) {
if (!isRegistered()) {
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
if (mIsConnected) {
probeService(this);
}
return false;
}
if (feature) {
readCharacteristic(reportCharacteristic);
return true;
} else {
// Not implemented
return false;
}
//Log.v(TAG, "getFeatureReport");
readCharacteristic(reportCharacteristic);
return true;
}
@Override

View file

@ -32,7 +32,7 @@ public class HIDDeviceManager {
private static HIDDeviceManager sManager;
private static int sManagerRefCount = 0;
static public HIDDeviceManager acquire(Context context) {
public static HIDDeviceManager acquire(Context context) {
if (sManagerRefCount == 0) {
sManager = new HIDDeviceManager(context);
}
@ -40,7 +40,7 @@ public class HIDDeviceManager {
return sManager;
}
static public void release(HIDDeviceManager manager) {
public static void release(HIDDeviceManager manager) {
if (manager == sManager) {
--sManagerRefCount;
if (sManagerRefCount == 0) {
@ -108,12 +108,12 @@ public class HIDDeviceManager {
HIDDeviceRegisterCallback();
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
mIsChromebook = SDLActivity.isChromebook();
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
// if (shouldClear) {
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
// spedit.clear();
// spedit.apply();
// spedit.commit();
// }
// else
{
@ -121,11 +121,11 @@ public class HIDDeviceManager {
}
}
Context getContext() {
public Context getContext() {
return mContext;
}
int getDeviceIDForIdentifier(String identifier) {
public int getDeviceIDForIdentifier(String identifier) {
SharedPreferences.Editor spedit = mSharedPreferences.edit();
int result = mSharedPreferences.getInt(identifier, 0);
@ -135,7 +135,7 @@ public class HIDDeviceManager {
}
spedit.putInt(identifier, result);
spedit.apply();
spedit.commit();
return result;
}
@ -193,11 +193,7 @@ public class HIDDeviceManager {
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
mContext.registerReceiver(mUsbBroadcast, filter, Context.RECEIVER_EXPORTED);
} else {
mContext.registerReceiver(mUsbBroadcast, filter);
}
mContext.registerReceiver(mUsbBroadcast, filter);
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
handleUsbDeviceAttached(usbDevice);
@ -256,8 +252,6 @@ public class HIDDeviceManager {
0x24c6, // PowerA
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x3537, // GameSir
0x37d7, // Flydigi
0x9886, // ASTRO Gaming
};
@ -290,13 +284,9 @@ public class HIDDeviceManager {
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
0x294b, // Snakebyte
0x2dc8, // 8BitDo
0x2e24, // Hyperkin
0x2e95, // SCUF
0x3285, // Nacon
0x3537, // GameSir
0x366c, // ByoWave
};
if (usbInterface.getId() == 0 &&
@ -361,7 +351,7 @@ public class HIDDeviceManager {
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId();
mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false, 0);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
}
}
}
@ -382,7 +372,7 @@ public class HIDDeviceManager {
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
@ -414,11 +404,7 @@ public class HIDDeviceManager {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
mContext.registerReceiver(mBluetoothBroadcast, filter, Context.RECEIVER_EXPORTED);
} else {
mContext.registerReceiver(mBluetoothBroadcast, filter);
}
mContext.registerReceiver(mBluetoothBroadcast, filter);
if (mIsChromebook) {
mHandler = new Handler(Looper.getMainLooper());
@ -445,7 +431,7 @@ public class HIDDeviceManager {
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
// This function provides a sort of dummy version of that, watching for changes in the
// connected devices and attempting to add controllers as things change.
void chromebookConnectionHandler() {
public void chromebookConnectionHandler() {
if (!mIsChromebook) {
return;
}
@ -484,7 +470,7 @@ public class HIDDeviceManager {
}, 10000);
}
boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
synchronized (this) {
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
@ -505,7 +491,7 @@ public class HIDDeviceManager {
return true;
}
void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
synchronized (this) {
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
if (device == null)
@ -519,7 +505,7 @@ public class HIDDeviceManager {
}
}
boolean isSteamController(BluetoothDevice bluetoothDevice) {
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
if (bluetoothDevice == null) {
return false;
@ -530,13 +516,7 @@ public class HIDDeviceManager {
return false;
}
// Steam Controllers will always support Bluetooth Low Energy
if ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) == 0) {
return false;
}
// Match on the name either the original Steam Controller or the new second-generation one advertise with.
return bluetoothDevice.getName().equals("SteamController") || bluetoothDevice.getName().startsWith("Steam Ctrl");
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
@ -579,7 +559,7 @@ public class HIDDeviceManager {
////////// JNI interface functions
//////////////////////////////////////////////////////////////////////////////////////////////////////
boolean initialize(boolean usb, boolean bluetooth) {
public boolean initialize(boolean usb, boolean bluetooth) {
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
if (usb) {
@ -591,7 +571,7 @@ public class HIDDeviceManager {
return true;
}
boolean openDevice(int deviceID) {
public boolean openDevice(int deviceID) {
Log.v(TAG, "openDevice deviceID=" + deviceID);
HIDDevice device = getDevice(deviceID);
if (device == null) {
@ -611,10 +591,13 @@ public class HIDDeviceManager {
} else {
flags = 0;
}
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
intent.setPackage(mContext.getPackageName());
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
intent.setPackage(mContext.getPackageName());
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
} else {
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
}
} catch (Exception e) {
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
HIDDeviceOpenResult(deviceID, false);
@ -630,9 +613,9 @@ public class HIDDeviceManager {
return false;
}
int writeReport(int deviceID, byte[] report, boolean feature) {
public int sendOutputReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "writeReport deviceID=" + deviceID + " length=" + report.length);
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
@ -640,16 +623,33 @@ public class HIDDeviceManager {
return -1;
}
return device.writeReport(report, feature);
return device.sendOutputReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
boolean readReport(int deviceID, byte[] report, boolean feature) {
public int sendFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "readReport deviceID=" + deviceID);
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
HIDDeviceDisconnected(deviceID);
return -1;
}
return device.sendFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return -1;
}
public boolean getFeatureReport(int deviceID, byte[] report) {
try {
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
HIDDevice device;
device = getDevice(deviceID);
if (device == null) {
@ -657,14 +657,14 @@ public class HIDDeviceManager {
return false;
}
return device.readReport(report, feature);
return device.getFeatureReport(report);
} catch (Exception e) {
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
}
return false;
}
void closeDevice(int deviceID) {
public void closeDevice(int deviceID) {
try {
Log.v(TAG, "closeDevice deviceID=" + deviceID);
HIDDevice device;
@ -688,11 +688,11 @@ public class HIDDeviceManager {
private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth, int reportID);
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID);
native void HIDDeviceInputReport(int deviceID, byte[] report);
native void HIDDeviceReportResponse(int deviceID, byte[] report);
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
}

View file

@ -4,7 +4,6 @@ import android.hardware.usb.*;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
import java.util.Locale;
class HIDDeviceUSB implements HIDDevice {
@ -21,7 +20,6 @@ class HIDDeviceUSB implements HIDDevice {
protected InputThread mInputThread;
protected boolean mRunning;
protected boolean mFrozen;
protected boolean mClaimed;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager;
@ -30,11 +28,10 @@ class HIDDeviceUSB implements HIDDevice {
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false;
mClaimed = false;
}
String getIdentifier() {
return String.format(Locale.ENGLISH, "%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
public String getIdentifier() {
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
}
@Override
@ -55,11 +52,13 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getSerialNumber() {
String result = null;
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
try {
result = mDevice.getSerialNumber();
}
catch (SecurityException exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
}
}
if (result == null) {
result = "";
@ -74,8 +73,10 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getManufacturerName() {
String result;
result = mDevice.getManufacturerName();
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getManufacturerName();
}
if (result == null) {
result = String.format("%x", getVendorId());
}
@ -84,8 +85,10 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getProductName() {
String result;
result = mDevice.getProductName();
String result = null;
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getProductName();
}
if (result == null) {
result = String.format("%x", getProductId());
}
@ -97,7 +100,7 @@ class HIDDeviceUSB implements HIDDevice {
return mDevice;
}
String getDeviceName() {
public String getDeviceName() {
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
}
@ -116,7 +119,6 @@ class HIDDeviceUSB implements HIDDevice {
close();
return false;
}
mClaimed = true;
// Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) {
@ -135,12 +137,9 @@ class HIDDeviceUSB implements HIDDevice {
}
}
// Make sure the required endpoints were present. The original Steam Controller and the wireless dongle for it do NOT
// actually have -- or require -- output endpoints, so we need to accept only an input one for them or else we'll fall
// back to the Android system gamepad functionality (and lose our paddles et al).
if (mInputEndpoint == null) {
// Make sure the required endpoints were present
if (mInputEndpoint == null || mOutputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
mConnection.releaseInterface(iface);
close();
return false;
}
@ -154,80 +153,55 @@ class HIDDeviceUSB implements HIDDevice {
}
@Override
public int writeReport(byte[] report, boolean feature) {
if (mConnection == null) {
Log.w(TAG, "writeReport() called with no device connection");
return -1;
}
if (!mClaimed) {
Log.w(TAG, "writeReport() called but some other process currently owns the USB device");
return -1;
}
if (feature) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "writeFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
} else {
if (mOutputEndpoint == null)
{
Log.e(TAG, "Tried to write an output report to an interface with no output endpoint!");
return -1;
}
int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (res != report.length) {
Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName());
}
return res;
}
}
@Override
public boolean readReport(byte[] report, boolean feature) {
public int sendFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (mConnection == null) {
Log.w(TAG, "readReport() called with no device connection");
return false;
if (report_number == 0x0) {
++offset;
--length;
skipped_report_id = true;
}
if (!mClaimed) {
if (feature) {
return false;
}
return true;
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
0x09/*HID set_report*/,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
if (res < 0) {
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
return -1;
}
if (skipped_report_id) {
++length;
}
return length;
}
@Override
public int sendOutputReport(byte[] report) {
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (r != report.length) {
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
}
return r;
}
@Override
public boolean getFeatureReport(byte[] report) {
int res = -1;
int offset = 0;
int length = report.length;
boolean skipped_report_id = false;
byte report_number = report[0];
if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID
will remain in byte 0. */
@ -239,7 +213,7 @@ class HIDDeviceUSB implements HIDDevice {
res = mConnection.controlTransfer(
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
0x01/*HID get_report*/,
((feature ? 3/*HID feature*/ : 1/*HID Input*/) << 8) | report_number,
(3/*HID feature*/ << 8) | report_number,
mInterface,
report, offset, length,
1000/*timeout millis*/);
@ -260,7 +234,7 @@ class HIDDeviceUSB implements HIDDevice {
} else {
data = Arrays.copyOfRange(report, 0, res);
}
mManager.HIDDeviceReportResponse(mDeviceId, data);
mManager.HIDDeviceFeatureReport(mDeviceId, data);
return true;
}
@ -280,13 +254,10 @@ class HIDDeviceUSB implements HIDDevice {
mInputThread = null;
}
if (mConnection != null) {
if (mClaimed) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
}
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
mConnection.close();
mConnection = null;
mClaimed = false;
}
}
@ -299,22 +270,6 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public void setFrozen(boolean frozen) {
mFrozen = frozen;
/* If we have a valid device connection and the claim state doesn't match what we want, try to correct that. */
if (mConnection != null && mClaimed == mFrozen) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (frozen) {
mClaimed = !mConnection.releaseInterface(iface);
if (mClaimed) {
Log.e(TAG, "Tried to release claim on USB device, but failed!");
}
} else {
mClaimed = mConnection.claimInterface(iface, true);
if (!mClaimed) {
Log.e(TAG, "Tried to regain claim on USB device, but failed!");
}
}
}
}
protected class InputThread extends Thread {

View file

@ -1,4 +1,4 @@
package com.bithack.principia;
package org.libsdl.app;
import com.bithack.principia.shared.Settings;
@ -87,7 +87,7 @@ public class PrincipiaBackend
float creature_absorb_time,
float player_respawn_time
);
public static native void setLevelAllowDerivatives(boolean state);
public static native void setLevelLocked(boolean state);
public static native void setarg(String arg);

View file

@ -1,8 +1,8 @@
package org.libsdl.app;
import android.app.Activity;
import android.content.Context;
import java.lang.Class;
import java.lang.reflect.Method;
/**
@ -12,14 +12,14 @@ public class SDL {
// This function should be called first and sets up the native code
// so it can call into the Java classes
static public void setupJNI() {
public static void setupJNI() {
SDLActivity.nativeSetupJNI();
SDLAudioManager.nativeSetupJNI();
SDLControllerManager.nativeSetupJNI();
}
// This function should be called each time the activity is started
static public void initialize() {
public static void initialize() {
setContext(null);
SDLActivity.initialize();
@ -28,33 +28,33 @@ public class SDL {
}
// This function stores the current activity (SDL or not)
static public void setContext(Activity context) {
public static void setContext(Context context) {
SDLAudioManager.setContext(context);
mContext = context;
}
static public Activity getContext() {
public static Context getContext() {
return mContext;
}
static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
loadLibrary(libraryName, mContext);
}
static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
if (libraryName == null) {
throw new NullPointerException("No library name provided.");
}
try {
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// Let's see if we have ReLinker available in the project. This is necessary for
// some projects that have huge numbers of local libraries bundled, and thus may
// trip a bug in Android's native library loader which ReLinker works around. (If
// loadLibrary works properly, ReLinker will simply use the normal Android method
// internally.)
//
// To use ReLinker, just add it as a dependency. For more information, see
// To use ReLinker, just add it as a dependency. For more information, see
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
//
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
@ -62,7 +62,7 @@ public class SDL {
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
// they've changed during updates.
Method forceMethod = relinkClass.getDeclaredMethod("force");
Object relinkInstance = forceMethod.invoke(null);
@ -86,5 +86,5 @@ public class SDL {
}
}
protected static Activity mContext;
protected static Context mContext;
}

File diff suppressed because it is too large Load diff

View file

@ -3,21 +3,30 @@ package org.libsdl.app;
import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
import java.util.ArrayList;
class SDLAudioManager {
public class SDLAudioManager {
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
static void initialize() {
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
mAudioDeviceCallback = null;
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
@ -25,85 +34,464 @@ class SDLAudioManager {
mAudioDeviceCallback = new AudioDeviceCallback() {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
for (AudioDeviceInfo deviceInfo : addedDevices) {
nativeAddAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
}
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
for (AudioDeviceInfo deviceInfo : removedDevices) {
nativeRemoveAudioDevice(deviceInfo.isSink(), deviceInfo.getId());
}
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
};
}
}
static void setContext(Context context) {
public static void setContext(Context context) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
static void release(Context context) {
// no-op atm
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
int channelConfig;
int sampleSize;
int frameSize;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
}
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
sampleRate = 48000;
}
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
}
switch (audioFormat)
{
case AudioFormat.ENCODING_PCM_8BIT:
sampleSize = 1;
break;
case AudioFormat.ENCODING_PCM_16BIT:
sampleSize = 2;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
sampleSize = 4;
break;
default:
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
sampleSize = 2;
break;
}
if (isCapture) {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_IN_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
break;
}
} else {
switch (desiredChannels) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 3:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 4:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
break;
case 5:
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 7:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
desiredChannels = 6;
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
}
break;
default:
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
desiredChannels = 2;
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
}
/*
Log.v(TAG, "Speaker configuration (and order of channels):");
if ((channelConfig & 0x00000004) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
}
if ((channelConfig & 0x00000008) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
}
if ((channelConfig & 0x00000010) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
}
if ((channelConfig & 0x00000020) != 0) {
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
}
if ((channelConfig & 0x00000040) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
}
if ((channelConfig & 0x00000080) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
}
if ((channelConfig & 0x00000100) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
}
if ((channelConfig & 0x00000200) != 0) {
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
}
if ((channelConfig & 0x00000400) != 0) {
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
}
if ((channelConfig & 0x00000800) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
}
if ((channelConfig & 0x00001000) != 0) {
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
}
*/
}
frameSize = (sampleSize * desiredChannels);
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
int minBufferSize;
if (isCapture) {
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
} else {
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
}
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
int[] results = new int[4];
if (isCapture) {
if (mAudioRecord == null) {
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Failed during initialization of AudioRecord");
mAudioRecord.release();
mAudioRecord = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
}
mAudioRecord.startRecording();
}
results[0] = mAudioRecord.getSampleRate();
results[1] = mAudioRecord.getAudioFormat();
results[2] = mAudioRecord.getChannelCount();
} else {
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
/* Try again, with safer values */
Log.e(TAG, "Failed during initialization of Audio Track");
mAudioTrack.release();
mAudioTrack = null;
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
}
mAudioTrack.play();
}
results[0] = mAudioTrack.getSampleRate();
results[1] = mAudioTrack.getAudioFormat();
results[2] = mAudioTrack.getChannelCount();
}
results[3] = desiredFrames;
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
return results;
}
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
if (deviceInfo.getId() == deviceId) {
return deviceInfo;
}
}
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
return null;
}
private static AudioDeviceInfo getPlaybackAudioDeviceInfo(int deviceId) {
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
if (deviceInfo.getId() == deviceId) {
return deviceInfo;
}
}
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
return null;
}
static void registerAudioDeviceCallback() {
private static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
// get an initial list now, before hotplug callbacks fire.
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
continue; // Device cannot be opened
}
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
}
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
}
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
}
}
static void unregisterAudioDeviceCallback() {
private static void unregisterAudioDeviceCallback(Context context) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteFloatBuffer(float[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(float)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteShortBuffer(short[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(short)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static void audioWriteByteBuffer(byte[] buffer) {
if (mAudioTrack == null) {
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch(InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "SDL audio: error return from write(byte)");
return;
}
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/** This method is called by SDL using JNI. */
static void audioSetThreadPriority(boolean recording, int device_id) {
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return 0;
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
/** This method is called by SDL using JNI. */
public static void captureClose() {
if (mAudioRecord != null) {
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
/** This method is called by SDL using JNI. */
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
try {
/* Set thread name */
if (recording) {
if (iscapture) {
Thread.currentThread().setName("SDLAudioC" + device_id);
} else {
Thread.currentThread().setName("SDLAudioP" + device_id);
@ -117,10 +505,10 @@ class SDLAudioManager {
}
}
static native void nativeSetupJNI();
public static native int nativeSetupJNI();
static native void nativeRemoveAudioDevice(boolean recording, int deviceId);
public static native void removeAudioDevice(boolean isCapture, int deviceId);
static native void nativeAddAudioDevice(boolean recording, String name, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId);
}

View file

@ -6,19 +6,9 @@ import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.hardware.lights.Light;
import android.hardware.lights.LightsRequest;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightState;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.graphics.Color;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@ -29,38 +19,38 @@ import android.view.View;
public class SDLControllerManager
{
static native void nativeSetupJNI();
public static native int nativeSetupJNI();
static native void nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
int button_mask,
int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led,
boolean has_accelerometer, boolean has_gyroscope);
static native void nativeRemoveJoystick(int device_id);
static native void nativeAddHaptic(int device_id, String name);
static native void nativeRemoveHaptic(int device_id);
static public native boolean onNativePadDown(int device_id, int keycode, int scancode);
static public native boolean onNativePadUp(int device_id, int keycode, int scancode);
static native void onNativeJoy(int device_id, int axis,
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int axis_mask, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
float value);
static native void onNativeHat(int device_id, int hat_id,
public static native void onNativeHat(int device_id, int hat_id,
int x, int y);
static native void onNativeJoySensor(int device_id, int sensor_type, long sensor_timestamp, float x, float y, float z);
protected static SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler;
private static final String TAG = "SDLControllerManager";
static void initialize() {
public static void initialize() {
if (mJoystickHandler == null) {
mJoystickHandler = new SDLJoystickHandler();
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
}
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
mHapticHandler = new SDLHapticHandler_API31();
} else if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
@ -69,62 +59,41 @@ public class SDLControllerManager
}
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
static public boolean handleJoystickMotionEvent(MotionEvent event) {
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
/**
* This method is called by SDL using JNI.
*/
static void pollInputDevices() {
public static void pollInputDevices() {
mJoystickHandler.pollInputDevices();
}
/**
* This method is called by SDL using JNI.
*/
static void joystickSetLED(int device_id, int red, int green, int blue) {
mJoystickHandler.setLED(device_id, red, green, blue);
}
/**
* This method is called by SDL using JNI.
*/
static void joystickSetSensorsEnabled(int device_id, boolean enabled) {
mJoystickHandler.setSensorsEnabled(device_id, enabled);
}
/**
* This method is called by SDL using JNI.
*/
static void pollHapticDevices() {
public static void pollHapticDevices() {
mHapticHandler.pollHapticDevices();
}
/**
* This method is called by SDL using JNI.
*/
static void hapticRun(int device_id, float intensity, int length) {
public static void hapticRun(int device_id, float intensity, int length) {
mHapticHandler.run(device_id, intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
static void hapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
mHapticHandler.rumble(device_id, low_frequency_intensity, high_frequency_intensity, length);
}
/**
* This method is called by SDL using JNI.
*/
static void hapticStop(int device_id)
public static void hapticStop(int device_id)
{
mHapticHandler.stop(device_id);
}
// Check if a given device is considered a possible SDL joystick
static public boolean isDeviceSDLJoystick(int deviceId) {
public static boolean isDeviceSDLJoystick(int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
// We cannot use InputDevice.isVirtual before API 16, so let's accept
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
@ -154,22 +123,33 @@ public class SDLControllerManager
}
/* Actual joystick functionality available for API >= 19 devices */
class SDLJoystickHandler {
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
public boolean handleMotionEvent(MotionEvent event) {
return false;
}
/**
* Handles adding and removing of input devices.
*/
public void pollInputDevices() {
}
}
/* Actual joystick functionality available for API >= 12 devices */
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
static class SDLJoystick {
int device_id;
String name;
String desc;
ArrayList<InputDevice.MotionRange> axes;
ArrayList<InputDevice.MotionRange> hats;
ArrayList<Light> lights;
LightsManager.LightsSession lightsSession;
SensorManager sensorManager;
SDLJoySensorListener sensorListener;
Sensor accelerometerSensor;
Sensor gyroscopeSensor;
public int device_id;
public String name;
public String desc;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
@ -220,15 +200,13 @@ class SDLJoystickHandler {
private final ArrayList<SDLJoystick> mJoysticks;
SDLJoystickHandler() {
public SDLJoystickHandler_API16() {
mJoysticks = new ArrayList<SDLJoystick>();
}
/**
* Handles adding and removing of input devices.
*/
synchronized void pollInputDevices() {
@Override
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
for (int device_id : deviceIds) {
@ -242,13 +220,11 @@ class SDLJoystickHandler {
joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>();
java.util.Set<Integer> axisStrsSet = new java.util.HashSet<Integer>();
joystick.lights = new ArrayList<Light>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) {
if (((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) && axisStrsSet.add(range.getAxis())) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
} else {
@ -257,47 +233,10 @@ class SDLJoystickHandler {
}
}
boolean can_rumble = false;
boolean has_rgb_led = false;
boolean has_accelerometer = false;
boolean has_gyroscope = false;
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
VibratorManager vibratorManager = joystickDevice.getVibratorManager();
int[] vibrators = vibratorManager.getVibratorIds();
if (vibrators.length > 0) {
can_rumble = true;
}
LightsManager lightsManager = joystickDevice.getLightsManager();
List<Light> lights = lightsManager.getLights();
for (Light light : lights) {
if (light.hasRgbControl()) {
joystick.lights.add(light);
}
}
if (!joystick.lights.isEmpty()) {
joystick.lightsSession = lightsManager.openSession();
has_rgb_led = true;
}
SensorManager sensorManager = joystickDevice.getSensorManager();
if (sensorManager != null) {
joystick.sensorManager = sensorManager;
joystick.sensorListener = new SDLJoySensorListener(joystick.device_id);
joystick.accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (joystick.accelerometerSensor != null) {
has_accelerometer = true;
}
joystick.gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (joystick.gyroscopeSensor != null) {
has_gyroscope = true;
}
}
}
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice),
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led,
has_accelerometer, has_gyroscope);
getVendorId(joystickDevice), getProductId(joystickDevice), false,
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
}
}
}
@ -323,16 +262,6 @@ class SDLJoystickHandler {
SDLControllerManager.nativeRemoveJoystick(device_id);
for (int i = 0; i < mJoysticks.size(); i++) {
if (mJoysticks.get(i).device_id == device_id) {
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
if (mJoysticks.get(i).lightsSession != null) {
try {
mJoysticks.get(i).lightsSession.close();
} catch (Exception e) {
// Session may already be unregistered when device disconnects
}
mJoysticks.get(i).lightsSession = null;
}
}
mJoysticks.remove(i);
break;
}
@ -341,7 +270,7 @@ class SDLJoystickHandler {
}
}
synchronized protected SDLJoystick getJoystick(int device_id) {
protected SDLJoystick getJoystick(int device_id) {
for (SDLJoystick joystick : mJoysticks) {
if (joystick.device_id == device_id) {
return joystick;
@ -350,12 +279,8 @@ class SDLJoystickHandler {
return null;
}
/**
* Handles given MotionEvent.
* @param event the event to be handled.
* @return if given event was processed.
*/
boolean handleMotionEvent(MotionEvent event) {
@Override
public boolean handleMotionEvent(MotionEvent event) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
@ -377,7 +302,7 @@ class SDLJoystickHandler {
return true;
}
String getJoystickDescriptor(InputDevice joystickDevice) {
public String getJoystickDescriptor(InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
@ -386,16 +311,34 @@ class SDLJoystickHandler {
return joystickDevice.getName();
}
public int getProductId(InputDevice joystickDevice) {
return 0;
}
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
return -1;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
}
int getProductId(InputDevice joystickDevice) {
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
@Override
public int getProductId(InputDevice joystickDevice) {
return joystickDevice.getProductId();
}
int getVendorId(InputDevice joystickDevice) {
@Override
public int getVendorId(InputDevice joystickDevice) {
return joystickDevice.getVendorId();
}
int getAxisMask(List<InputDevice.MotionRange> ranges) {
@Override
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
// For compatibility, keep computing the axis mask like before,
// only really distinguishing 2, 4 and 6 axes.
int axis_mask = 0;
@ -431,7 +374,8 @@ class SDLJoystickHandler {
return axis_mask;
}
int getButtonMask(InputDevice joystickDevice) {
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
@ -524,125 +468,14 @@ class SDLJoystickHandler {
}
return button_mask;
}
void setLED(int device_id, int red, int green, int blue) {
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
return;
}
SDLJoystick joystick = getJoystick(device_id);
if (joystick == null || joystick.lights.isEmpty()) {
return;
}
LightsRequest.Builder lightsRequest = new LightsRequest.Builder();
LightState lightState = new LightState.Builder().setColor(Color.rgb(red, green, blue)).build();
for (Light light : joystick.lights) {
if (light.hasRgbControl()) {
lightsRequest.addLight(light, lightState);
}
}
joystick.lightsSession.requestLights(lightsRequest.build());
}
void setSensorsEnabled(int device_id, boolean enabled) {
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
return;
}
SDLJoystick joystick = getJoystick(device_id);
if (joystick == null || joystick.sensorManager == null) {
return;
}
if (enabled) {
if (joystick.accelerometerSensor != null) {
SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
}
if (joystick.gyroscopeSensor != null) {
SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME);
}
} else {
if (joystick.accelerometerSensor != null) {
SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor);
}
if (joystick.gyroscopeSensor != null) {
SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor);
}
}
}
}
class SDLHapticHandler_API31 extends SDLHapticHandler {
@Override
void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
vibrate(haptic.vib, intensity, length);
}
}
@Override
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
InputDevice device = InputDevice.getDevice(device_id);
if (device == null) {
return;
}
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
/* Silence 'lint' warning */
return;
}
VibratorManager manager = device.getVibratorManager();
int[] vibrators = manager.getVibratorIds();
if (vibrators.length >= 2) {
vibrate(manager.getVibrator(vibrators[0]), low_frequency_intensity, length);
vibrate(manager.getVibrator(vibrators[1]), high_frequency_intensity, length);
} else if (vibrators.length == 1) {
float intensity = (low_frequency_intensity * 0.6f) + (high_frequency_intensity * 0.4f);
vibrate(manager.getVibrator(vibrators[0]), intensity, length);
}
}
private void vibrate(Vibrator vibrator, float intensity, int length) {
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
/* Silence 'lint' warning */
return;
}
if (intensity == 0.0f) {
vibrator.cancel();
return;
}
int value = Math.round(intensity * 255);
if (value > 255) {
value = 255;
}
if (value < 1) {
vibrator.cancel();
return;
}
try {
vibrator.vibrate(VibrationEffect.createOneShot(length, value));
}
catch (Exception e) {
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
// something went horribly wrong with the Android 8.0 APIs.
vibrator.vibrate(length);
}
}
}
class SDLHapticHandler_API26 extends SDLHapticHandler {
@Override
void run(int device_id, float intensity, int length) {
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
/* Silence 'lint' warning */
return;
}
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
if (intensity == 0.0f) {
stop(device_id);
return;
@ -672,40 +505,60 @@ class SDLHapticHandler_API26 extends SDLHapticHandler {
class SDLHapticHandler {
static class SDLHaptic {
int device_id;
String name;
Vibrator vib;
public int device_id;
public String name;
public Vibrator vib;
}
private final ArrayList<SDLHaptic> mHaptics;
SDLHapticHandler() {
public SDLHapticHandler() {
mHaptics = new ArrayList<SDLHaptic>();
}
void run(int device_id, float intensity, int length) {
public void run(int device_id, float intensity, int length) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.vibrate(length);
}
}
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
// Not supported in older APIs
}
void stop(int device_id) {
public void stop(int device_id) {
SDLHaptic haptic = getHaptic(device_id);
if (haptic != null) {
haptic.vib.cancel();
}
}
synchronized void pollHapticDevices() {
public void pollHapticDevices() {
final int deviceId_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
SDLHaptic haptic = getHaptic(deviceIds[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
Vibrator vib = device.getVibrator();
if (vib != null) {
if (vib.hasVibrator()) {
haptic = new SDLHaptic();
haptic.device_id = deviceIds[i];
haptic.name = device.getName();
haptic.vib = vib;
mHaptics.add(haptic);
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
}
}
}
}
/* Check VIBRATOR_SERVICE */
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null) {
@ -728,11 +581,18 @@ class SDLHapticHandler {
ArrayList<Integer> removedDevices = null;
for (SDLHaptic haptic : mHaptics) {
int device_id = haptic.device_id;
int i;
for (i = 0; i < deviceIds.length; i++) {
if (device_id == deviceIds[i]) break;
}
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
if (i == deviceIds.length) {
if (removedDevices == null) {
removedDevices = new ArrayList<Integer>();
}
removedDevices.add(device_id);
}
removedDevices.add(device_id);
} // else: don't remove the vibrator if it is still present
}
@ -749,7 +609,7 @@ class SDLHapticHandler {
}
}
synchronized protected SDLHaptic getHaptic(int device_id) {
protected SDLHaptic getHaptic(int device_id) {
for (SDLHaptic haptic : mHaptics) {
if (haptic.device_id == device_id) {
return haptic;
@ -759,154 +619,129 @@ class SDLHapticHandler {
}
}
class SDLGenericMotionListener_API14 implements View.OnGenericMotionListener {
protected static final int SDL_PEN_DEVICE_TYPE_UNKNOWN = 0;
protected static final int SDL_PEN_DEVICE_TYPE_DIRECT = 1;
protected static final int SDL_PEN_DEVICE_TYPE_INDIRECT = 2;
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
// Generic Motion (mouse hover, joystick...) events go here
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
if (event.getSource() == InputDevice.SOURCE_JOYSTICK)
return SDLControllerManager.handleJoystickMotionEvent(event);
float x, y;
int action = event.getActionMasked();
int pointerCount = event.getPointerCount();
boolean consumed = false;
int action;
for (int i = 0; i < pointerCount; i++) {
int toolType = event.getToolType(i);
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
consumed = true;
break;
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = getEventX(event, i);
y = getEventY(event, i);
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, checkRelativeEvent(event));
consumed = true;
break;
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_HOVER_EXIT:
x = event.getX(i);
y = event.getY(i);
float p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
break;
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
int buttons = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) {
buttons |= 0x08;
}
SDLActivity.onNativePen(event.getPointerId(i), getPenDeviceType(event.getDevice()), buttons, action, x, y, p);
consumed = true;
break;
}
}
default:
break;
}
return consumed;
}
boolean supportsRelativeMouse() {
// Event was not managed
return false;
}
boolean inRelativeMode() {
public boolean supportsRelativeMouse() {
return false;
}
boolean setRelativeMouseEnabled(boolean enabled) {
public boolean inRelativeMode() {
return false;
}
void reclaimRelativeMouseModeIfNeeded() {
public boolean setRelativeMouseEnabled(boolean enabled) {
return false;
}
public void reclaimRelativeMouseModeIfNeeded()
{
}
boolean checkRelativeEvent(MotionEvent event) {
return inRelativeMode();
public float getEventX(MotionEvent event) {
return event.getX(0);
}
float getEventX(MotionEvent event, int pointerIndex) {
return event.getX(pointerIndex);
public float getEventY(MotionEvent event) {
return event.getY(0);
}
float getEventY(MotionEvent event, int pointerIndex) {
return event.getY(pointerIndex);
}
int getPenDeviceType(InputDevice penDevice) {
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
}
}
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API14 {
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
// Generic Motion (mouse hover, joystick...) events go here
private boolean mRelativeModeEnabled;
@Override
boolean supportsRelativeMouse() {
public boolean onGenericMotion(View v, MotionEvent event) {
// Handle relative mouse mode
if (mRelativeModeEnabled) {
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
}
}
}
// Event was not managed, call SDLGenericMotionListener_API12 method
return super.onGenericMotion(v, event);
}
@Override
public boolean supportsRelativeMouse() {
return true;
}
@Override
boolean inRelativeMode() {
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
boolean setRelativeMouseEnabled(boolean enabled) {
public boolean setRelativeMouseEnabled(boolean enabled) {
mRelativeModeEnabled = enabled;
return true;
}
@Override
float getEventX(MotionEvent event, int pointerIndex) {
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
/* Silence 'lint' warning */
return 0;
}
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X, pointerIndex);
public float getEventX(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
} else {
return event.getX(pointerIndex);
return event.getX(0);
}
}
@Override
float getEventY(MotionEvent event, int pointerIndex) {
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
/* Silence 'lint' warning */
return 0;
}
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y, pointerIndex);
public float getEventY(MotionEvent event) {
if (mRelativeModeEnabled) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
} else {
return event.getY(pointerIndex);
return event.getY(0);
}
}
}
@ -916,23 +751,76 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
private boolean mRelativeModeEnabled;
@Override
boolean supportsRelativeMouse() {
public boolean onGenericMotion(View v, MotionEvent event) {
float x, y;
int action;
switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK:
return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
// DeX desktop mouse cursor is a separate non-standard input type.
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
default:
break;
}
break;
case InputDevice.SOURCE_MOUSE_RELATIVE:
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
default:
break;
}
break;
default:
break;
}
// Event was not managed
return false;
}
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
}
@Override
boolean inRelativeMode() {
public boolean inRelativeMode() {
return mRelativeModeEnabled;
}
@Override
boolean setRelativeMouseEnabled(boolean enabled) {
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
/* Silence 'lint' warning */
return false;
}
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
@ -947,64 +835,22 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
}
@Override
void reclaimRelativeMouseModeIfNeeded() {
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
/* Silence 'lint' warning */
return;
}
public void reclaimRelativeMouseModeIfNeeded()
{
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
SDLActivity.getContentView().requestPointerCapture();
}
}
@Override
boolean checkRelativeEvent(MotionEvent event) {
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
/* Silence 'lint' warning */
return false;
}
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
}
@Override
float getEventX(MotionEvent event, int pointerIndex) {
public float getEventX(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getX(pointerIndex);
return event.getX(0);
}
@Override
float getEventY(MotionEvent event, int pointerIndex) {
public float getEventY(MotionEvent event) {
// Relative mouse in capture mode will only have relative for X/Y
return event.getY(pointerIndex);
}
}
class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 {
@Override
int getPenDeviceType(InputDevice penDevice)
{
if (penDevice == null) {
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
}
return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT;
}
}
class SDLJoySensorListener implements SensorEventListener {
int device_id;
public SDLJoySensorListener(int device_id) {
this.device_id = device_id;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
@Override
public void onSensorChanged(SensorEvent event) {
SDLControllerManager.onNativeJoySensor(device_id, event.sensor.getType(), event.timestamp, event.values[0], event.values[1], event.values[2]);
return event.getY(0);
}
}

View file

@ -1,66 +0,0 @@
package org.libsdl.app;
import android.content.*;
import android.text.InputType;
import android.view.*;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
public class SDLDummyEdit extends View implements View.OnKeyListener
{
InputConnection ic;
int input_type;
SDLDummyEdit(Context context) {
super(context);
setFocusableInTouchMode(true);
setFocusable(true);
setOnKeyListener(this);
}
void setInputType(int input_type) {
this.input_type = input_type;
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
}
//
@Override
public boolean onKeyPreIme (int keyCode, KeyEvent event) {
// As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
// FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
// FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
// FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
// FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
// FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
SDLActivity.onNativeKeyboardFocusLost();
}
}
return super.onKeyPreIme(keyCode, event);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new SDLInputConnection(this, true);
outAttrs.inputType = input_type;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
return ic;
}
}

View file

@ -1,136 +0,0 @@
package org.libsdl.app;
import android.content.*;
import android.os.Build;
import android.text.Editable;
import android.view.*;
import android.view.inputmethod.BaseInputConnection;
import android.widget.EditText;
class SDLInputConnection extends BaseInputConnection
{
protected EditText mEditText;
protected String mCommittedText = "";
SDLInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
mEditText = new EditText(SDL.getContext());
}
@Override
public Editable getEditable() {
return mEditText.getEditableText();
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
/*
* This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
* However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses
* and so we need to generate them ourselves in commitText. To avoid duplicates on the handful of keys
* that still do, we empty this out.
*/
/*
* Return DOES still generate a key event, however. So rather than using it as the 'click a button' key
* as we do with physical keyboards, let's just use it to hide the keyboard.
*/
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
if (SDLActivity.onNativeSoftReturnKey()) {
return true;
}
}
return super.sendKeyEvent(event);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (!super.commitText(text, newCursorPosition)) {
return false;
}
updateText();
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (!super.setComposingText(text, newCursorPosition)) {
return false;
}
updateText();
return true;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if (beforeLength > 0 && afterLength == 0) {
// backspace(s)
while (beforeLength-- > 0) {
nativeGenerateScancodeForUnichar('\b');
}
return true;
}
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
return false;
}
updateText();
return true;
}
protected void updateText() {
final Editable content = getEditable();
if (content == null) {
return;
}
String text = content.toString();
int compareLength = Math.min(text.length(), mCommittedText.length());
int matchLength, offset;
/* Backspace over characters that are no longer in the string */
for (matchLength = 0; matchLength < compareLength; ) {
int codePoint = mCommittedText.codePointAt(matchLength);
if (codePoint != text.codePointAt(matchLength)) {
break;
}
matchLength += Character.charCount(codePoint);
}
/* FIXME: This doesn't handle graphemes, like '🌬️' */
for (offset = matchLength; offset < mCommittedText.length(); ) {
int codePoint = mCommittedText.codePointAt(offset);
nativeGenerateScancodeForUnichar('\b');
offset += Character.charCount(codePoint);
}
if (matchLength < text.length()) {
String pendingText = text.subSequence(matchLength, text.length()).toString();
if (!SDLActivity.dispatchingKeyEvent()) {
for (offset = 0; offset < pendingText.length(); ) {
int codePoint = pendingText.codePointAt(offset);
if (codePoint == '\n') {
if (SDLActivity.onNativeSoftReturnKey()) {
return;
}
}
/* Higher code points don't generate simulated scancodes */
if (codePoint > 0 && codePoint < 128) {
nativeGenerateScancodeForUnichar((char)codePoint);
}
offset += Character.charCount(codePoint);
}
}
SDLInputConnection.nativeCommitText(pendingText, 0);
}
mCommittedText = text;
}
public static native void nativeCommitText(String text, int newCursorPosition);
public static native void nativeGenerateScancodeForUnichar(char c);
}

View file

@ -1,32 +0,0 @@
package org.libsdl.app;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
// This class coordinates synchronized access to sensor manager registration
//
// This prevents a java.util.ConcurrentModificationException exception on
// Android 16, specifically on the Samsung Tab S9 Ultra.
class SDLSensorManager
{
static private SDLSensorManager mManager = new SDLSensorManager();
public static void registerListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
mManager.RegisterListener(manager, listener, sensor, samplingPeriodUs);
}
public static void unregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
mManager.UnregisterListener(manager, listener, sensor);
}
private synchronized void RegisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
manager.registerListener(listener, sensor, samplingPeriodUs, null);
}
private synchronized void UnregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
manager.unregisterListener(listener, sensor);
}
}

View file

@ -3,7 +3,6 @@ package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@ -15,15 +14,12 @@ import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.ScaleGestureDetector;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
@ -32,8 +28,7 @@ import android.view.ScaleGestureDetector;
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
@ -43,25 +38,16 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
protected boolean mIsSurfaceReady;
// Is on-screen keyboard visible
protected boolean mKeyboardVisible;
// Pinch events
private final ScaleGestureDetector scaleGestureDetector;
public boolean mIsSurfaceReady;
// Startup
protected SDLSurface(Context context) {
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
scaleGestureDetector = new ScaleGestureDetector(context, this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnApplyWindowInsetsListener(this);
setOnKeyListener(this);
setOnTouchListener(this);
@ -77,21 +63,20 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
mIsSurfaceReady = false;
}
protected void handlePause() {
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
protected void handleResume() {
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnApplyWindowInsetsListener(this);
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
protected Surface getNativeSurface() {
public Surface getNativeSurface() {
return getHolder().getSurface();
}
@ -129,15 +114,14 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
float density = 1.0f;
try
{
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
// Use densityDpi instead of density to more closely match what the UI scale is
density = (float)realMetrics.densityDpi / 160.0f;
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
@ -148,7 +132,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, density, mDisplay.getRefreshRate());
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
@ -177,10 +161,13 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
}
// Don't skip if we might be multi-window or have popup dialogs
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
skip = false;
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
@ -200,59 +187,12 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
SDLActivity.handleNativeState();
}
// Window inset
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
if (Build.VERSION.SDK_INT >= 30 /* Android 11 (R) */) {
Insets combined = insets.getInsets(WindowInsets.Type.systemBars() |
WindowInsets.Type.systemGestures() |
WindowInsets.Type.mandatorySystemGestures() |
WindowInsets.Type.tappableElement() |
WindowInsets.Type.displayCutout());
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
if (insets.isVisible(WindowInsets.Type.ime())) {
if (!mKeyboardVisible) {
mKeyboardVisible = true;
SDLActivity.onNativeScreenKeyboardShown();
}
} else {
if (mKeyboardVisible) {
mKeyboardVisible = false;
SDLActivity.onNativeScreenKeyboardHidden();
}
}
}
// Pass these to any child views in case they need them
return insets;
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
private float getNormalizedX(float x)
{
if (mWidth <= 1) {
return 0.5f;
} else {
return (x / (mWidth - 1));
}
}
private float getNormalizedY(float y)
{
if (mHeight <= 1) {
return 0.5f;
} else {
return (y / (mHeight - 1));
}
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
@ -260,79 +200,113 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerId;
int i = 0;
int pointerFingerId;
int i = -1;
float x,y,p;
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
i = event.getActionIndex();
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
do {
int toolType = event.getToolType(i);
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
int buttonState = event.getButtonState();
boolean relative = false;
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API14 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event, i);
y = motionListener.getEventY(event, i);
relative = motionListener.inRelativeMode();
SDLActivity.onNativeMouse(buttonState, action, x, y, relative);
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
pointerId = event.getPointerId(i);
x = event.getX(i);
y = event.getY(i);
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
int buttonState = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) {
buttonState |= 0x08;
}
SDLActivity.onNativePen(pointerId, SDLActivity.getMotionListener().getPenDeviceType(event.getDevice()), buttonState, action, x, y, p);
} else { // MotionEvent.TOOL_TYPE_FINGER or MotionEvent.TOOL_TYPE_UNKNOWN
pointerId = event.getPointerId(i);
x = getNormalizedX(event.getX(i));
y = getNormalizedY(event.getY(i));
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerId, action, x, y, p);
} catch(Exception ignored) {
}
// Non-primary up/down
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
break;
} while (++i < pointerCount);
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
scaleGestureDetector.onTouchEvent(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
}
// Sensor events
protected void enableSensor(int sensortype, boolean enabled) {
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
SDLSensorManager.registerListener(mSensorManager, this,
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME);
SensorManager.SENSOR_DELAY_GAME, null);
} else {
SDLSensorManager.unregisterListener(mSensorManager, this,
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@ -348,36 +322,36 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newRotation;
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newRotation = 0;
break;
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newRotation = 90;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newRotation = 180;
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newRotation = 270;
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newRotation != SDLActivity.mCurrentRotation) {
SDLActivity.mCurrentRotation = newRotation;
SDLActivity.onNativeRotationChanged(newRotation);
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
@ -388,77 +362,44 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
}
// Prevent android internal NullPointerException (https://github.com/libsdl-org/SDL/issues/13306)
@Override
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
try {
return super.onResolvePointerIcon(event, pointerIndex);
} catch (NullPointerException e) {
return null;
}
}
// Captured pointer events for API 26.
@Override
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(i);
y = event.getY(i);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(i);
y = event.getY(i);
int button = event.getButtonState();
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
SDLActivity.onNativePinchUpdate(scale);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
SDLActivity.onNativePinchStart();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
SDLActivity.onNativePinchEnd();
}
}

View file

@ -1,6 +1,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="PrincipiaTheme" parent="@style/Theme.AppCompat">
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>

View file

@ -1,51 +0,0 @@
function(download_dep_tarball depname version url sha256)
set(lib_path "${CMAKE_SOURCE_DIR}/lib")
get_filename_component(filename "${url}" NAME)
set(download_path "${lib_path}/${depname}-${filename}")
set(dep_path "${lib_path}/${depname}")
set(ver_file "${dep_path}/.download_version")
if(EXISTS "${ver_file}")
file(READ "${ver_file}" current_version)
if("${current_version}" STREQUAL "${version}")
# up to date
return()
endif()
# outdated, redownload
file(REMOVE_RECURSE "${dep_path}")
endif()
file(MAKE_DIRECTORY "${dep_path}")
message(STATUS "Downloading ${depname} ${version}")
file(DOWNLOAD "${url}" "${download_path}" STATUS status)
list(GET status 0 status_code)
if(NOT status_code EQUAL 0)
message(FATAL_ERROR "Failed to download ${depname}, status: ${status}")
endif()
# Verify SHA256 checksum if provided
if(NOT sha256 STREQUAL "SKIP")
file(SHA256 "${download_path}" downloaded_sha256)
if(NOT "${downloaded_sha256}" STREQUAL "${sha256}")
message(FATAL_ERROR "SHA256 checksum mismatch for ${depname}!\n Expected: ${sha256}\n Got: ${downloaded_sha256}")
endif()
endif()
message(STATUS "Extracting...")
execute_process(
COMMAND tar -xzf "${download_path}" --strip-components=1
WORKING_DIRECTORY "${dep_path}"
RESULT_VARIABLE extract_result)
if(NOT extract_result EQUAL 0)
message(FATAL_ERROR "Failed to extract ${depname}")
endif()
file(WRITE "${ver_file}" "${version}")
endfunction()

View file

@ -9,3 +9,9 @@ set(JPEG_INCLUDE_DIR ${DEPS}/libjpeg/include)
set(JPEG_LIBRARY ${DEPS}/libjpeg/libjpeg.a)
set(PNG_PNG_INCLUDE_DIR ${DEPS}/libpng/include) #what
set(PNG_LIBRARY ${DEPS}/libpng/libpng.a)
add_library(SDL2::SDL2 STATIC IMPORTED)
set_target_properties(SDL2::SDL2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${DEPS}/SDL2/include/SDL2
IMPORTED_LOCATION ${DEPS}/SDL2/libSDL2.a)

View file

@ -1,33 +0,0 @@
set(DEP_SDL_VER "3.4.10")
download_dep_tarball(
"SDL"
"${DEP_SDL_VER}"
"https://github.com/libsdl-org/SDL/releases/download/release-${DEP_SDL_VER}/SDL3-${DEP_SDL_VER}.tar.gz"
"12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785"
)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_STATIC ON CACHE BOOL "" FORCE)
set(DISABLED_FEATURES CAMERA GPU HAPTIC POWER RENDER SENSOR TESTS VULKAN)
foreach(feature ${DISABLED_FEATURES})
set(SDL_${feature} OFF CACHE BOOL "" FORCE)
endforeach()
if(ANDROID OR HAIKU)
enable_language(CXX)
endif()
if(HAIKU)
add_definitions(-fPIC)
endif()
if(EMSCRIPTEN)
set(SDL_PTHREADS ON CACHE BOOL "" FORCE)
set(SDL_EMSCRIPTEN_PERSISTENT_PATH "/storage" CACHE STRING "" FORCE)
endif()
add_definitions(-DSDL_LEAN_AND_MEAN=1)
add_subdirectory(lib/SDL EXCLUDE_FROM_ALL)

Some files were not shown because too many files have changed in this diff Show more