kivyforge 3.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. kivyforge/__init__.py +9 -0
  2. kivyforge/artifacts/__init__.py +41 -0
  3. kivyforge/artifacts/cache.py +81 -0
  4. kivyforge/artifacts/collect.py +354 -0
  5. kivyforge/artifacts/download.py +125 -0
  6. kivyforge/artifacts/frameworks.py +240 -0
  7. kivyforge/artifacts/native_stage_util.py +346 -0
  8. kivyforge/artifacts/runtime.py +65 -0
  9. kivyforge/artifacts/verify.py +45 -0
  10. kivyforge/artifacts/wheels.py +89 -0
  11. kivyforge/bundle/__init__.py +1 -0
  12. kivyforge/bundle/pycompile.py +161 -0
  13. kivyforge/cli/__init__.py +52 -0
  14. kivyforge/cli/_common.py +62 -0
  15. kivyforge/cli/_platform.py +84 -0
  16. kivyforge/cli/build.py +111 -0
  17. kivyforge/cli/clean.py +135 -0
  18. kivyforge/cli/doctor.py +52 -0
  19. kivyforge/cli/init.py +612 -0
  20. kivyforge/cli/init_writer.py +716 -0
  21. kivyforge/cli/lock.py +261 -0
  22. kivyforge/cli/open_cmd.py +15 -0
  23. kivyforge/cli/package.py +134 -0
  24. kivyforge/cli/run.py +162 -0
  25. kivyforge/cli/status.py +20 -0
  26. kivyforge/cli/upgrade.py +314 -0
  27. kivyforge/config/__init__.py +46 -0
  28. kivyforge/config/errors.py +43 -0
  29. kivyforge/config/icons.py +60 -0
  30. kivyforge/config/loader.py +3079 -0
  31. kivyforge/config/model.py +817 -0
  32. kivyforge/doctor/__init__.py +19 -0
  33. kivyforge/doctor/checks_common.py +95 -0
  34. kivyforge/doctor/probe.py +357 -0
  35. kivyforge/doctor/result.py +41 -0
  36. kivyforge/lock/__init__.py +42 -0
  37. kivyforge/lock/_pip_shim.py +63 -0
  38. kivyforge/lock/find_links.py +182 -0
  39. kivyforge/lock/model.py +69 -0
  40. kivyforge/lock/pep751.py +111 -0
  41. kivyforge/lock/reader.py +30 -0
  42. kivyforge/lock/resolver.py +124 -0
  43. kivyforge/lock/wheelruntime/__init__.py +70 -0
  44. kivyforge/lock/wheelruntime/builder.py +280 -0
  45. kivyforge/lock/wheelruntime/model.py +94 -0
  46. kivyforge/lock/wheelruntime/native_binaries.py +127 -0
  47. kivyforge/lock/wheelruntime/pbs_github.py +133 -0
  48. kivyforge/lock/wheelruntime/profile.py +104 -0
  49. kivyforge/lock/wheelruntime/resolver.py +248 -0
  50. kivyforge/lock/wheelruntime/runtime.py +161 -0
  51. kivyforge/lock/wheelruntime/serialize.py +245 -0
  52. kivyforge/platforms/__init__.py +114 -0
  53. kivyforge/platforms/android/__init__.py +140 -0
  54. kivyforge/platforms/android/adb.py +215 -0
  55. kivyforge/platforms/android/bootstrap/__init__.py +7 -0
  56. kivyforge/platforms/android/bootstrap/contract.py +166 -0
  57. kivyforge/platforms/android/bootstrap/render.py +244 -0
  58. kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
  59. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
  60. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
  61. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
  62. kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
  63. kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
  64. kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
  65. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
  66. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
  67. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
  68. kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
  69. kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
  70. kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
  71. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
  72. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
  73. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
  74. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
  75. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
  76. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
  77. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
  78. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
  79. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
  80. kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
  81. kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
  82. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
  83. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
  84. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
  85. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
  86. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
  87. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
  88. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
  89. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
  90. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
  91. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
  92. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
  93. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
  94. kivyforge/platforms/android/cli.py +1023 -0
  95. kivyforge/platforms/android/doctor.py +899 -0
  96. kivyforge/platforms/android/elf.py +148 -0
  97. kivyforge/platforms/android/generate/__init__.py +1 -0
  98. kivyforge/platforms/android/generate/manifest.py +261 -0
  99. kivyforge/platforms/android/generate/project.py +462 -0
  100. kivyforge/platforms/android/generate/services.py +187 -0
  101. kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
  102. kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
  103. kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
  104. kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
  105. kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
  106. kivyforge/platforms/android/gradlew.py +60 -0
  107. kivyforge/platforms/android/icons.py +237 -0
  108. kivyforge/platforms/android/lock/__init__.py +1 -0
  109. kivyforge/platforms/android/lock/archives.py +109 -0
  110. kivyforge/platforms/android/lock/builder.py +387 -0
  111. kivyforge/platforms/android/lock/markers.py +71 -0
  112. kivyforge/platforms/android/lock/maven.py +252 -0
  113. kivyforge/platforms/android/lock/model.py +113 -0
  114. kivyforge/platforms/android/lock/python_meta.py +199 -0
  115. kivyforge/platforms/android/lock/reader.py +206 -0
  116. kivyforge/platforms/android/lock/resolver.py +276 -0
  117. kivyforge/platforms/android/lock/writer.py +121 -0
  118. kivyforge/platforms/android/policy.py +214 -0
  119. kivyforge/platforms/android/signing.py +145 -0
  120. kivyforge/platforms/android/smoke.py +43 -0
  121. kivyforge/platforms/android/splash.py +293 -0
  122. kivyforge/platforms/android/stage/__init__.py +1 -0
  123. kivyforge/platforms/android/stage/bundle.py +295 -0
  124. kivyforge/platforms/android/stage/jnilibs.py +186 -0
  125. kivyforge/platforms/android/stage/runtime.py +61 -0
  126. kivyforge/platforms/android/stage/wheels.py +106 -0
  127. kivyforge/platforms/android/toolchain.py +29 -0
  128. kivyforge/platforms/base.py +157 -0
  129. kivyforge/platforms/ios/__init__.py +129 -0
  130. kivyforge/platforms/ios/assets.py +193 -0
  131. kivyforge/platforms/ios/buildsettings.py +128 -0
  132. kivyforge/platforms/ios/cli.py +543 -0
  133. kivyforge/platforms/ios/doctor.py +410 -0
  134. kivyforge/platforms/ios/entitlements.py +34 -0
  135. kivyforge/platforms/ios/generator.py +318 -0
  136. kivyforge/platforms/ios/lock/__init__.py +71 -0
  137. kivyforge/platforms/ios/lock/builder.py +393 -0
  138. kivyforge/platforms/ios/lock/markers.py +69 -0
  139. kivyforge/platforms/ios/lock/model.py +95 -0
  140. kivyforge/platforms/ios/lock/python_meta.py +187 -0
  141. kivyforge/platforms/ios/lock/reader.py +191 -0
  142. kivyforge/platforms/ios/lock/resolver.py +304 -0
  143. kivyforge/platforms/ios/lock/spm.py +217 -0
  144. kivyforge/platforms/ios/lock/writer.py +114 -0
  145. kivyforge/platforms/ios/lock/xcframework.py +188 -0
  146. kivyforge/platforms/ios/materialize.py +59 -0
  147. kivyforge/platforms/ios/plist.py +91 -0
  148. kivyforge/platforms/ios/privacy.py +39 -0
  149. kivyforge/platforms/ios/skeleton.py +165 -0
  150. kivyforge/platforms/ios/sources.py +63 -0
  151. kivyforge/platforms/ios/staging.py +225 -0
  152. kivyforge/platforms/ios/swift_packages.py +324 -0
  153. kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
  154. kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
  155. kivyforge/platforms/ios/templates/main.m +15 -0
  156. kivyforge/platforms/ios/templates/main_config.h.in +12 -0
  157. kivyforge/platforms/ios/xcframeworks.py +194 -0
  158. kivyforge/platforms/ios/xcode/__init__.py +76 -0
  159. kivyforge/platforms/ios/xcode/commands.py +296 -0
  160. kivyforge/platforms/ios/xcode/runner.py +316 -0
  161. kivyforge/platforms/linux/__init__.py +120 -0
  162. kivyforge/platforms/linux/appimage.py +169 -0
  163. kivyforge/platforms/linux/bundle.py +235 -0
  164. kivyforge/platforms/linux/cli.py +232 -0
  165. kivyforge/platforms/linux/desktop.py +71 -0
  166. kivyforge/platforms/linux/doctor.py +438 -0
  167. kivyforge/platforms/linux/elftools.py +95 -0
  168. kivyforge/platforms/linux/icons.py +129 -0
  169. kivyforge/platforms/linux/launcher.py +107 -0
  170. kivyforge/platforms/linux/lock/__init__.py +126 -0
  171. kivyforge/platforms/linux/lock/profile.py +254 -0
  172. kivyforge/platforms/linux/lock/runtime.py +75 -0
  173. kivyforge/platforms/linux/native_stage.py +47 -0
  174. kivyforge/platforms/linux/runtime_stage.py +107 -0
  175. kivyforge/platforms/linux/wheels_stage.py +103 -0
  176. kivyforge/platforms/macos/__init__.py +121 -0
  177. kivyforge/platforms/macos/bundle.py +255 -0
  178. kivyforge/platforms/macos/cli.py +269 -0
  179. kivyforge/platforms/macos/doctor.py +480 -0
  180. kivyforge/platforms/macos/icns.py +75 -0
  181. kivyforge/platforms/macos/launcher.py +118 -0
  182. kivyforge/platforms/macos/lock/__init__.py +112 -0
  183. kivyforge/platforms/macos/lock/profile.py +119 -0
  184. kivyforge/platforms/macos/lock/runtime.py +58 -0
  185. kivyforge/platforms/macos/machotools.py +138 -0
  186. kivyforge/platforms/macos/native_stage.py +47 -0
  187. kivyforge/platforms/macos/notarize.py +143 -0
  188. kivyforge/platforms/macos/plist.py +37 -0
  189. kivyforge/platforms/macos/runtime_stage.py +110 -0
  190. kivyforge/platforms/macos/signing.py +78 -0
  191. kivyforge/platforms/macos/wheels_stage.py +108 -0
  192. kivyforge/platforms/windows/__init__.py +133 -0
  193. kivyforge/platforms/windows/assets.py +76 -0
  194. kivyforge/platforms/windows/bundle.py +298 -0
  195. kivyforge/platforms/windows/cli.py +314 -0
  196. kivyforge/platforms/windows/doctor.py +563 -0
  197. kivyforge/platforms/windows/fsswap.py +87 -0
  198. kivyforge/platforms/windows/icons.py +64 -0
  199. kivyforge/platforms/windows/launcher/__init__.py +193 -0
  200. kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
  201. kivyforge/platforms/windows/launcher/launcher.c +379 -0
  202. kivyforge/platforms/windows/lock/__init__.py +112 -0
  203. kivyforge/platforms/windows/lock/profile.py +101 -0
  204. kivyforge/platforms/windows/lock/runtime.py +64 -0
  205. kivyforge/platforms/windows/naming.py +52 -0
  206. kivyforge/platforms/windows/native_stage.py +66 -0
  207. kivyforge/platforms/windows/petools.py +98 -0
  208. kivyforge/platforms/windows/rcedit.py +116 -0
  209. kivyforge/platforms/windows/runtime_stage.py +221 -0
  210. kivyforge/platforms/windows/signing.py +168 -0
  211. kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
  212. kivyforge/platforms/windows/vendor/NOTICE +17 -0
  213. kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
  214. kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
  215. kivyforge/platforms/windows/vendor/__init__.py +12 -0
  216. kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
  217. kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
  218. kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
  219. kivyforge/platforms/windows/wheels_stage.py +139 -0
  220. kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
  221. kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
  222. kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
  223. kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
  224. kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  225. kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2120 @@
1
+ package org.libsdl.app;
2
+
3
+ import android.app.Activity;
4
+ import android.app.AlertDialog;
5
+ import android.app.Dialog;
6
+ import android.app.UiModeManager;
7
+ import android.content.ClipboardManager;
8
+ import android.content.ClipData;
9
+ import android.content.Context;
10
+ import android.content.DialogInterface;
11
+ import android.content.Intent;
12
+ import android.content.pm.ActivityInfo;
13
+ import android.content.pm.ApplicationInfo;
14
+ import android.content.pm.PackageManager;
15
+ import android.content.res.Configuration;
16
+ import android.graphics.Bitmap;
17
+ import android.graphics.Color;
18
+ import android.graphics.PorterDuff;
19
+ import android.graphics.drawable.Drawable;
20
+ import android.hardware.Sensor;
21
+ import android.net.Uri;
22
+ import android.os.Build;
23
+ import android.os.Bundle;
24
+ import android.os.Handler;
25
+ import android.os.Message;
26
+ import android.text.Editable;
27
+ import android.text.InputType;
28
+ import android.text.Selection;
29
+ import android.util.DisplayMetrics;
30
+ import android.util.Log;
31
+ import android.util.SparseArray;
32
+ import android.view.Display;
33
+ import android.view.Gravity;
34
+ import android.view.InputDevice;
35
+ import android.view.KeyEvent;
36
+ import android.view.PointerIcon;
37
+ import android.view.Surface;
38
+ import android.view.View;
39
+ import android.view.ViewGroup;
40
+ import android.view.Window;
41
+ import android.view.WindowManager;
42
+ import android.view.inputmethod.BaseInputConnection;
43
+ import android.view.inputmethod.EditorInfo;
44
+ import android.view.inputmethod.InputConnection;
45
+ import android.view.inputmethod.InputMethodManager;
46
+ import android.widget.Button;
47
+ import android.widget.EditText;
48
+ import android.widget.LinearLayout;
49
+ import android.widget.RelativeLayout;
50
+ import android.widget.TextView;
51
+ import android.widget.Toast;
52
+
53
+ import java.util.Hashtable;
54
+ import java.util.Locale;
55
+
56
+
57
+ /**
58
+ SDL Activity
59
+ */
60
+ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
61
+ private static final String TAG = "SDL";
62
+ private static final int SDL_MAJOR_VERSION = 2;
63
+ private static final int SDL_MINOR_VERSION = 32;
64
+ private static final int SDL_MICRO_VERSION = 10;
65
+ /*
66
+ // Display InputType.SOURCE/CLASS of events and devices
67
+ //
68
+ // SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]");
69
+ // SDLActivity.debugSource(event.getSource(), "event");
70
+ public static void debugSource(int sources, String prefix) {
71
+ int s = sources;
72
+ int s_copy = sources;
73
+ String cls = "";
74
+ String src = "";
75
+ int tst = 0;
76
+ int FLAG_TAINTED = 0x80000000;
77
+
78
+ if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON";
79
+ if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK";
80
+ if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER";
81
+ if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION";
82
+ if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL";
83
+
84
+
85
+ int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits
86
+ s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON
87
+ | InputDevice.SOURCE_CLASS_JOYSTICK
88
+ | InputDevice.SOURCE_CLASS_POINTER
89
+ | InputDevice.SOURCE_CLASS_POSITION
90
+ | InputDevice.SOURCE_CLASS_TRACKBALL);
91
+
92
+ if (s2 != 0) cls += "Some_Unknown";
93
+
94
+ s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class;
95
+
96
+ if (Build.VERSION.SDK_INT >= 23) {
97
+ tst = InputDevice.SOURCE_BLUETOOTH_STYLUS;
98
+ if ((s & tst) == tst) src += " BLUETOOTH_STYLUS";
99
+ s2 &= ~tst;
100
+ }
101
+
102
+ tst = InputDevice.SOURCE_DPAD;
103
+ if ((s & tst) == tst) src += " DPAD";
104
+ s2 &= ~tst;
105
+
106
+ tst = InputDevice.SOURCE_GAMEPAD;
107
+ if ((s & tst) == tst) src += " GAMEPAD";
108
+ s2 &= ~tst;
109
+
110
+ if (Build.VERSION.SDK_INT >= 21) {
111
+ tst = InputDevice.SOURCE_HDMI;
112
+ if ((s & tst) == tst) src += " HDMI";
113
+ s2 &= ~tst;
114
+ }
115
+
116
+ tst = InputDevice.SOURCE_JOYSTICK;
117
+ if ((s & tst) == tst) src += " JOYSTICK";
118
+ s2 &= ~tst;
119
+
120
+ tst = InputDevice.SOURCE_KEYBOARD;
121
+ if ((s & tst) == tst) src += " KEYBOARD";
122
+ s2 &= ~tst;
123
+
124
+ tst = InputDevice.SOURCE_MOUSE;
125
+ if ((s & tst) == tst) src += " MOUSE";
126
+ s2 &= ~tst;
127
+
128
+ if (Build.VERSION.SDK_INT >= 26) {
129
+ tst = InputDevice.SOURCE_MOUSE_RELATIVE;
130
+ if ((s & tst) == tst) src += " MOUSE_RELATIVE";
131
+ s2 &= ~tst;
132
+
133
+ tst = InputDevice.SOURCE_ROTARY_ENCODER;
134
+ if ((s & tst) == tst) src += " ROTARY_ENCODER";
135
+ s2 &= ~tst;
136
+ }
137
+ tst = InputDevice.SOURCE_STYLUS;
138
+ if ((s & tst) == tst) src += " STYLUS";
139
+ s2 &= ~tst;
140
+
141
+ tst = InputDevice.SOURCE_TOUCHPAD;
142
+ if ((s & tst) == tst) src += " TOUCHPAD";
143
+ s2 &= ~tst;
144
+
145
+ tst = InputDevice.SOURCE_TOUCHSCREEN;
146
+ if ((s & tst) == tst) src += " TOUCHSCREEN";
147
+ s2 &= ~tst;
148
+
149
+ if (Build.VERSION.SDK_INT >= 18) {
150
+ tst = InputDevice.SOURCE_TOUCH_NAVIGATION;
151
+ if ((s & tst) == tst) src += " TOUCH_NAVIGATION";
152
+ s2 &= ~tst;
153
+ }
154
+
155
+ tst = InputDevice.SOURCE_TRACKBALL;
156
+ if ((s & tst) == tst) src += " TRACKBALL";
157
+ s2 &= ~tst;
158
+
159
+ tst = InputDevice.SOURCE_ANY;
160
+ if ((s & tst) == tst) src += " ANY";
161
+ s2 &= ~tst;
162
+
163
+ if (s == FLAG_TAINTED) src += " FLAG_TAINTED";
164
+ s2 &= ~FLAG_TAINTED;
165
+
166
+ if (s2 != 0) src += " Some_Unknown";
167
+
168
+ Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src);
169
+ }
170
+ */
171
+
172
+ public static boolean mIsResumedCalled, mHasFocus;
173
+ public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */);
174
+
175
+ // Cursor types
176
+ // private static final int SDL_SYSTEM_CURSOR_NONE = -1;
177
+ private static final int SDL_SYSTEM_CURSOR_ARROW = 0;
178
+ private static final int SDL_SYSTEM_CURSOR_IBEAM = 1;
179
+ private static final int SDL_SYSTEM_CURSOR_WAIT = 2;
180
+ private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3;
181
+ private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4;
182
+ private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5;
183
+ private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6;
184
+ private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7;
185
+ private static final int SDL_SYSTEM_CURSOR_SIZENS = 8;
186
+ private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9;
187
+ private static final int SDL_SYSTEM_CURSOR_NO = 10;
188
+ private static final int SDL_SYSTEM_CURSOR_HAND = 11;
189
+
190
+ protected static final int SDL_ORIENTATION_UNKNOWN = 0;
191
+ protected static final int SDL_ORIENTATION_LANDSCAPE = 1;
192
+ protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2;
193
+ protected static final int SDL_ORIENTATION_PORTRAIT = 3;
194
+ protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4;
195
+
196
+ protected static int mCurrentOrientation;
197
+ protected static Locale mCurrentLocale;
198
+
199
+ // Handle the state of the native layer
200
+ public enum NativeState {
201
+ INIT, RESUMED, PAUSED
202
+ }
203
+
204
+ public static NativeState mNextNativeState;
205
+ public static NativeState mCurrentNativeState;
206
+
207
+ /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
208
+ public static boolean mBrokenLibraries = true;
209
+
210
+ // Main components
211
+ protected static SDLActivity mSingleton;
212
+ protected static SDLSurface mSurface;
213
+ protected static DummyEdit mTextEdit;
214
+ protected static boolean mScreenKeyboardShown;
215
+ protected static ViewGroup mLayout;
216
+ protected static SDLClipboardHandler mClipboardHandler;
217
+ protected static Hashtable<Integer, PointerIcon> mCursors;
218
+ protected static int mLastCursorID;
219
+ protected static SDLGenericMotionListener_API12 mMotionListener;
220
+ protected static HIDDeviceManager mHIDDeviceManager;
221
+
222
+ // This is what SDL runs in. It invokes SDL_main(), eventually
223
+ protected static Thread mSDLThread;
224
+
225
+ protected static SDLGenericMotionListener_API12 getMotionListener() {
226
+ if (mMotionListener == null) {
227
+ if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
228
+ mMotionListener = new SDLGenericMotionListener_API26();
229
+ } else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
230
+ mMotionListener = new SDLGenericMotionListener_API24();
231
+ } else {
232
+ mMotionListener = new SDLGenericMotionListener_API12();
233
+ }
234
+ }
235
+
236
+ return mMotionListener;
237
+ }
238
+
239
+ /**
240
+ * This method returns the name of the shared object with the application entry point
241
+ * It can be overridden by derived classes.
242
+ */
243
+ protected String getMainSharedObject() {
244
+ String library;
245
+ String[] libraries = SDLActivity.mSingleton.getLibraries();
246
+ if (libraries.length > 0) {
247
+ library = "lib" + libraries[libraries.length - 1] + ".so";
248
+ } else {
249
+ library = "libmain.so";
250
+ }
251
+ return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;
252
+ }
253
+
254
+ /**
255
+ * This method returns the name of the application entry point
256
+ * It can be overridden by derived classes.
257
+ */
258
+ protected String getMainFunction() {
259
+ return "SDL_main";
260
+ }
261
+
262
+ /**
263
+ * This method is called by SDL before loading the native shared libraries.
264
+ * It can be overridden to provide names of shared libraries to be loaded.
265
+ * The default implementation returns the defaults. It never returns null.
266
+ * An array returned by a new implementation must at least contain "SDL2".
267
+ * Also keep in mind that the order the libraries are loaded may matter.
268
+ * @return names of shared libraries to be loaded (e.g. "SDL2", "main").
269
+ */
270
+ protected String[] getLibraries() {
271
+ return new String[] {
272
+ "SDL2",
273
+ // "SDL2_image",
274
+ // "SDL2_mixer",
275
+ // "SDL2_net",
276
+ // "SDL2_ttf",
277
+ "main"
278
+ };
279
+ }
280
+
281
+ // Load the .so
282
+ public void loadLibraries() {
283
+ for (String lib : getLibraries()) {
284
+ SDL.loadLibrary(lib, this);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * This method is called by SDL before starting the native application thread.
290
+ * It can be overridden to provide the arguments after the application name.
291
+ * The default implementation returns an empty array. It never returns null.
292
+ * @return arguments for the native application.
293
+ */
294
+ protected String[] getArguments() {
295
+ return new String[0];
296
+ }
297
+
298
+ public static void initialize() {
299
+ // The static nature of the singleton and Android quirkyness force us to initialize everything here
300
+ // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
301
+ mSingleton = null;
302
+ mSurface = null;
303
+ mTextEdit = null;
304
+ mLayout = null;
305
+ mClipboardHandler = null;
306
+ mCursors = new Hashtable<Integer, PointerIcon>();
307
+ mLastCursorID = 0;
308
+ mSDLThread = null;
309
+ mIsResumedCalled = false;
310
+ mHasFocus = true;
311
+ mNextNativeState = NativeState.INIT;
312
+ mCurrentNativeState = NativeState.INIT;
313
+ }
314
+
315
+ protected SDLSurface createSDLSurface(Context context) {
316
+ return new SDLSurface(context);
317
+ }
318
+
319
+ // Setup
320
+ @Override
321
+ protected void onCreate(Bundle savedInstanceState) {
322
+ Log.v(TAG, "Device: " + Build.DEVICE);
323
+ Log.v(TAG, "Model: " + Build.MODEL);
324
+ Log.v(TAG, "onCreate()");
325
+ super.onCreate(savedInstanceState);
326
+
327
+ try {
328
+ Thread.currentThread().setName("SDLActivity");
329
+ } catch (Exception e) {
330
+ Log.v(TAG, "modify thread properties failed " + e.toString());
331
+ }
332
+
333
+ // Load shared libraries
334
+ String errorMsgBrokenLib = "";
335
+ try {
336
+ loadLibraries();
337
+ mBrokenLibraries = false; /* success */
338
+ } catch(UnsatisfiedLinkError e) {
339
+ System.err.println(e.getMessage());
340
+ mBrokenLibraries = true;
341
+ errorMsgBrokenLib = e.getMessage();
342
+ } catch(Exception e) {
343
+ System.err.println(e.getMessage());
344
+ mBrokenLibraries = true;
345
+ errorMsgBrokenLib = e.getMessage();
346
+ }
347
+
348
+ if (!mBrokenLibraries) {
349
+ String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." +
350
+ String.valueOf(SDL_MINOR_VERSION) + "." +
351
+ String.valueOf(SDL_MICRO_VERSION);
352
+ String version = nativeGetVersion();
353
+ if (!version.equals(expected_version)) {
354
+ mBrokenLibraries = true;
355
+ errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")";
356
+ }
357
+ }
358
+
359
+ if (mBrokenLibraries) {
360
+ mSingleton = this;
361
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
362
+ dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
363
+ + System.getProperty("line.separator")
364
+ + System.getProperty("line.separator")
365
+ + "Error: " + errorMsgBrokenLib);
366
+ dlgAlert.setTitle("SDL Error");
367
+ dlgAlert.setPositiveButton("Exit",
368
+ new DialogInterface.OnClickListener() {
369
+ @Override
370
+ public void onClick(DialogInterface dialog,int id) {
371
+ // if this button is clicked, close current activity
372
+ SDLActivity.mSingleton.finish();
373
+ }
374
+ });
375
+ dlgAlert.setCancelable(false);
376
+ dlgAlert.create().show();
377
+
378
+ return;
379
+ }
380
+
381
+ // Set up JNI
382
+ SDL.setupJNI();
383
+
384
+ // Initialize state
385
+ SDL.initialize();
386
+
387
+ // So we can call stuff from static callbacks
388
+ mSingleton = this;
389
+ SDL.setContext(this);
390
+
391
+ mClipboardHandler = new SDLClipboardHandler();
392
+
393
+ mHIDDeviceManager = HIDDeviceManager.acquire(this);
394
+
395
+ // Set up the surface
396
+ mSurface = createSDLSurface(this);
397
+
398
+ mLayout = new RelativeLayout(this);
399
+ mLayout.addView(mSurface);
400
+
401
+ // Get our current screen orientation and pass it down.
402
+ mCurrentOrientation = SDLActivity.getCurrentOrientation();
403
+ // Only record current orientation
404
+ SDLActivity.onNativeOrientationChanged(mCurrentOrientation);
405
+
406
+ try {
407
+ if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
408
+ mCurrentLocale = getContext().getResources().getConfiguration().locale;
409
+ } else {
410
+ mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0);
411
+ }
412
+ } catch(Exception ignored) {
413
+ }
414
+
415
+ setContentView(mLayout);
416
+
417
+ setWindowStyle(false);
418
+
419
+ getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
420
+
421
+ // Get filename from "Open with" of another application
422
+ Intent intent = getIntent();
423
+ if (intent != null && intent.getData() != null) {
424
+ String filename = intent.getData().getPath();
425
+ if (filename != null) {
426
+ Log.v(TAG, "Got filename: " + filename);
427
+ SDLActivity.onNativeDropFile(filename);
428
+ }
429
+ }
430
+ }
431
+
432
+ protected void pauseNativeThread() {
433
+ mNextNativeState = NativeState.PAUSED;
434
+ mIsResumedCalled = false;
435
+
436
+ if (SDLActivity.mBrokenLibraries) {
437
+ return;
438
+ }
439
+
440
+ SDLActivity.handleNativeState();
441
+ }
442
+
443
+ protected void resumeNativeThread() {
444
+ mNextNativeState = NativeState.RESUMED;
445
+ mIsResumedCalled = true;
446
+
447
+ if (SDLActivity.mBrokenLibraries) {
448
+ return;
449
+ }
450
+
451
+ SDLActivity.handleNativeState();
452
+ }
453
+
454
+ // Events
455
+ @Override
456
+ protected void onPause() {
457
+ Log.v(TAG, "onPause()");
458
+ super.onPause();
459
+
460
+ if (mHIDDeviceManager != null) {
461
+ mHIDDeviceManager.setFrozen(true);
462
+ }
463
+ if (!mHasMultiWindow) {
464
+ pauseNativeThread();
465
+ }
466
+ }
467
+
468
+ @Override
469
+ protected void onResume() {
470
+ Log.v(TAG, "onResume()");
471
+ super.onResume();
472
+
473
+ if (mHIDDeviceManager != null) {
474
+ mHIDDeviceManager.setFrozen(false);
475
+ }
476
+ if (!mHasMultiWindow) {
477
+ resumeNativeThread();
478
+ }
479
+ }
480
+
481
+ @Override
482
+ protected void onStop() {
483
+ Log.v(TAG, "onStop()");
484
+ super.onStop();
485
+ if (mHasMultiWindow) {
486
+ pauseNativeThread();
487
+ }
488
+ }
489
+
490
+ @Override
491
+ protected void onStart() {
492
+ Log.v(TAG, "onStart()");
493
+ super.onStart();
494
+ if (mHasMultiWindow) {
495
+ resumeNativeThread();
496
+ }
497
+ }
498
+
499
+ public static int getCurrentOrientation() {
500
+ int result = SDL_ORIENTATION_UNKNOWN;
501
+
502
+ Activity activity = (Activity)getContext();
503
+ if (activity == null) {
504
+ return result;
505
+ }
506
+ Display display = activity.getWindowManager().getDefaultDisplay();
507
+
508
+ switch (display.getRotation()) {
509
+ case Surface.ROTATION_0:
510
+ result = SDL_ORIENTATION_PORTRAIT;
511
+ break;
512
+
513
+ case Surface.ROTATION_90:
514
+ result = SDL_ORIENTATION_LANDSCAPE;
515
+ break;
516
+
517
+ case Surface.ROTATION_180:
518
+ result = SDL_ORIENTATION_PORTRAIT_FLIPPED;
519
+ break;
520
+
521
+ case Surface.ROTATION_270:
522
+ result = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
523
+ break;
524
+ }
525
+
526
+ return result;
527
+ }
528
+
529
+ @Override
530
+ public void onWindowFocusChanged(boolean hasFocus) {
531
+ super.onWindowFocusChanged(hasFocus);
532
+ Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
533
+
534
+ if (SDLActivity.mBrokenLibraries) {
535
+ return;
536
+ }
537
+
538
+ mHasFocus = hasFocus;
539
+ if (hasFocus) {
540
+ mNextNativeState = NativeState.RESUMED;
541
+ SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded();
542
+
543
+ SDLActivity.handleNativeState();
544
+ nativeFocusChanged(true);
545
+
546
+ } else {
547
+ nativeFocusChanged(false);
548
+ if (!mHasMultiWindow) {
549
+ mNextNativeState = NativeState.PAUSED;
550
+ SDLActivity.handleNativeState();
551
+ }
552
+ }
553
+ }
554
+
555
+ @Override
556
+ public void onLowMemory() {
557
+ Log.v(TAG, "onLowMemory()");
558
+ super.onLowMemory();
559
+
560
+ if (SDLActivity.mBrokenLibraries) {
561
+ return;
562
+ }
563
+
564
+ SDLActivity.nativeLowMemory();
565
+ }
566
+
567
+ @Override
568
+ public void onConfigurationChanged(Configuration newConfig) {
569
+ Log.v(TAG, "onConfigurationChanged()");
570
+ super.onConfigurationChanged(newConfig);
571
+
572
+ if (SDLActivity.mBrokenLibraries) {
573
+ return;
574
+ }
575
+
576
+ if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) {
577
+ mCurrentLocale = newConfig.locale;
578
+ SDLActivity.onNativeLocaleChanged();
579
+ }
580
+ }
581
+
582
+ @Override
583
+ protected void onDestroy() {
584
+ Log.v(TAG, "onDestroy()");
585
+
586
+ if (mHIDDeviceManager != null) {
587
+ HIDDeviceManager.release(mHIDDeviceManager);
588
+ mHIDDeviceManager = null;
589
+ }
590
+
591
+ SDLAudioManager.release(this);
592
+
593
+ if (SDLActivity.mBrokenLibraries) {
594
+ super.onDestroy();
595
+ return;
596
+ }
597
+
598
+ if (SDLActivity.mSDLThread != null) {
599
+
600
+ // Send Quit event to "SDLThread" thread
601
+ SDLActivity.nativeSendQuit();
602
+
603
+ // Wait for "SDLThread" thread to end
604
+ try {
605
+ SDLActivity.mSDLThread.join();
606
+ } catch(Exception e) {
607
+ Log.v(TAG, "Problem stopping SDLThread: " + e);
608
+ }
609
+ }
610
+
611
+ SDLActivity.nativeQuit();
612
+
613
+ super.onDestroy();
614
+ }
615
+
616
+ @Override
617
+ public void onBackPressed() {
618
+ // Check if we want to block the back button in case of mouse right click.
619
+ //
620
+ // If we do, the normal hardware back button will no longer work and people have to use home,
621
+ // but the mouse right click will work.
622
+ //
623
+ boolean trapBack = SDLActivity.nativeGetHintBoolean("SDL_ANDROID_TRAP_BACK_BUTTON", false);
624
+ if (trapBack) {
625
+ // Exit and let the mouse handler handle this button (if appropriate)
626
+ return;
627
+ }
628
+
629
+ // Default system back button behavior.
630
+ if (!isFinishing()) {
631
+ super.onBackPressed();
632
+ }
633
+ }
634
+
635
+ // Called by JNI from SDL.
636
+ public static void manualBackButton() {
637
+ mSingleton.pressBackButton();
638
+ }
639
+
640
+ // Used to get us onto the activity's main thread
641
+ public void pressBackButton() {
642
+ runOnUiThread(new Runnable() {
643
+ @Override
644
+ public void run() {
645
+ if (!SDLActivity.this.isFinishing()) {
646
+ SDLActivity.this.superOnBackPressed();
647
+ }
648
+ }
649
+ });
650
+ }
651
+
652
+ // Used to access the system back behavior.
653
+ public void superOnBackPressed() {
654
+ super.onBackPressed();
655
+ }
656
+
657
+ @Override
658
+ public boolean dispatchKeyEvent(KeyEvent event) {
659
+
660
+ if (SDLActivity.mBrokenLibraries) {
661
+ return false;
662
+ }
663
+
664
+ int keyCode = event.getKeyCode();
665
+ // Ignore certain special keys so they're handled by Android
666
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
667
+ keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
668
+ keyCode == KeyEvent.KEYCODE_CAMERA ||
669
+ keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */
670
+ keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */
671
+ ) {
672
+ return false;
673
+ }
674
+ return super.dispatchKeyEvent(event);
675
+ }
676
+
677
+ /* Transition to next state */
678
+ public static void handleNativeState() {
679
+
680
+ if (mNextNativeState == mCurrentNativeState) {
681
+ // Already in same state, discard.
682
+ return;
683
+ }
684
+
685
+ // Try a transition to init state
686
+ if (mNextNativeState == NativeState.INIT) {
687
+
688
+ mCurrentNativeState = mNextNativeState;
689
+ return;
690
+ }
691
+
692
+ // Try a transition to paused state
693
+ if (mNextNativeState == NativeState.PAUSED) {
694
+ if (mSDLThread != null) {
695
+ nativePause();
696
+ }
697
+ if (mSurface != null) {
698
+ mSurface.handlePause();
699
+ }
700
+ mCurrentNativeState = mNextNativeState;
701
+ return;
702
+ }
703
+
704
+ // Try a transition to resumed state
705
+ if (mNextNativeState == NativeState.RESUMED) {
706
+ if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
707
+ if (mSDLThread == null) {
708
+ // This is the entry point to the C app.
709
+ // Start up the C app thread and enable sensor input for the first time
710
+ // FIXME: Why aren't we enabling sensor input at start?
711
+
712
+ mSDLThread = new Thread(new SDLMain(), "SDLThread");
713
+ mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
714
+ mSDLThread.start();
715
+
716
+ // No nativeResume(), don't signal Android_ResumeSem
717
+ } else {
718
+ nativeResume();
719
+ }
720
+ mSurface.handleResume();
721
+
722
+ mCurrentNativeState = mNextNativeState;
723
+ }
724
+ }
725
+ }
726
+
727
+ // Messages from the SDLMain thread
728
+ static final int COMMAND_CHANGE_TITLE = 1;
729
+ static final int COMMAND_CHANGE_WINDOW_STYLE = 2;
730
+ static final int COMMAND_TEXTEDIT_HIDE = 3;
731
+ static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
732
+
733
+ protected static final int COMMAND_USER = 0x8000;
734
+
735
+ protected static boolean mFullscreenModeActive;
736
+
737
+ /**
738
+ * This method is called by SDL if SDL did not handle a message itself.
739
+ * This happens if a received message contains an unsupported command.
740
+ * Method can be overwritten to handle Messages in a different class.
741
+ * @param command the command of the message.
742
+ * @param param the parameter of the message. May be null.
743
+ * @return if the message was handled in overridden method.
744
+ */
745
+ protected boolean onUnhandledMessage(int command, Object param) {
746
+ return false;
747
+ }
748
+
749
+ /**
750
+ * A Handler class for Messages from native SDL applications.
751
+ * It uses current Activities as target (e.g. for the title).
752
+ * static to prevent implicit references to enclosing object.
753
+ */
754
+ protected static class SDLCommandHandler extends Handler {
755
+ @Override
756
+ public void handleMessage(Message msg) {
757
+ Context context = SDL.getContext();
758
+ if (context == null) {
759
+ Log.e(TAG, "error handling message, getContext() returned null");
760
+ return;
761
+ }
762
+ switch (msg.arg1) {
763
+ case COMMAND_CHANGE_TITLE:
764
+ if (context instanceof Activity) {
765
+ ((Activity) context).setTitle((String)msg.obj);
766
+ } else {
767
+ Log.e(TAG, "error handling message, getContext() returned no Activity");
768
+ }
769
+ break;
770
+ case COMMAND_CHANGE_WINDOW_STYLE:
771
+ if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
772
+ if (context instanceof Activity) {
773
+ Window window = ((Activity) context).getWindow();
774
+ if (window != null) {
775
+ if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) {
776
+ int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
777
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
778
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
779
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
780
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
781
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
782
+ window.getDecorView().setSystemUiVisibility(flags);
783
+ window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
784
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
785
+ SDLActivity.mFullscreenModeActive = true;
786
+ } else {
787
+ int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE;
788
+ window.getDecorView().setSystemUiVisibility(flags);
789
+ window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
790
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
791
+ SDLActivity.mFullscreenModeActive = false;
792
+ }
793
+ if (Build.VERSION.SDK_INT >= 28 /* Android 9 (Pie) */) {
794
+ window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
795
+ }
796
+ }
797
+ } else {
798
+ Log.e(TAG, "error handling message, getContext() returned no Activity");
799
+ }
800
+ }
801
+ break;
802
+ case COMMAND_TEXTEDIT_HIDE:
803
+ if (mTextEdit != null) {
804
+ // Note: On some devices setting view to GONE creates a flicker in landscape.
805
+ // Setting the View's sizes to 0 is similar to GONE but without the flicker.
806
+ // The sizes will be set to useful values when the keyboard is shown again.
807
+ mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
808
+
809
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
810
+ imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
811
+
812
+ mScreenKeyboardShown = false;
813
+
814
+ mSurface.requestFocus();
815
+ }
816
+ break;
817
+ case COMMAND_SET_KEEP_SCREEN_ON:
818
+ {
819
+ if (context instanceof Activity) {
820
+ Window window = ((Activity) context).getWindow();
821
+ if (window != null) {
822
+ if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) {
823
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
824
+ } else {
825
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
826
+ }
827
+ }
828
+ }
829
+ break;
830
+ }
831
+ default:
832
+ if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
833
+ Log.e(TAG, "error handling message, command is " + msg.arg1);
834
+ }
835
+ }
836
+ }
837
+ }
838
+
839
+ // Handler for the messages
840
+ Handler commandHandler = new SDLCommandHandler();
841
+
842
+ // Send a message from the SDLMain thread
843
+ boolean sendCommand(int command, Object data) {
844
+ Message msg = commandHandler.obtainMessage();
845
+ msg.arg1 = command;
846
+ msg.obj = data;
847
+ boolean result = commandHandler.sendMessage(msg);
848
+
849
+ if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
850
+ if (command == COMMAND_CHANGE_WINDOW_STYLE) {
851
+ // Ensure we don't return until the resize has actually happened,
852
+ // or 500ms have passed.
853
+
854
+ boolean bShouldWait = false;
855
+
856
+ if (data instanceof Integer) {
857
+ // Let's figure out if we're already laid out fullscreen or not.
858
+ Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
859
+ DisplayMetrics realMetrics = new DisplayMetrics();
860
+ display.getRealMetrics(realMetrics);
861
+
862
+ boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) &&
863
+ (realMetrics.heightPixels == mSurface.getHeight()));
864
+
865
+ if ((Integer) data == 1) {
866
+ // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going
867
+ // to change size and should wait for surfaceChanged() before we return, so the size
868
+ // is right back in native code. If we're already laid out fullscreen, though, we're
869
+ // not going to change size even if we change decor modes, so we shouldn't wait for
870
+ // surfaceChanged() -- which may not even happen -- and should return immediately.
871
+ bShouldWait = !bFullscreenLayout;
872
+ } else {
873
+ // If we're laid out fullscreen (even if the status bar and nav bar are present),
874
+ // or are actively in fullscreen, we're going to change size and should wait for
875
+ // surfaceChanged before we return, so the size is right back in native code.
876
+ bShouldWait = bFullscreenLayout;
877
+ }
878
+ }
879
+
880
+ if (bShouldWait && (SDLActivity.getContext() != null)) {
881
+ // We'll wait for the surfaceChanged() method, which will notify us
882
+ // when called. That way, we know our current size is really the
883
+ // size we need, instead of grabbing a size that's still got
884
+ // the navigation and/or status bars before they're hidden.
885
+ //
886
+ // We'll wait for up to half a second, because some devices
887
+ // take a surprisingly long time for the surface resize, but
888
+ // then we'll just give up and return.
889
+ //
890
+ synchronized (SDLActivity.getContext()) {
891
+ try {
892
+ SDLActivity.getContext().wait(500);
893
+ } catch (InterruptedException ie) {
894
+ ie.printStackTrace();
895
+ }
896
+ }
897
+ }
898
+ }
899
+ }
900
+
901
+ return result;
902
+ }
903
+
904
+ // C functions we call
905
+ public static native String nativeGetVersion();
906
+ public static native int nativeSetupJNI();
907
+ public static native int nativeRunMain(String library, String function, Object arguments);
908
+ public static native void nativeLowMemory();
909
+ public static native void nativeSendQuit();
910
+ public static native void nativeQuit();
911
+ public static native void nativePause();
912
+ public static native void nativeResume();
913
+ public static native void nativeFocusChanged(boolean hasFocus);
914
+ public static native void onNativeDropFile(String filename);
915
+ public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate);
916
+ public static native void onNativeResize();
917
+ public static native void onNativeKeyDown(int keycode);
918
+ public static native void onNativeKeyUp(int keycode);
919
+ public static native boolean onNativeSoftReturnKey();
920
+ public static native void onNativeKeyboardFocusLost();
921
+ public static native void onNativeMouse(int button, int action, float x, float y, boolean relative);
922
+ public static native void onNativeTouch(int touchDevId, int pointerFingerId,
923
+ int action, float x,
924
+ float y, float p);
925
+ public static native void onNativeAccel(float x, float y, float z);
926
+ public static native void onNativeClipboardChanged();
927
+ public static native void onNativeSurfaceCreated();
928
+ public static native void onNativeSurfaceChanged();
929
+ public static native void onNativeSurfaceDestroyed();
930
+ public static native String nativeGetHint(String name);
931
+ public static native boolean nativeGetHintBoolean(String name, boolean default_value);
932
+ public static native void nativeSetenv(String name, String value);
933
+ public static native void onNativeOrientationChanged(int orientation);
934
+ public static native void nativeAddTouch(int touchId, String name);
935
+ public static native void nativePermissionResult(int requestCode, boolean result);
936
+ public static native void onNativeLocaleChanged();
937
+
938
+ /**
939
+ * This method is called by SDL using JNI.
940
+ */
941
+ public static boolean setActivityTitle(String title) {
942
+ // Called from SDLMain() thread and can't directly affect the view
943
+ return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
944
+ }
945
+
946
+ /**
947
+ * This method is called by SDL using JNI.
948
+ */
949
+ public static void setWindowStyle(boolean fullscreen) {
950
+ // Called from SDLMain() thread and can't directly affect the view
951
+ mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0);
952
+ }
953
+
954
+ /**
955
+ * This method is called by SDL using JNI.
956
+ * This is a static method for JNI convenience, it calls a non-static method
957
+ * so that is can be overridden
958
+ */
959
+ public static void setOrientation(int w, int h, boolean resizable, String hint)
960
+ {
961
+ if (mSingleton != null) {
962
+ mSingleton.setOrientationBis(w, h, resizable, hint);
963
+ }
964
+ }
965
+
966
+ /**
967
+ * This can be overridden
968
+ */
969
+ public void setOrientationBis(int w, int h, boolean resizable, String hint)
970
+ {
971
+ int orientation_landscape = -1;
972
+ int orientation_portrait = -1;
973
+
974
+ /* If set, hint "explicitly controls which UI orientations are allowed". */
975
+ if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
976
+ orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
977
+ } else if (hint.contains("LandscapeLeft")) {
978
+ orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
979
+ } else if (hint.contains("LandscapeRight")) {
980
+ orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
981
+ }
982
+
983
+ /* exact match to 'Portrait' to distinguish with PortraitUpsideDown */
984
+ boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait");
985
+
986
+ if (contains_Portrait && hint.contains("PortraitUpsideDown")) {
987
+ orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
988
+ } else if (contains_Portrait) {
989
+ orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
990
+ } else if (hint.contains("PortraitUpsideDown")) {
991
+ orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
992
+ }
993
+
994
+ boolean is_landscape_allowed = (orientation_landscape != -1);
995
+ boolean is_portrait_allowed = (orientation_portrait != -1);
996
+ int req; /* Requested orientation */
997
+
998
+ /* No valid hint, nothing is explicitly allowed */
999
+ if (!is_portrait_allowed && !is_landscape_allowed) {
1000
+ if (resizable) {
1001
+ /* All orientations are allowed, respecting user orientation lock setting */
1002
+ req = ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
1003
+ } else {
1004
+ /* Fixed window and nothing specified. Get orientation from w/h of created window */
1005
+ req = (w > h ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
1006
+ }
1007
+ } else {
1008
+ /* At least one orientation is allowed */
1009
+ if (resizable) {
1010
+ if (is_portrait_allowed && is_landscape_allowed) {
1011
+ /* hint allows both landscape and portrait, promote to full user */
1012
+ req = ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
1013
+ } else {
1014
+ /* Use the only one allowed "orientation" */
1015
+ req = (is_landscape_allowed ? orientation_landscape : orientation_portrait);
1016
+ }
1017
+ } else {
1018
+ /* Fixed window and both orientations are allowed. Choose one. */
1019
+ if (is_portrait_allowed && is_landscape_allowed) {
1020
+ req = (w > h ? orientation_landscape : orientation_portrait);
1021
+ } else {
1022
+ /* Use the only one allowed "orientation" */
1023
+ req = (is_landscape_allowed ? orientation_landscape : orientation_portrait);
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ Log.v(TAG, "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint);
1029
+ mSingleton.setRequestedOrientation(req);
1030
+ }
1031
+
1032
+ /**
1033
+ * This method is called by SDL using JNI.
1034
+ */
1035
+ public static void minimizeWindow() {
1036
+
1037
+ if (mSingleton == null) {
1038
+ return;
1039
+ }
1040
+
1041
+ Intent startMain = new Intent(Intent.ACTION_MAIN);
1042
+ startMain.addCategory(Intent.CATEGORY_HOME);
1043
+ startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1044
+ mSingleton.startActivity(startMain);
1045
+ }
1046
+
1047
+ /**
1048
+ * This method is called by SDL using JNI.
1049
+ */
1050
+ public static boolean shouldMinimizeOnFocusLoss() {
1051
+ /*
1052
+ if (Build.VERSION.SDK_INT >= 24) {
1053
+ if (mSingleton == null) {
1054
+ return true;
1055
+ }
1056
+
1057
+ if (mSingleton.isInMultiWindowMode()) {
1058
+ return false;
1059
+ }
1060
+
1061
+ if (mSingleton.isInPictureInPictureMode()) {
1062
+ return false;
1063
+ }
1064
+ }
1065
+
1066
+ return true;
1067
+ */
1068
+ return false;
1069
+ }
1070
+
1071
+ /**
1072
+ * This method is called by SDL using JNI.
1073
+ */
1074
+ public static boolean isScreenKeyboardShown()
1075
+ {
1076
+ if (mTextEdit == null) {
1077
+ return false;
1078
+ }
1079
+
1080
+ if (!mScreenKeyboardShown) {
1081
+ return false;
1082
+ }
1083
+
1084
+ InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1085
+ return imm.isAcceptingText();
1086
+
1087
+ }
1088
+
1089
+ /**
1090
+ * This method is called by SDL using JNI.
1091
+ */
1092
+ public static boolean supportsRelativeMouse()
1093
+ {
1094
+ // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under
1095
+ // Android 7 APIs, and simply returns no data under Android 8 APIs.
1096
+ //
1097
+ // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and
1098
+ // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result,
1099
+ // we should stick to relative mode.
1100
+ //
1101
+ if (Build.VERSION.SDK_INT < 27 /* Android 8.1 (O_MR1) */ && isDeXMode()) {
1102
+ return false;
1103
+ }
1104
+
1105
+ return SDLActivity.getMotionListener().supportsRelativeMouse();
1106
+ }
1107
+
1108
+ /**
1109
+ * This method is called by SDL using JNI.
1110
+ */
1111
+ public static boolean setRelativeMouseEnabled(boolean enabled)
1112
+ {
1113
+ if (enabled && !supportsRelativeMouse()) {
1114
+ return false;
1115
+ }
1116
+
1117
+ return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled);
1118
+ }
1119
+
1120
+ /**
1121
+ * This method is called by SDL using JNI.
1122
+ */
1123
+ public static boolean sendMessage(int command, int param) {
1124
+ if (mSingleton == null) {
1125
+ return false;
1126
+ }
1127
+ return mSingleton.sendCommand(command, param);
1128
+ }
1129
+
1130
+ /**
1131
+ * This method is called by SDL using JNI.
1132
+ */
1133
+ public static Context getContext() {
1134
+ return SDL.getContext();
1135
+ }
1136
+
1137
+ /**
1138
+ * This method is called by SDL using JNI.
1139
+ */
1140
+ public static boolean isAndroidTV() {
1141
+ UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
1142
+ if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
1143
+ return true;
1144
+ }
1145
+ if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) {
1146
+ return true;
1147
+ }
1148
+ if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) {
1149
+ return true;
1150
+ }
1151
+ return Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV");
1152
+ }
1153
+
1154
+ public static double getDiagonal()
1155
+ {
1156
+ DisplayMetrics metrics = new DisplayMetrics();
1157
+ Activity activity = (Activity)getContext();
1158
+ if (activity == null) {
1159
+ return 0.0;
1160
+ }
1161
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
1162
+
1163
+ double dWidthInches = metrics.widthPixels / (double)metrics.xdpi;
1164
+ double dHeightInches = metrics.heightPixels / (double)metrics.ydpi;
1165
+
1166
+ return Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches));
1167
+ }
1168
+
1169
+ /**
1170
+ * This method is called by SDL using JNI.
1171
+ */
1172
+ public static boolean isTablet() {
1173
+ // If our diagonal size is seven inches or greater, we consider ourselves a tablet.
1174
+ return (getDiagonal() >= 7.0);
1175
+ }
1176
+
1177
+ /**
1178
+ * This method is called by SDL using JNI.
1179
+ */
1180
+ public static boolean isChromebook() {
1181
+ if (getContext() == null) {
1182
+ return false;
1183
+ }
1184
+ return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
1185
+ }
1186
+
1187
+ /**
1188
+ * This method is called by SDL using JNI.
1189
+ */
1190
+ public static boolean isDeXMode() {
1191
+ if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
1192
+ return false;
1193
+ }
1194
+ try {
1195
+ final Configuration config = getContext().getResources().getConfiguration();
1196
+ final Class<?> configClass = config.getClass();
1197
+ return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
1198
+ == configClass.getField("semDesktopModeEnabled").getInt(config);
1199
+ } catch(Exception ignored) {
1200
+ return false;
1201
+ }
1202
+ }
1203
+
1204
+ /**
1205
+ * This method is called by SDL using JNI.
1206
+ */
1207
+ public static DisplayMetrics getDisplayDPI() {
1208
+ return getContext().getResources().getDisplayMetrics();
1209
+ }
1210
+
1211
+ /**
1212
+ * This method is called by SDL using JNI.
1213
+ */
1214
+ public static boolean getManifestEnvironmentVariables() {
1215
+ try {
1216
+ if (getContext() == null) {
1217
+ return false;
1218
+ }
1219
+
1220
+ ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
1221
+ Bundle bundle = applicationInfo.metaData;
1222
+ if (bundle == null) {
1223
+ return false;
1224
+ }
1225
+ String prefix = "SDL_ENV.";
1226
+ final int trimLength = prefix.length();
1227
+ for (String key : bundle.keySet()) {
1228
+ if (key.startsWith(prefix)) {
1229
+ String name = key.substring(trimLength);
1230
+ String value = bundle.get(key).toString();
1231
+ nativeSetenv(name, value);
1232
+ }
1233
+ }
1234
+ /* environment variables set! */
1235
+ return true;
1236
+ } catch (Exception e) {
1237
+ Log.v(TAG, "exception " + e.toString());
1238
+ }
1239
+ return false;
1240
+ }
1241
+
1242
+ // This method is called by SDLControllerManager's API 26 Generic Motion Handler.
1243
+ public static View getContentView() {
1244
+ return mLayout;
1245
+ }
1246
+
1247
+ static class ShowTextInputTask implements Runnable {
1248
+ /*
1249
+ * This is used to regulate the pan&scan method to have some offset from
1250
+ * the bottom edge of the input region and the top edge of an input
1251
+ * method (soft keyboard)
1252
+ */
1253
+ static final int HEIGHT_PADDING = 15;
1254
+
1255
+ public int x, y, w, h;
1256
+
1257
+ public ShowTextInputTask(int x, int y, int w, int h) {
1258
+ this.x = x;
1259
+ this.y = y;
1260
+ this.w = w;
1261
+ this.h = h;
1262
+
1263
+ /* Minimum size of 1 pixel, so it takes focus. */
1264
+ if (this.w <= 0) {
1265
+ this.w = 1;
1266
+ }
1267
+ if (this.h + HEIGHT_PADDING <= 0) {
1268
+ this.h = 1 - HEIGHT_PADDING;
1269
+ }
1270
+ }
1271
+
1272
+ @Override
1273
+ public void run() {
1274
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
1275
+ params.leftMargin = x;
1276
+ params.topMargin = y;
1277
+
1278
+ if (mTextEdit == null) {
1279
+ mTextEdit = new DummyEdit(SDL.getContext());
1280
+
1281
+ mLayout.addView(mTextEdit, params);
1282
+ } else {
1283
+ mTextEdit.setLayoutParams(params);
1284
+ }
1285
+
1286
+ mTextEdit.setVisibility(View.VISIBLE);
1287
+ mTextEdit.requestFocus();
1288
+
1289
+ InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1290
+ imm.showSoftInput(mTextEdit, 0);
1291
+
1292
+ mScreenKeyboardShown = true;
1293
+ }
1294
+ }
1295
+
1296
+ /**
1297
+ * This method is called by SDL using JNI.
1298
+ */
1299
+ public static boolean showTextInput(int x, int y, int w, int h) {
1300
+ // Transfer the task to the main thread as a Runnable
1301
+ return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
1302
+ }
1303
+
1304
+ public static boolean isTextInputEvent(KeyEvent event) {
1305
+
1306
+ // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT
1307
+ if (event.isCtrlPressed()) {
1308
+ return false;
1309
+ }
1310
+
1311
+ return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
1312
+ }
1313
+
1314
+ public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) {
1315
+ int deviceId = event.getDeviceId();
1316
+ int source = event.getSource();
1317
+
1318
+ if (source == InputDevice.SOURCE_UNKNOWN) {
1319
+ InputDevice device = InputDevice.getDevice(deviceId);
1320
+ if (device != null) {
1321
+ source = device.getSources();
1322
+ }
1323
+ }
1324
+
1325
+ // if (event.getAction() == KeyEvent.ACTION_DOWN) {
1326
+ // Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
1327
+ // } else if (event.getAction() == KeyEvent.ACTION_UP) {
1328
+ // Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
1329
+ // }
1330
+
1331
+ // Dispatch the different events depending on where they come from
1332
+ // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
1333
+ // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
1334
+ //
1335
+ // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
1336
+ // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
1337
+ // So, retrieve the device itself and check all of its sources
1338
+ if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
1339
+ // Note that we process events with specific key codes here
1340
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
1341
+ if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
1342
+ return true;
1343
+ }
1344
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
1345
+ if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
1346
+ return true;
1347
+ }
1348
+ }
1349
+ }
1350
+
1351
+ if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
1352
+ // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
1353
+ // they are ignored here because sending them as mouse input to SDL is messy
1354
+ if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
1355
+ switch (event.getAction()) {
1356
+ case KeyEvent.ACTION_DOWN:
1357
+ case KeyEvent.ACTION_UP:
1358
+ // mark the event as handled or it will be handled by system
1359
+ // handling KEYCODE_BACK by system will call onBackPressed()
1360
+ return true;
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
1366
+ if (isTextInputEvent(event)) {
1367
+ if (ic != null) {
1368
+ ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
1369
+ } else {
1370
+ SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
1371
+ }
1372
+ }
1373
+ onNativeKeyDown(keyCode);
1374
+ return true;
1375
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
1376
+ onNativeKeyUp(keyCode);
1377
+ return true;
1378
+ }
1379
+
1380
+ return false;
1381
+ }
1382
+
1383
+ /**
1384
+ * This method is called by SDL using JNI.
1385
+ */
1386
+ public static Surface getNativeSurface() {
1387
+ if (SDLActivity.mSurface == null) {
1388
+ return null;
1389
+ }
1390
+ return SDLActivity.mSurface.getNativeSurface();
1391
+ }
1392
+
1393
+ // Input
1394
+
1395
+ /**
1396
+ * This method is called by SDL using JNI.
1397
+ */
1398
+ public static void initTouch() {
1399
+ int[] ids = InputDevice.getDeviceIds();
1400
+
1401
+ for (int id : ids) {
1402
+ InputDevice device = InputDevice.getDevice(id);
1403
+ /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */
1404
+ if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN
1405
+ || device.isVirtual())) {
1406
+
1407
+ int touchDevId = device.getId();
1408
+ /*
1409
+ * Prevent id to be -1, since it's used in SDL internal for synthetic events
1410
+ * Appears when using Android emulator, eg:
1411
+ * adb shell input mouse tap 100 100
1412
+ * adb shell input touchscreen tap 100 100
1413
+ */
1414
+ if (touchDevId < 0) {
1415
+ touchDevId -= 1;
1416
+ }
1417
+ nativeAddTouch(touchDevId, device.getName());
1418
+ }
1419
+ }
1420
+ }
1421
+
1422
+ // Messagebox
1423
+
1424
+ /** Result of current messagebox. Also used for blocking the calling thread. */
1425
+ protected final int[] messageboxSelection = new int[1];
1426
+
1427
+ /**
1428
+ * This method is called by SDL using JNI.
1429
+ * Shows the messagebox from UI thread and block calling thread.
1430
+ * buttonFlags, buttonIds and buttonTexts must have same length.
1431
+ * @param buttonFlags array containing flags for every button.
1432
+ * @param buttonIds array containing id for every button.
1433
+ * @param buttonTexts array containing text for every button.
1434
+ * @param colors null for default or array of length 5 containing colors.
1435
+ * @return button id or -1.
1436
+ */
1437
+ public int messageboxShowMessageBox(
1438
+ final int flags,
1439
+ final String title,
1440
+ final String message,
1441
+ final int[] buttonFlags,
1442
+ final int[] buttonIds,
1443
+ final String[] buttonTexts,
1444
+ final int[] colors) {
1445
+
1446
+ messageboxSelection[0] = -1;
1447
+
1448
+ // sanity checks
1449
+
1450
+ if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
1451
+ return -1; // implementation broken
1452
+ }
1453
+
1454
+ // collect arguments for Dialog
1455
+
1456
+ final Bundle args = new Bundle();
1457
+ args.putInt("flags", flags);
1458
+ args.putString("title", title);
1459
+ args.putString("message", message);
1460
+ args.putIntArray("buttonFlags", buttonFlags);
1461
+ args.putIntArray("buttonIds", buttonIds);
1462
+ args.putStringArray("buttonTexts", buttonTexts);
1463
+ args.putIntArray("colors", colors);
1464
+
1465
+ // trigger Dialog creation on UI thread
1466
+
1467
+ runOnUiThread(new Runnable() {
1468
+ @Override
1469
+ public void run() {
1470
+ messageboxCreateAndShow(args);
1471
+ }
1472
+ });
1473
+
1474
+ // block the calling thread
1475
+
1476
+ synchronized (messageboxSelection) {
1477
+ try {
1478
+ messageboxSelection.wait();
1479
+ } catch (InterruptedException ex) {
1480
+ ex.printStackTrace();
1481
+ return -1;
1482
+ }
1483
+ }
1484
+
1485
+ // return selected value
1486
+
1487
+ return messageboxSelection[0];
1488
+ }
1489
+
1490
+ protected void messageboxCreateAndShow(Bundle args) {
1491
+
1492
+ // TODO set values from "flags" to messagebox dialog
1493
+
1494
+ // get colors
1495
+
1496
+ int[] colors = args.getIntArray("colors");
1497
+ int backgroundColor;
1498
+ int textColor;
1499
+ int buttonBorderColor;
1500
+ int buttonBackgroundColor;
1501
+ int buttonSelectedColor;
1502
+ if (colors != null) {
1503
+ int i = -1;
1504
+ backgroundColor = colors[++i];
1505
+ textColor = colors[++i];
1506
+ buttonBorderColor = colors[++i];
1507
+ buttonBackgroundColor = colors[++i];
1508
+ buttonSelectedColor = colors[++i];
1509
+ } else {
1510
+ backgroundColor = Color.TRANSPARENT;
1511
+ textColor = Color.TRANSPARENT;
1512
+ buttonBorderColor = Color.TRANSPARENT;
1513
+ buttonBackgroundColor = Color.TRANSPARENT;
1514
+ buttonSelectedColor = Color.TRANSPARENT;
1515
+ }
1516
+
1517
+ // create dialog with title and a listener to wake up calling thread
1518
+
1519
+ final AlertDialog dialog = new AlertDialog.Builder(this).create();
1520
+ dialog.setTitle(args.getString("title"));
1521
+ dialog.setCancelable(false);
1522
+ dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
1523
+ @Override
1524
+ public void onDismiss(DialogInterface unused) {
1525
+ synchronized (messageboxSelection) {
1526
+ messageboxSelection.notify();
1527
+ }
1528
+ }
1529
+ });
1530
+
1531
+ // create text
1532
+
1533
+ TextView message = new TextView(this);
1534
+ message.setGravity(Gravity.CENTER);
1535
+ message.setText(args.getString("message"));
1536
+ if (textColor != Color.TRANSPARENT) {
1537
+ message.setTextColor(textColor);
1538
+ }
1539
+
1540
+ // create buttons
1541
+
1542
+ int[] buttonFlags = args.getIntArray("buttonFlags");
1543
+ int[] buttonIds = args.getIntArray("buttonIds");
1544
+ String[] buttonTexts = args.getStringArray("buttonTexts");
1545
+
1546
+ final SparseArray<Button> mapping = new SparseArray<Button>();
1547
+
1548
+ LinearLayout buttons = new LinearLayout(this);
1549
+ buttons.setOrientation(LinearLayout.HORIZONTAL);
1550
+ buttons.setGravity(Gravity.CENTER);
1551
+ for (int i = 0; i < buttonTexts.length; ++i) {
1552
+ Button button = new Button(this);
1553
+ final int id = buttonIds[i];
1554
+ button.setOnClickListener(new View.OnClickListener() {
1555
+ @Override
1556
+ public void onClick(View v) {
1557
+ messageboxSelection[0] = id;
1558
+ dialog.dismiss();
1559
+ }
1560
+ });
1561
+ if (buttonFlags[i] != 0) {
1562
+ // see SDL_messagebox.h
1563
+ if ((buttonFlags[i] & 0x00000001) != 0) {
1564
+ mapping.put(KeyEvent.KEYCODE_ENTER, button);
1565
+ }
1566
+ if ((buttonFlags[i] & 0x00000002) != 0) {
1567
+ mapping.put(KeyEvent.KEYCODE_ESCAPE, button); /* API 11 */
1568
+ }
1569
+ }
1570
+ button.setText(buttonTexts[i]);
1571
+ if (textColor != Color.TRANSPARENT) {
1572
+ button.setTextColor(textColor);
1573
+ }
1574
+ if (buttonBorderColor != Color.TRANSPARENT) {
1575
+ // TODO set color for border of messagebox button
1576
+ }
1577
+ if (buttonBackgroundColor != Color.TRANSPARENT) {
1578
+ Drawable drawable = button.getBackground();
1579
+ if (drawable == null) {
1580
+ // setting the color this way removes the style
1581
+ button.setBackgroundColor(buttonBackgroundColor);
1582
+ } else {
1583
+ // setting the color this way keeps the style (gradient, padding, etc.)
1584
+ drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY);
1585
+ }
1586
+ }
1587
+ if (buttonSelectedColor != Color.TRANSPARENT) {
1588
+ // TODO set color for selected messagebox button
1589
+ }
1590
+ buttons.addView(button);
1591
+ }
1592
+
1593
+ // create content
1594
+
1595
+ LinearLayout content = new LinearLayout(this);
1596
+ content.setOrientation(LinearLayout.VERTICAL);
1597
+ content.addView(message);
1598
+ content.addView(buttons);
1599
+ if (backgroundColor != Color.TRANSPARENT) {
1600
+ content.setBackgroundColor(backgroundColor);
1601
+ }
1602
+
1603
+ // add content to dialog and return
1604
+
1605
+ dialog.setView(content);
1606
+ dialog.setOnKeyListener(new Dialog.OnKeyListener() {
1607
+ @Override
1608
+ public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
1609
+ Button button = mapping.get(keyCode);
1610
+ if (button != null) {
1611
+ if (event.getAction() == KeyEvent.ACTION_UP) {
1612
+ button.performClick();
1613
+ }
1614
+ return true; // also for ignored actions
1615
+ }
1616
+ return false;
1617
+ }
1618
+ });
1619
+
1620
+ dialog.show();
1621
+ }
1622
+
1623
+ private final Runnable rehideSystemUi = new Runnable() {
1624
+ @Override
1625
+ public void run() {
1626
+ if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
1627
+ int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
1628
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
1629
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
1630
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
1631
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
1632
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
1633
+
1634
+ SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags);
1635
+ }
1636
+ }
1637
+ };
1638
+
1639
+ public void onSystemUiVisibilityChange(int visibility) {
1640
+ if (SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) {
1641
+
1642
+ Handler handler = getWindow().getDecorView().getHandler();
1643
+ if (handler != null) {
1644
+ handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop.
1645
+ handler.postDelayed(rehideSystemUi, 2000);
1646
+ }
1647
+
1648
+ }
1649
+ }
1650
+
1651
+ /**
1652
+ * This method is called by SDL using JNI.
1653
+ */
1654
+ public static boolean clipboardHasText() {
1655
+ return mClipboardHandler.clipboardHasText();
1656
+ }
1657
+
1658
+ /**
1659
+ * This method is called by SDL using JNI.
1660
+ */
1661
+ public static String clipboardGetText() {
1662
+ return mClipboardHandler.clipboardGetText();
1663
+ }
1664
+
1665
+ /**
1666
+ * This method is called by SDL using JNI.
1667
+ */
1668
+ public static void clipboardSetText(String string) {
1669
+ mClipboardHandler.clipboardSetText(string);
1670
+ }
1671
+
1672
+ /**
1673
+ * This method is called by SDL using JNI.
1674
+ */
1675
+ public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
1676
+ Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
1677
+ ++mLastCursorID;
1678
+
1679
+ if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
1680
+ try {
1681
+ mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY));
1682
+ } catch (Exception e) {
1683
+ return 0;
1684
+ }
1685
+ } else {
1686
+ return 0;
1687
+ }
1688
+ return mLastCursorID;
1689
+ }
1690
+
1691
+ /**
1692
+ * This method is called by SDL using JNI.
1693
+ */
1694
+ public static void destroyCustomCursor(int cursorID) {
1695
+ if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
1696
+ try {
1697
+ mCursors.remove(cursorID);
1698
+ } catch (Exception e) {
1699
+ }
1700
+ }
1701
+ return;
1702
+ }
1703
+
1704
+ /**
1705
+ * This method is called by SDL using JNI.
1706
+ */
1707
+ public static boolean setCustomCursor(int cursorID) {
1708
+
1709
+ if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
1710
+ try {
1711
+ mSurface.setPointerIcon(mCursors.get(cursorID));
1712
+ } catch (Exception e) {
1713
+ return false;
1714
+ }
1715
+ } else {
1716
+ return false;
1717
+ }
1718
+ return true;
1719
+ }
1720
+
1721
+ /**
1722
+ * This method is called by SDL using JNI.
1723
+ */
1724
+ public static boolean setSystemCursor(int cursorID) {
1725
+ int cursor_type = 0; //PointerIcon.TYPE_NULL;
1726
+ switch (cursorID) {
1727
+ case SDL_SYSTEM_CURSOR_ARROW:
1728
+ cursor_type = 1000; //PointerIcon.TYPE_ARROW;
1729
+ break;
1730
+ case SDL_SYSTEM_CURSOR_IBEAM:
1731
+ cursor_type = 1008; //PointerIcon.TYPE_TEXT;
1732
+ break;
1733
+ case SDL_SYSTEM_CURSOR_WAIT:
1734
+ cursor_type = 1004; //PointerIcon.TYPE_WAIT;
1735
+ break;
1736
+ case SDL_SYSTEM_CURSOR_CROSSHAIR:
1737
+ cursor_type = 1007; //PointerIcon.TYPE_CROSSHAIR;
1738
+ break;
1739
+ case SDL_SYSTEM_CURSOR_WAITARROW:
1740
+ cursor_type = 1004; //PointerIcon.TYPE_WAIT;
1741
+ break;
1742
+ case SDL_SYSTEM_CURSOR_SIZENWSE:
1743
+ cursor_type = 1017; //PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
1744
+ break;
1745
+ case SDL_SYSTEM_CURSOR_SIZENESW:
1746
+ cursor_type = 1016; //PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
1747
+ break;
1748
+ case SDL_SYSTEM_CURSOR_SIZEWE:
1749
+ cursor_type = 1014; //PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
1750
+ break;
1751
+ case SDL_SYSTEM_CURSOR_SIZENS:
1752
+ cursor_type = 1015; //PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
1753
+ break;
1754
+ case SDL_SYSTEM_CURSOR_SIZEALL:
1755
+ cursor_type = 1020; //PointerIcon.TYPE_GRAB;
1756
+ break;
1757
+ case SDL_SYSTEM_CURSOR_NO:
1758
+ cursor_type = 1012; //PointerIcon.TYPE_NO_DROP;
1759
+ break;
1760
+ case SDL_SYSTEM_CURSOR_HAND:
1761
+ cursor_type = 1002; //PointerIcon.TYPE_HAND;
1762
+ break;
1763
+ }
1764
+ if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
1765
+ try {
1766
+ mSurface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type));
1767
+ } catch (Exception e) {
1768
+ return false;
1769
+ }
1770
+ }
1771
+ return true;
1772
+ }
1773
+
1774
+ /**
1775
+ * This method is called by SDL using JNI.
1776
+ */
1777
+ public static void requestPermission(String permission, int requestCode) {
1778
+ if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
1779
+ nativePermissionResult(requestCode, true);
1780
+ return;
1781
+ }
1782
+
1783
+ Activity activity = (Activity)getContext();
1784
+ if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
1785
+ activity.requestPermissions(new String[]{permission}, requestCode);
1786
+ } else {
1787
+ nativePermissionResult(requestCode, true);
1788
+ }
1789
+ }
1790
+
1791
+ @Override
1792
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
1793
+ boolean result = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
1794
+ nativePermissionResult(requestCode, result);
1795
+ }
1796
+
1797
+ /**
1798
+ * This method is called by SDL using JNI.
1799
+ */
1800
+ public static int openURL(String url)
1801
+ {
1802
+ try {
1803
+ Intent i = new Intent(Intent.ACTION_VIEW);
1804
+ i.setData(Uri.parse(url));
1805
+
1806
+ int flags = Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
1807
+ if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
1808
+ flags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
1809
+ } else {
1810
+ flags |= Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
1811
+ }
1812
+ i.addFlags(flags);
1813
+
1814
+ mSingleton.startActivity(i);
1815
+ } catch (Exception ex) {
1816
+ return -1;
1817
+ }
1818
+ return 0;
1819
+ }
1820
+
1821
+ /**
1822
+ * This method is called by SDL using JNI.
1823
+ */
1824
+ public static int showToast(String message, int duration, int gravity, int xOffset, int yOffset)
1825
+ {
1826
+ if(null == mSingleton) {
1827
+ return - 1;
1828
+ }
1829
+
1830
+ try
1831
+ {
1832
+ class OneShotTask implements Runnable {
1833
+ String mMessage;
1834
+ int mDuration;
1835
+ int mGravity;
1836
+ int mXOffset;
1837
+ int mYOffset;
1838
+
1839
+ OneShotTask(String message, int duration, int gravity, int xOffset, int yOffset) {
1840
+ mMessage = message;
1841
+ mDuration = duration;
1842
+ mGravity = gravity;
1843
+ mXOffset = xOffset;
1844
+ mYOffset = yOffset;
1845
+ }
1846
+
1847
+ public void run() {
1848
+ try
1849
+ {
1850
+ Toast toast = Toast.makeText(mSingleton, mMessage, mDuration);
1851
+ if (mGravity >= 0) {
1852
+ toast.setGravity(mGravity, mXOffset, mYOffset);
1853
+ }
1854
+ toast.show();
1855
+ } catch(Exception ex) {
1856
+ Log.e(TAG, ex.getMessage());
1857
+ }
1858
+ }
1859
+ }
1860
+ mSingleton.runOnUiThread(new OneShotTask(message, duration, gravity, xOffset, yOffset));
1861
+ } catch(Exception ex) {
1862
+ return -1;
1863
+ }
1864
+ return 0;
1865
+ }
1866
+ }
1867
+
1868
+ /**
1869
+ Simple runnable to start the SDL application
1870
+ */
1871
+ class SDLMain implements Runnable {
1872
+ @Override
1873
+ public void run() {
1874
+ // Runs SDL_main()
1875
+ String library = SDLActivity.mSingleton.getMainSharedObject();
1876
+ String function = SDLActivity.mSingleton.getMainFunction();
1877
+ String[] arguments = SDLActivity.mSingleton.getArguments();
1878
+
1879
+ try {
1880
+ android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY);
1881
+ } catch (Exception e) {
1882
+ Log.v("SDL", "modify thread properties failed " + e.toString());
1883
+ }
1884
+
1885
+ Log.v("SDL", "Running main function " + function + " from library " + library);
1886
+
1887
+ SDLActivity.nativeRunMain(library, function, arguments);
1888
+
1889
+ Log.v("SDL", "Finished main function");
1890
+
1891
+ if (SDLActivity.mSingleton != null && !SDLActivity.mSingleton.isFinishing()) {
1892
+ // Let's finish the Activity
1893
+ SDLActivity.mSDLThread = null;
1894
+ SDLActivity.mSingleton.finish();
1895
+ } // else: Activity is already being destroyed
1896
+
1897
+ }
1898
+ }
1899
+
1900
+ /* This is a fake invisible editor view that receives the input and defines the
1901
+ * pan&scan region
1902
+ */
1903
+ class DummyEdit extends View implements View.OnKeyListener {
1904
+ InputConnection ic;
1905
+
1906
+ public DummyEdit(Context context) {
1907
+ super(context);
1908
+ setFocusableInTouchMode(true);
1909
+ setFocusable(true);
1910
+ setOnKeyListener(this);
1911
+ }
1912
+
1913
+ @Override
1914
+ public boolean onCheckIsTextEditor() {
1915
+ return true;
1916
+ }
1917
+
1918
+ @Override
1919
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
1920
+ return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
1921
+ }
1922
+
1923
+ //
1924
+ @Override
1925
+ public boolean onKeyPreIme (int keyCode, KeyEvent event) {
1926
+ // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
1927
+ // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
1928
+ // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
1929
+ // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
1930
+ // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
1931
+ // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
1932
+ if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
1933
+ if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
1934
+ SDLActivity.onNativeKeyboardFocusLost();
1935
+ }
1936
+ }
1937
+ return super.onKeyPreIme(keyCode, event);
1938
+ }
1939
+
1940
+ @Override
1941
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1942
+ ic = new SDLInputConnection(this, true);
1943
+
1944
+ outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
1945
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE;
1946
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
1947
+ EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
1948
+
1949
+ return ic;
1950
+ }
1951
+ }
1952
+
1953
+ class SDLInputConnection extends BaseInputConnection {
1954
+
1955
+ protected EditText mEditText;
1956
+ protected String mCommittedText = "";
1957
+
1958
+ public SDLInputConnection(View targetView, boolean fullEditor) {
1959
+ super(targetView, fullEditor);
1960
+ mEditText = new EditText(SDL.getContext());
1961
+ }
1962
+
1963
+ @Override
1964
+ public Editable getEditable() {
1965
+ return mEditText.getEditableText();
1966
+ }
1967
+
1968
+ @Override
1969
+ public boolean sendKeyEvent(KeyEvent event) {
1970
+ /*
1971
+ * This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
1972
+ * However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses
1973
+ * and so we need to generate them ourselves in commitText. To avoid duplicates on the handful of keys
1974
+ * that still do, we empty this out.
1975
+ */
1976
+
1977
+ /*
1978
+ * Return DOES still generate a key event, however. So rather than using it as the 'click a button' key
1979
+ * as we do with physical keyboards, let's just use it to hide the keyboard.
1980
+ */
1981
+
1982
+ if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
1983
+ if (SDLActivity.onNativeSoftReturnKey()) {
1984
+ return true;
1985
+ }
1986
+ }
1987
+
1988
+ return super.sendKeyEvent(event);
1989
+ }
1990
+
1991
+ @Override
1992
+ public boolean commitText(CharSequence text, int newCursorPosition) {
1993
+ if (!super.commitText(text, newCursorPosition)) {
1994
+ return false;
1995
+ }
1996
+ updateText();
1997
+ return true;
1998
+ }
1999
+
2000
+ @Override
2001
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
2002
+ if (!super.setComposingText(text, newCursorPosition)) {
2003
+ return false;
2004
+ }
2005
+ updateText();
2006
+ return true;
2007
+ }
2008
+
2009
+ @Override
2010
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
2011
+ if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
2012
+ // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
2013
+ // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
2014
+ if (beforeLength > 0 && afterLength == 0) {
2015
+ // backspace(s)
2016
+ while (beforeLength-- > 0) {
2017
+ nativeGenerateScancodeForUnichar('\b');
2018
+ }
2019
+ return true;
2020
+ }
2021
+ }
2022
+
2023
+ if (!super.deleteSurroundingText(beforeLength, afterLength)) {
2024
+ return false;
2025
+ }
2026
+ updateText();
2027
+ return true;
2028
+ }
2029
+
2030
+ protected void updateText() {
2031
+ final Editable content = getEditable();
2032
+ if (content == null) {
2033
+ return;
2034
+ }
2035
+
2036
+ String text = content.toString();
2037
+ int compareLength = Math.min(text.length(), mCommittedText.length());
2038
+ int matchLength, offset;
2039
+
2040
+ /* Backspace over characters that are no longer in the string */
2041
+ for (matchLength = 0; matchLength < compareLength; ) {
2042
+ int codePoint = mCommittedText.codePointAt(matchLength);
2043
+ if (codePoint != text.codePointAt(matchLength)) {
2044
+ break;
2045
+ }
2046
+ matchLength += Character.charCount(codePoint);
2047
+ }
2048
+ /* FIXME: This doesn't handle graphemes, like '🌬️' */
2049
+ for (offset = matchLength; offset < mCommittedText.length(); ) {
2050
+ int codePoint = mCommittedText.codePointAt(offset);
2051
+ nativeGenerateScancodeForUnichar('\b');
2052
+ offset += Character.charCount(codePoint);
2053
+ }
2054
+
2055
+ if (matchLength < text.length()) {
2056
+ String pendingText = text.subSequence(matchLength, text.length()).toString();
2057
+ for (offset = 0; offset < pendingText.length(); ) {
2058
+ int codePoint = pendingText.codePointAt(offset);
2059
+ if (codePoint == '\n') {
2060
+ if (SDLActivity.onNativeSoftReturnKey()) {
2061
+ return;
2062
+ }
2063
+ }
2064
+ /* Higher code points don't generate simulated scancodes */
2065
+ if (codePoint < 128) {
2066
+ nativeGenerateScancodeForUnichar((char)codePoint);
2067
+ }
2068
+ offset += Character.charCount(codePoint);
2069
+ }
2070
+ SDLInputConnection.nativeCommitText(pendingText, 0);
2071
+ }
2072
+ mCommittedText = text;
2073
+ }
2074
+
2075
+ public static native void nativeCommitText(String text, int newCursorPosition);
2076
+
2077
+ public static native void nativeGenerateScancodeForUnichar(char c);
2078
+ }
2079
+
2080
+ class SDLClipboardHandler implements
2081
+ ClipboardManager.OnPrimaryClipChangedListener {
2082
+
2083
+ protected ClipboardManager mClipMgr;
2084
+
2085
+ SDLClipboardHandler() {
2086
+ mClipMgr = (ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
2087
+ mClipMgr.addPrimaryClipChangedListener(this);
2088
+ }
2089
+
2090
+ public boolean clipboardHasText() {
2091
+ return mClipMgr.hasPrimaryClip();
2092
+ }
2093
+
2094
+ public String clipboardGetText() {
2095
+ ClipData clip = mClipMgr.getPrimaryClip();
2096
+ if (clip != null) {
2097
+ ClipData.Item item = clip.getItemAt(0);
2098
+ if (item != null) {
2099
+ CharSequence text = item.getText();
2100
+ if (text != null) {
2101
+ return text.toString();
2102
+ }
2103
+ }
2104
+ }
2105
+ return null;
2106
+ }
2107
+
2108
+ public void clipboardSetText(String string) {
2109
+ mClipMgr.removePrimaryClipChangedListener(this);
2110
+ ClipData clip = ClipData.newPlainText(null, string);
2111
+ mClipMgr.setPrimaryClip(clip);
2112
+ mClipMgr.addPrimaryClipChangedListener(this);
2113
+ }
2114
+
2115
+ @Override
2116
+ public void onPrimaryClipChanged() {
2117
+ SDLActivity.onNativeClipboardChanged();
2118
+ }
2119
+ }
2120
+