Android.cpp 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. /******************************************************************************
  2. Android platform application life cycle:
  3. -constructors for global objects are called
  4. -'android_main' is called (with full loop)
  5. -'android_main' can be called again (with full loop)
  6. -..
  7. -if OS decides to kill the app, then probably destructors will be called (but it is unknown)
  8. When using WORK_IN_BACKGROUND then 'android_main' with full loop will continue to run even when app is closed/minimized.
  9. In that case when trying to remove the app from recent apps list (for example by "close all" or "swipe" in the apps list)
  10. then the loop will be stopped and 'android_main' will return, HOWEVER 'BackgroundThread' will continue to run.
  11. After that, 'android_main' may be called again if the app is started again.
  12. JNI object life exists as long as we're in native (C++) mode, it will be automatically deleted once we're back to Java.
  13. Therefore objects that want to be stored globally across multiple threads, must use 'NewGlobalRef' and later 'DeleteGlobalRef'.
  14. 'jfieldID' and 'jmethodID' should NOT be passed to 'NewGlobalRef', 'DeleteGlobalRef' or 'DeleteLocalRef'.
  15. https://developer.android.com/training/articles/perf-jni.html
  16. JNI Sample Usage documentation link:
  17. http://android.wooyd.org/JNIExample/
  18. 'KeyCharacterMapGet' does not convert meta to accents, what we would need are accented characters to be used with 'getDeadChar'.
  19. However NDK input does not provide COMBINING_ACCENT and COMBINING_ACCENT_MASK for key codes.
  20. I still need to use 'KeyCharacterMap' instead of converting buttons to characters, because on Swiftkey Android 2.3 pressing '(' results in 'k' character, and 'KeyCharacterMap' fixes that.
  21. Type Signature Java Type
  22. Z boolean
  23. B byte
  24. C char
  25. S short
  26. I int
  27. J long
  28. F float
  29. D double
  30. L*; "*" class
  31. [* *[] array
  32. (params)ret method
  33. For example, the Java method: long f(int n, String s, int[] arr);
  34. has the following type signature: (ILjava/lang/String;[I)J
  35. /******************************************************************************/
  36. #include "stdafx.h"
  37. #if ANDROID
  38. ASSERT(SIZE(Long)>=SIZE(Ptr)); // some pointers are processed using Long's on Java
  39. namespace EE{
  40. /******************************************************************************/
  41. #ifndef AMETA_CAPS_LOCK_ON
  42. #define AMETA_CAPS_LOCK_ON 0x100000
  43. #endif
  44. #ifndef AINPUT_SOURCE_STYLUS
  45. #define AINPUT_SOURCE_STYLUS (0x00004000|AINPUT_SOURCE_CLASS_POINTER)
  46. #endif
  47. #ifndef AMOTION_EVENT_ACTION_HOVER_MOVE
  48. #define AMOTION_EVENT_ACTION_HOVER_MOVE 7
  49. #endif
  50. #ifndef AMOTION_EVENT_ACTION_SCROLL
  51. #define AMOTION_EVENT_ACTION_SCROLL 8
  52. #endif
  53. #ifndef AMOTION_EVENT_ACTION_HOVER_ENTER
  54. #define AMOTION_EVENT_ACTION_HOVER_ENTER 9
  55. #endif
  56. #ifndef AMOTION_EVENT_ACTION_HOVER_EXIT
  57. #define AMOTION_EVENT_ACTION_HOVER_EXIT 10
  58. #endif
  59. #ifndef AMOTION_EVENT_AXIS_VSCROLL
  60. #define AMOTION_EVENT_AXIS_VSCROLL 9
  61. #endif
  62. #ifndef AMOTION_EVENT_AXIS_HSCROLL
  63. #define AMOTION_EVENT_AXIS_HSCROLL 10
  64. #endif
  65. #ifndef AINPUT_SOURCE_JOYSTICK
  66. #define AINPUT_SOURCE_JOYSTICK 0x01000010
  67. #endif
  68. #ifndef AMOTION_EVENT_AXIS_X
  69. #define AMOTION_EVENT_AXIS_X 0
  70. #endif
  71. #ifndef AMOTION_EVENT_AXIS_Y
  72. #define AMOTION_EVENT_AXIS_Y 1
  73. #endif
  74. #ifndef AMOTION_EVENT_AXIS_RX
  75. #define AMOTION_EVENT_AXIS_RX 12
  76. #endif
  77. #ifndef AMOTION_EVENT_AXIS_RY
  78. #define AMOTION_EVENT_AXIS_RY 13
  79. #endif
  80. #ifndef AMOTION_EVENT_AXIS_HAT_X
  81. #define AMOTION_EVENT_AXIS_HAT_X 15
  82. #endif
  83. #ifndef AMOTION_EVENT_AXIS_HAT_Y
  84. #define AMOTION_EVENT_AXIS_HAT_Y 16
  85. #endif
  86. #ifndef HISTORY_CURRENT
  87. #define HISTORY_CURRENT (-0x80000000)
  88. #endif
  89. #ifndef AMETA_CTRL_ON
  90. #define AMETA_CTRL_ON 0x1000
  91. #endif
  92. #ifndef AMETA_CTRL_LEFT_ON
  93. #define AMETA_CTRL_LEFT_ON 0x2000
  94. #endif
  95. #ifndef AMETA_CTRL_RIGHT_ON
  96. #define AMETA_CTRL_RIGHT_ON 0x4000
  97. #endif
  98. #if DEBUG
  99. #define LOG(x) LogN(x)
  100. #else
  101. #define LOG(x) if(LogInit)LogN(x)
  102. #endif
  103. #define LOG2(x)
  104. /******************************************************************************/
  105. enum ROTATION
  106. {
  107. ROTATION_0 ,
  108. ROTATION_90 ,
  109. ROTATION_180,
  110. ROTATION_270,
  111. };
  112. /******************************************************************************/
  113. JNI Jni(null);
  114. static JavaVM *JVM;
  115. jobject Activity;
  116. JClass ActivityClass, ClipboardManagerClass, InputDeviceClass, KeyCharacterMapClass;
  117. JObject DefaultDisplay, ClipboardManager, LocationManager, KeyCharacterMap,
  118. GPS_PROVIDER, NETWORK_PROVIDER, EsenthelLocationListener[2];
  119. JMethodID getRotation,
  120. InputDeviceGetDevice, InputDeviceGetName,
  121. KeyCharacterMapLoad, KeyCharacterMapGet,
  122. getLastKnownLocation, getLatitude, getLongitude, getAltitude, getAccuracy, getSpeed, getTime, requestLocationUpdates, removeUpdates;
  123. Int AndroidSDK;
  124. AAssetManager *AssetManager;
  125. android_app *AndroidApp;
  126. Str8 AndroidPackageName;
  127. Str AndroidAppPath, AndroidAppDataPath, AndroidAppDataPublicPath, AndroidPublicPath, AndroidSDCardPath;
  128. static KB_KEY KeyMap[256];
  129. static Byte ShiftMap[3][128], JoyMap[256];
  130. static Bool Initialized, // !! This may be set to true when app is restarted (without previous crashing) !!
  131. KeyboardLoaded, PossibleTap;
  132. static Int KeyboardDeviceID;
  133. static Dbl PossibleTapTime;
  134. static VecI2 LastMousePos;
  135. static Thread BackgroundThread;
  136. #if __ANDROID_API__<14
  137. static JClass MotionEventClass;
  138. static JMethodID getButtonState, getRawAxisValue;
  139. #endif
  140. /******************************************************************************/
  141. static Bool BackgroundFunc(Thread &thread)
  142. {
  143. LOG("BackgroundFunc");
  144. ThreadMayUseGPUData(); // here we call Update functions which normally assume to be called on the main thread, so make sure GPU can be used just like on the main thread
  145. Jni.attach();
  146. LOG("BackgroundFunc Loop");
  147. for(;;) // do a manual loop to avoid the overhead of 'ThreadMayUseGPUData' and 'ThreadFunc' (which is needed only if we may want to pause the thread, which we won't)
  148. {
  149. if(App.background_wait)thread._resume.wait(App.background_wait); // wait requested time, Warning: here we reuse its 'SyncEvent' because it's used only for pausing/resuming
  150. if(App._close) // first check if we want to close manually
  151. {
  152. App.del(); // manually call shut down
  153. ExitNow(); // do complete reset (including state of global variables) by killing the process
  154. }
  155. if(thread.wantStop())break; // check after waiting and before updating
  156. App.update();
  157. }
  158. Jni.del();
  159. LOG("BackgroundFunc End");
  160. return false;
  161. }
  162. /******************************************************************************/
  163. void JNI::clear()
  164. {
  165. _=null;
  166. attached=false;
  167. }
  168. void JNI::del()
  169. {
  170. if(_ && attached)JVM->DetachCurrentThread();
  171. clear();
  172. }
  173. void JNI::attach()
  174. {
  175. if(!_)
  176. {
  177. if(!JVM)Exit("JVM is null");
  178. JVM->GetEnv((Ptr*)&_, JNI_VERSION_1_6);
  179. if(!_)
  180. {
  181. JVM->AttachCurrentThread(&_, null);
  182. if(_)attached=true;
  183. }
  184. if(!_)Exit("Can't attach thread to Java Virtual Machine");
  185. }
  186. }
  187. JObject& JObject::clear () {_=null; _global=false; return T;}
  188. JObject& JObject::del () {if(_ && _jni){if(_global)_jni->DeleteGlobalRef(_);else _jni->DeleteLocalRef(_);} _=null; _global=false; return T;}
  189. JObject& JObject::makeGlobal() {if(!_global && _jni && _){jobject j=_jni->NewGlobalRef(_); del(); _=j; _global=true;} return T;}
  190. JObject& JObject::operator= (jobject j) {del(); _=j; _global=false; return T;}
  191. JClass& JClass::operator=(CChar8 *name) {del(); _=_jni->FindClass (name); _global=false; return T;}
  192. JClass& JClass::operator=(jobject obj ) {del(); _=_jni->GetObjectClass(obj ); _global=false; return T;}
  193. JClass& JClass::operator=(jclass cls ) {del(); _= cls ; _global=false; return T;}
  194. JClass::JClass( CChar8 *name) : JObject( name ? Jni->FindClass (name) : null) {}
  195. JClass::JClass( jobject obj ) : JObject( Jni->GetObjectClass(obj ) ) {}
  196. JClass::JClass( jclass cls ) : JObject( cls ) {}
  197. JClass::JClass(JNI &jni, CChar8 *name) : JObject(jni, name ? jni->FindClass (name) : null) {}
  198. JClass::JClass(JNI &jni, jobject obj ) : JObject(jni, jni->GetObjectClass(obj ) ) {}
  199. JClass::JClass(JNI &jni, jclass cls ) : JObject(jni, cls ) {}
  200. JString& JString::operator=(CChar8 *t) {T=_jni->NewStringUTF( t ); return T;}
  201. JString& JString::operator=(CChar *t) {T=_jni->NewString ((jchar*)t , Length(t)); return T;}
  202. JString& JString::operator=(C Str8 &s) {T=_jni->NewStringUTF( s() ); return T;}
  203. JString& JString::operator=(C Str &s) {T=_jni->NewString ((jchar*)s(), s.length()); return T;}
  204. Int JObjectArray::elms ( )C {return _jni->GetArrayLength (T );}
  205. jobject JObjectArray::operator[](Int i)C {return _jni->GetObjectArrayElement(T, i);}
  206. JObjectArray::JObjectArray(JNI &jni, int elms) : JObject(jni, jni->NewObjectArray(elms, jni->FindClass("java/lang/String"), NULL)) {}
  207. void JObjectArray::set(Int i, CChar8 *t) {if(_ && _jni)_jni->SetObjectArrayElement(T, i, _jni->NewStringUTF(t));}
  208. /******************************************************************************/
  209. static Str JavaInputDeviceName(Int i)
  210. {
  211. if(Jni && InputDeviceGetDevice && InputDeviceGetName)
  212. if(JObject device=Jni->CallStaticObjectMethod(InputDeviceClass, InputDeviceGetDevice, jint(i)))
  213. if(JString name=Jni->CallObjectMethod(device, InputDeviceGetName))
  214. return name.str();
  215. return S;
  216. }
  217. /******************************************************************************/
  218. static void UpdateOrientation()
  219. {
  220. //switch(AConfiguration_getOrientation(AndroidApp->config)) this appears to have weird enum values: ACONFIGURATION_ORIENTATION_ANY, ACONFIGURATION_ORIENTATION_PORT, ACONFIGURATION_ORIENTATION_LAND, ACONFIGURATION_ORIENTATION_SQUARE
  221. if(Jni && getRotation)switch(Jni->CallIntMethod(DefaultDisplay, getRotation))
  222. {
  223. case ROTATION_0 : App._orientation=DIR_UP ; break;
  224. case ROTATION_90 : App._orientation=DIR_LEFT ; break;
  225. case ROTATION_180: App._orientation=DIR_DOWN ; break;
  226. case ROTATION_270: App._orientation=DIR_RIGHT; break;
  227. }
  228. }
  229. static void UpdateSize(ANativeWindow &window)
  230. {
  231. VecI2 res(ANativeWindow_getWidth (&window),
  232. ANativeWindow_getHeight(&window));
  233. if(D.res()!=res && res.x>0 && res.y>0)
  234. {
  235. LOG(S+"Resize: "+res.x+", "+res.y);
  236. Renderer._main.forceInfo(res.x, res.y, 1, Renderer._main.type(), Renderer._main.mode()); // '_main_ds' will be set in 'rtCreate'
  237. D.modeSet(res.x, res.y);
  238. }
  239. }
  240. /******************************************************************************/
  241. static Char AdjustByShift(Char c, Bool shift, Bool caps)
  242. {
  243. if(shift || caps)
  244. {
  245. if(InRange(U16(c), ShiftMap[0]))return ShiftMap[(shift | (caps<<1))-1][U16(c)];
  246. if(shift!=caps)return CaseUp(c);
  247. }
  248. return c;
  249. }
  250. static int32_t InputCallback(android_app *app, AInputEvent *event)
  251. {
  252. LOG2("InputCallback");
  253. Int device=AInputEvent_getDeviceId(event);
  254. switch( AInputEvent_getType (event))
  255. {
  256. case AINPUT_EVENT_TYPE_MOTION:
  257. {
  258. Int source =(AInputEvent_getSource(event) & ~AINPUT_SOURCE_CLASS_POINTER), // disable 'AINPUT_SOURCE_CLASS_POINTER' because all have it (including AINPUT_SOURCE_MOUSE and AINPUT_SOURCE_STYLUS)
  259. action =AMotionEvent_getAction(event),
  260. action_type = (action&AMOTION_EVENT_ACTION_MASK),
  261. action_index=((action&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)>>AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
  262. Bool stylus =FlagTest(source, AINPUT_SOURCE_STYLUS);
  263. if(source&AINPUT_SOURCE_JOYSTICK)
  264. {
  265. if(action_type==AMOTION_EVENT_ACTION_MOVE
  266. #if __ANDROID_API__<14
  267. && getRawAxisValue
  268. #endif
  269. )
  270. {
  271. Joypad &joy=Joypads(action_index); if(!joy._name.is()){joy._name=JavaInputDeviceName(device); if(!joy._name.is())joy._name="JoyPad";}
  272. #if __ANDROID_API__>=14
  273. joy.dir .set(AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, action_index),
  274. -AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, action_index));
  275. joy.dir_a[0].set(AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X , action_index),
  276. -AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y , action_index));
  277. joy.dir_a[1].set(AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RX , action_index),
  278. -AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RY , action_index));
  279. #elif X64
  280. joy.dir .set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_HAT_X), jint(action_index), jint(HISTORY_CURRENT)),
  281. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_HAT_Y), jint(action_index), jint(HISTORY_CURRENT)));
  282. joy.dir_a[0].set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_X ), jint(action_index), jint(HISTORY_CURRENT)),
  283. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_Y ), jint(action_index), jint(HISTORY_CURRENT)));
  284. joy.dir_a[1].set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_RX ), jint(action_index), jint(HISTORY_CURRENT)),
  285. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_RY ), jint(action_index), jint(HISTORY_CURRENT)));
  286. #else
  287. joy.dir .set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_HAT_X), jint(action_index), jint(HISTORY_CURRENT)),
  288. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_HAT_Y), jint(action_index), jint(HISTORY_CURRENT)));
  289. joy.dir_a[0].set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_X ), jint(action_index), jint(HISTORY_CURRENT)),
  290. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_Y ), jint(action_index), jint(HISTORY_CURRENT)));
  291. joy.dir_a[1].set(Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_RX ), jint(action_index), jint(HISTORY_CURRENT)),
  292. -Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_RY ), jint(action_index), jint(HISTORY_CURRENT)));
  293. #endif
  294. }
  295. }else
  296. if((source&AINPUT_SOURCE_MOUSE) && !stylus) // check for stylus because on "Samsung Galaxy Note 2" stylus input generates both "AINPUT_SOURCE_STYLUS|AINPUT_SOURCE_MOUSE" at the same time
  297. {
  298. if(action_type==AMOTION_EVENT_ACTION_SCROLL
  299. #if __ANDROID_API__<14
  300. && getRawAxisValue
  301. #endif
  302. )
  303. {
  304. #if __ANDROID_API__>=14
  305. Ms._wheel.x+=AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HSCROLL, action_index);
  306. Ms._wheel.y+=AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_VSCROLL, action_index);
  307. #elif X64
  308. Ms._wheel.x+=Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_HSCROLL), jint(action_index), jint(HISTORY_CURRENT));
  309. Ms._wheel.y+=Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jlong(event), jint(AMOTION_EVENT_AXIS_VSCROLL), jint(action_index), jint(HISTORY_CURRENT));
  310. #else
  311. Ms._wheel.x+=Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_HSCROLL), jint(action_index), jint(HISTORY_CURRENT));
  312. Ms._wheel.y+=Jni->CallStaticFloatMethod(MotionEventClass, getRawAxisValue, jint (event), jint(AMOTION_EVENT_AXIS_VSCROLL), jint(action_index), jint(HISTORY_CURRENT));
  313. #endif
  314. }
  315. // get button states
  316. Int button_state=0;
  317. if( // if we have access to button states
  318. #if __ANDROID_API__>=14
  319. true
  320. #else
  321. getButtonState
  322. #endif
  323. )
  324. {
  325. #if __ANDROID_API__>=14
  326. button_state=AMotionEvent_getButtonState(event);
  327. #elif X64
  328. button_state=Jni->CallStaticIntMethod(MotionEventClass, getButtonState, jlong(event));
  329. #else
  330. button_state=Jni->CallStaticIntMethod(MotionEventClass, getButtonState, jint (event));
  331. #endif
  332. if(action_type==AMOTION_EVENT_ACTION_DOWN && !button_state){PossibleTap=true; PossibleTapTime=Time.appTime();}else // 'getButtonState' does not detect tapping on the touchpad, so we need to detect it according to 'AMOTION_EVENT_ACTION_DOWN', also proceed only if no buttons are pressed in case this event is triggered by secondary mouse button
  333. if(PossibleTap && (button_state || (LastMousePos-Ms.desktopPos()).abs().max()>=6))PossibleTap=false; // if we've pressed a button or moved away too much then it's definitely not a tap
  334. if(action_type==AMOTION_EVENT_ACTION_UP && PossibleTap ){PossibleTap=false; if(Time.appTime()<=PossibleTapTime+0.33f+Time.ad())Ms.push(0, 0.33f);} // this is a tap so push the button and it will be released line below because 'button_state' is 0, use 0.33f time limit because on Asus Transformer Prime tapping can result in times as long as 0.318s
  335. REPA(Ms._button)if(FlagTest(button_state, 1<<i)!=Ms.b(i))if(Ms.b(i))Ms.release(i);else Ms.push(i);
  336. }else // if not then rely on actions
  337. switch(action_type)
  338. {
  339. case AMOTION_EVENT_ACTION_DOWN: Ms.push(0); break;
  340. case AMOTION_EVENT_ACTION_UP :
  341. case AMOTION_EVENT_ACTION_CANCEL: Ms.release(0); break;
  342. case AMOTION_EVENT_ACTION_POINTER_DOWN: Ms.push(0); break;
  343. case AMOTION_EVENT_ACTION_POINTER_UP: Ms.release(0); break;
  344. }
  345. // get scrolling and cursor position
  346. if(action_type!=AMOTION_EVENT_ACTION_UP // this can happen on release of TouchPad scroll, where the position is still at the dragged position
  347. && AMotionEvent_getPointerCount(event)>=1)
  348. {
  349. VecI2 pos(Round(AMotionEvent_getRawX(event, 0)),
  350. Round(AMotionEvent_getRawY(event, 0)));
  351. if(action_type==AMOTION_EVENT_ACTION_MOVE // if we're dragging
  352. // we have access to button states
  353. #if __ANDROID_API__<14
  354. && getButtonState
  355. #endif
  356. && !button_state) // none of them are pressed
  357. { // this is TouchPad scroll (with 2 fingers on the TouchPad)
  358. const Flt mul=16.0f/D.screen().min(); // trigger 16 full mouse wheel events when moving mouse from one side to another (this value was set to match results on a Windows laptop, use 'min' or 'max' to get the same results even when device got rotated and that would cause resolution to change)
  359. Ms._wheel.x-=(pos.x-LastMousePos.x)*mul;
  360. Ms._wheel.y+=(pos.y-LastMousePos.y)*mul;
  361. }else
  362. {
  363. Ms._desktop_posi=pos;
  364. Ms. _window_posi.x=Round(AMotionEvent_getX(event, 0));
  365. Ms. _window_posi.y=Round(AMotionEvent_getY(event, 0));
  366. }
  367. LastMousePos=pos;
  368. }
  369. }else
  370. {
  371. switch(action_type)
  372. {
  373. case AMOTION_EVENT_ACTION_HOVER_ENTER: // touch is not pressed but appeared
  374. case AMOTION_EVENT_ACTION_DOWN : // touch was pressed (1st pointer)
  375. {
  376. REP(AMotionEvent_getPointerCount(event))
  377. {
  378. CPtr pid =(CPtr)AMotionEvent_getPointerId(event, i);
  379. Vec2 pixel(AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)),
  380. pos =D.windowPixelToScreen(pixel);
  381. VecI2 posi =Round(pixel);
  382. Touch *touch=FindTouchByHandle(pid);
  383. if( !touch)touch=&Touches.New().init(posi, pos, pid, stylus);else
  384. {
  385. touch->_remove=false; // disable 'remove' in case it was enabled (for example the same touch was released in same/previous frame)
  386. if(action_type!=AMOTION_EVENT_ACTION_HOVER_ENTER)touch->reinit(posi, pos); // re-initialize for push (don't do this for hover because it can be called the same frame that release is called, and for release we want to keep the original values)
  387. }
  388. if(action_type!=AMOTION_EVENT_ACTION_HOVER_ENTER){touch->_state=BS_ON|BS_PUSHED; touch->_force=1;}
  389. }
  390. }break;
  391. case AMOTION_EVENT_ACTION_POINTER_DOWN: // touch was pressed (secondary pointer)
  392. {
  393. CPtr pid =(CPtr)AMotionEvent_getPointerId(event, action_index);
  394. Vec2 pixel(AMotionEvent_getX(event, action_index), AMotionEvent_getY(event, action_index)),
  395. pos =D.windowPixelToScreen(pixel);
  396. VecI2 posi =Round(pixel);
  397. Touch *touch=FindTouchByHandle(pid);
  398. if( !touch)touch=&Touches.New().init(posi, pos, pid, stylus);else
  399. {
  400. // re-initialize for push
  401. touch->_remove=false; // disable 'remove' in case it was enabled (for example the same touch was released in same/previous frame)
  402. touch-> reinit(posi, pos);
  403. }
  404. touch->_state=BS_ON|BS_PUSHED;
  405. touch->_force=1;
  406. }break;
  407. case AMOTION_EVENT_ACTION_MOVE: // touch is pressed and was moved
  408. case AMOTION_EVENT_ACTION_HOVER_MOVE: // touch is not pressed and was moved
  409. {
  410. REP(AMotionEvent_getPointerCount(event))
  411. {
  412. CPtr pid =(CPtr)AMotionEvent_getPointerId(event, i);
  413. Vec2 pixel(AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)),
  414. pos =D.windowPixelToScreen(pixel);
  415. VecI2 posi =Round(pixel);
  416. Touch *touch=FindTouchByHandle(pid);
  417. if( !touch)touch=&Touches.New().init(posi, pos, pid, stylus);else
  418. { // update
  419. touch->_deltai+=posi-touch->_posi;
  420. touch->_posi =posi;
  421. touch->_pos =pos ;
  422. if(!touch->_state)touch->_gui_obj=Gui.objAtPos(touch->pos()); // when hovering then also update gui object (check for 'state' because hover can be called the same frame that release is called, and for release we want to keep the original values)
  423. }
  424. }
  425. }break;
  426. case AMOTION_EVENT_ACTION_UP:
  427. case AMOTION_EVENT_ACTION_CANCEL: // release all touches
  428. {
  429. REPA(Touches)
  430. {
  431. Touch &touch=Touches[i];
  432. touch._remove=true;
  433. if(touch._state&BS_ON) // check for state in case it was manually eaten
  434. {
  435. touch._state|= BS_RELEASED;
  436. touch._state&=~BS_ON;
  437. }
  438. }
  439. }break;
  440. case AMOTION_EVENT_ACTION_POINTER_UP: // release one touch
  441. {
  442. CPtr pid=(CPtr)AMotionEvent_getPointerId(event, action_index);
  443. if(Touch *touch=FindTouchByHandle(pid))
  444. {
  445. Vec2 pixel(AMotionEvent_getX(event, action_index), AMotionEvent_getY(event, action_index));
  446. VecI2 posi =Round(pixel);
  447. touch->_deltai+=posi-touch->_posi;
  448. touch->_posi =posi;
  449. touch->_pos =D.windowPixelToScreen(pixel);
  450. touch->_remove =true;
  451. if(touch->_state&BS_ON) // check for state in case it was manually eaten
  452. {
  453. touch->_state|= BS_RELEASED;
  454. touch->_state&=~BS_ON;
  455. }
  456. }
  457. }break;
  458. case AMOTION_EVENT_ACTION_HOVER_EXIT: // hover exited screen
  459. {
  460. REP(AMotionEvent_getPointerCount(event))
  461. {
  462. CPtr pid=(CPtr)AMotionEvent_getPointerId(event, i);
  463. if(Touch *touch=FindTouchByHandle(pid))
  464. {
  465. if(touch->on()) // if was pressed then release it (so application can process release)
  466. {
  467. touch->_remove= true;
  468. touch->_state|= BS_RELEASED;
  469. touch->_state&=~BS_ON;
  470. }else // if not pressed then just remove it
  471. {
  472. Touches.removeData(touch, true);
  473. }
  474. }
  475. }
  476. }break;
  477. }
  478. }
  479. #if 0
  480. Int pc=AMotionEvent_getPointerCount(event);
  481. Str o=S+"event:"+TextHex((UInt)source, -1, 0, true)+", action:"+Int(action_type)+", action_index:"+Int(action_index)+", ptr_cnt:"+pc+", ptr_id:";
  482. FREP(pc)o+=S+CPtr(AMotionEvent_getPointerId(event, i))+" | ";
  483. o+=S+"Touches:"+Touches.elms()+" "; FREPA(Touches)o+=S+'('+Touches[i]._state+", "+Touches[i].pos()+')';
  484. LogN(o);
  485. #endif
  486. }return 1;
  487. case AINPUT_EVENT_TYPE_KEY:
  488. {
  489. Int code =AKeyEvent_getKeyCode (event),
  490. meta =AKeyEvent_getMetaState(event);
  491. Bool ctrl =FlagTest(meta, (Int)AMETA_CTRL_ON ),
  492. shift=FlagTest(meta, (Int)AMETA_SHIFT_ON ),
  493. alt =FlagTest(meta, (Int)AMETA_ALT_ON ),
  494. caps =FlagTest(meta, (Int)AMETA_CAPS_LOCK_ON);
  495. Byte bcode=Byte(code);
  496. KB_KEY key =KeyMap[bcode];
  497. Byte joy =JoyMap[bcode];
  498. /*if(shift && !ctrl && !alt && !Kb.anyWin())
  499. {
  500. if(key==KB_BACK ){key=KB_DEL; Kb._disable_shift=true;}else // Shift+Back = Del (this is universal behaviour on Android platform)
  501. if(key==KB_ENTER){key=KB_INS; Kb._disable_shift=true;} // Shift+Enter = Ins (this is non-universal behaviour on Android platform)
  502. }*/
  503. Joypad *joypad=null; if(joy){Bool empty=!InRange(0, Joypads); joypad=&Joypads(0); if(empty)joypad->_name=JavaInputDeviceName(device); joy--;}
  504. //LogN(S+"dev:"+device+", code:"+code+", meta:"+meta+", key:"+key+", action:"+(int)AKeyEvent_getAction(event));
  505. //if(!key)LogN(S+"key:"+code);
  506. // check for characters here still because 'dispatchKeyEvent' does not catch keys from hardware keyboards (like tablet with attachable keyboard)
  507. Char chr='\0';
  508. if(KeySource!=KEY_JAVA)
  509. {
  510. // get 'KeyCharacterMap' for this device
  511. if(!joypad)
  512. if(!KeyboardLoaded || KeyboardDeviceID!=device)
  513. {
  514. KeyboardLoaded =true;
  515. KeyboardDeviceID=device;
  516. if(KeyCharacterMapGet)
  517. {
  518. KeyCharacterMap=Jni->CallStaticObjectMethod(KeyCharacterMapClass, KeyCharacterMapLoad, jint(KeyboardDeviceID));
  519. if(Jni->ExceptionCheck()){KeyCharacterMap.clear(); Jni->ExceptionClear();}else KeyCharacterMap.makeGlobal();
  520. }
  521. }
  522. if(KeyCharacterMap)
  523. {
  524. FlagDisable(meta, Int(AMETA_CTRL_ON|AMETA_CTRL_LEFT_ON|AMETA_CTRL_RIGHT_ON)); // disable CTRL in meta because if it would be present then 'chr' would be zero, we can't remove LALT yet because Swiftkey Android 2.3 relies on it
  525. chr=Jni->CallIntMethod(KeyCharacterMap, KeyCharacterMapGet, jint(code), jint(meta));
  526. if(!chr && (meta&AMETA_ALT_LEFT_ON))
  527. {
  528. FlagDisable(meta, (Int)AMETA_ALT_LEFT_ON);
  529. FlagSet(meta, (Int)AMETA_ALT_ON, FlagTest(meta, (Int)AMETA_ALT_RIGHT_ON)); // setup correct ALT mask
  530. chr=Jni->CallIntMethod(KeyCharacterMap, KeyCharacterMapGet, jint(code), jint(meta));
  531. }
  532. }else
  533. if(!Kb.b(KB_RALT))chr=AdjustByShift(Kb._key_char[key], shift, caps);
  534. if(chr)KeySource=KEY_CPP; // if detected and character then set CPP source
  535. }
  536. switch(AKeyEvent_getAction(event))
  537. {
  538. case AKEY_EVENT_ACTION_DOWN:
  539. {
  540. if(joypad)joypad->push(joy);
  541. Kb .push(key, code); Kb.queue(chr, code); // !! queue characters after push !!
  542. }break;
  543. case AKEY_EVENT_ACTION_UP:
  544. {
  545. if(joypad)joypad->release(joy);
  546. Kb .release(key);
  547. }break;
  548. case AKEY_EVENT_ACTION_MULTIPLE:
  549. {
  550. if(joypad)
  551. {
  552. joypad->push (joy);
  553. joypad->release(joy);
  554. }
  555. Kb.push (key, code); Kb.queue(chr, code); // !! queue characters after push !!
  556. Kb.release(key );
  557. }break;
  558. }
  559. // on some systems 'dispatchKeyEvent' will not be called if we return 1
  560. if(chr // if we did receive a character then we don't need 'dispatchKeyEvent' anymore
  561. || joypad // no point in forwarding joy buttons further
  562. || key==KB_NAV_BACK // on Asus Transformer Prime pressing this hardware keyboard key will close the app
  563. )return 1;
  564. }return 0; // call 'dispatchKeyEvent'
  565. default:
  566. {
  567. /*Int device=AInputEvent_getDeviceId(event);
  568. LogN(S+"Unknown input type, dev:"+device);*/
  569. }break;
  570. }
  571. return 0;
  572. }
  573. /******************************************************************************/
  574. enum ANDROID_STATE
  575. {
  576. AS_FOCUSED=1<<0,
  577. AS_STOPPED=1<<1,
  578. AS_PAUSED =1<<2,
  579. };
  580. static Byte AndroidState=0;
  581. static void SetActive()
  582. {
  583. Bool active=((AndroidState&(AS_FOCUSED|AS_STOPPED|AS_PAUSED))==AS_FOCUSED);
  584. App._maximized=((AndroidState&( AS_STOPPED|AS_PAUSED))== 0);
  585. App._minimized=!App._maximized;
  586. if(App.active()!=active)
  587. {
  588. if(active)
  589. {
  590. if(Jni && ActivityClass)
  591. if(JMethodID stopBackgroundService=Jni->GetMethodID(ActivityClass, "stopBackgroundService", "()V"))
  592. Jni->CallVoidMethod(Activity, stopBackgroundService);
  593. LOG2("ResumeSound");
  594. ResumeSound();
  595. LOG2("Kb.refreshTextInput");
  596. Kb.refreshTextInput();
  597. }
  598. LOG(S+"App.setActive("+active+")");
  599. App.setActive(active);
  600. if(!active)
  601. {
  602. if(App.flag&APP_WORK_IN_BACKGROUND)
  603. {
  604. if(Jni && ActivityClass)
  605. if(JMethodID startBackgroundService=Jni->GetMethodID(ActivityClass, "startBackgroundService", "(Ljava/lang/String;)V"))
  606. if(JString j_text=JString(Jni, App.backgroundText()))
  607. Jni->CallVoidMethod(Activity, startBackgroundService, j_text());
  608. }else
  609. {
  610. LOG2("PauseSound");
  611. PauseSound();
  612. }
  613. }
  614. LOG2("SetActive finished");
  615. }
  616. }
  617. static void CmdCallback(android_app *app, int32_t cmd)
  618. {
  619. switch(cmd)
  620. {
  621. default: LOG(S+"CmdCallback ("+cmd+")"); break;
  622. case APP_CMD_INIT_WINDOW:
  623. {
  624. LOG("APP_CMD_INIT_WINDOW");
  625. if(app->window)
  626. {
  627. if(!Initialized)
  628. {
  629. Initialized=true;
  630. if(!App.create())Exit("Failed to initialize the application"); // something failed, and current state of global variables is in failed mode (they won't be restored at next app launch because Android does not reset the state of global variables) that's why need to do complete reset by killing the process
  631. }else
  632. {
  633. D.androidOpen();
  634. }
  635. }
  636. }break;
  637. case APP_CMD_TERM_WINDOW:
  638. {
  639. LOG("APP_CMD_TERM_WINDOW");
  640. D.androidClose();
  641. }break;
  642. case APP_CMD_CONFIG_CHANGED:
  643. {
  644. LOG("APP_CMD_CONFIG_CHANGED");
  645. Kb.refreshTextInput();
  646. /*EGLint w=-1, h=-1;
  647. eglQuerySurface(display, surface, EGL_WIDTH , &w);
  648. eglQuerySurface(display, surface, EGL_HEIGHT, &h);
  649. LOG(S+"EGL: w:"+w+", h:"+h);*/
  650. UpdateOrientation();
  651. //if(app->window)UpdateSize(*app->window); // at this stage, old window size may occur, instead we need to check this every frame
  652. }break;
  653. case APP_CMD_WINDOW_REDRAW_NEEDED:
  654. {
  655. LOG("APP_CMD_WINDOW_REDRAW_NEEDED");
  656. if(D.created())DrawState();
  657. }break;
  658. case APP_CMD_GAINED_FOCUS: LOG("APP_CMD_GAINED_FOCUS"); FlagSet(AndroidState, AS_FOCUSED, true ); SetActive(); break;
  659. case APP_CMD_LOST_FOCUS : LOG("APP_CMD_LOST_FOCUS" ); FlagSet(AndroidState, AS_FOCUSED, false); SetActive(); break;
  660. case APP_CMD_START : LOG("APP_CMD_START" ); FlagSet(AndroidState, AS_STOPPED, false); SetActive(); break;
  661. case APP_CMD_STOP : LOG("APP_CMD_STOP" ); FlagSet(AndroidState, AS_STOPPED, true ); SetActive(); break;
  662. case APP_CMD_PAUSE : LOG("APP_CMD_PAUSE" ); FlagSet(AndroidState, AS_PAUSED , true ); SetActive(); break;
  663. case APP_CMD_RESUME : LOG("APP_CMD_RESUME" ); FlagSet(AndroidState, AS_PAUSED , false); SetActive(); break;
  664. case APP_CMD_DESTROY : LOG("APP_CMD_DESTROY" ); break;
  665. case APP_CMD_SAVE_STATE : LOG("APP_CMD_SAVE_STATE" ); if(App.save_state)App.save_state(); break;
  666. case APP_CMD_WINDOW_RESIZED : LOG("APP_CMD_WINDOW_RESIZED" ); break;
  667. case APP_CMD_CONTENT_RECT_CHANGED: LOG("APP_CMD_CONTENT_RECT_CHANGED"); break;
  668. case APP_CMD_LOW_MEMORY : LOG("APP_CMD_LOW_MEMORY" ); App.lowMemory(); break;
  669. }
  670. }
  671. /******************************************************************************/
  672. static void JavaInit()
  673. {
  674. AndroidApp->onAppCmd = CmdCallback;
  675. AndroidApp->onInputEvent=InputCallback;
  676. //AndroidApp->activity->callbacks->onNativeWindowResized=WindowResized; // this is never called
  677. JVM =AndroidApp->activity->vm;
  678. Activity =AndroidApp->activity->clazz; // this is not stored as 'JObject' so we can always assign it
  679. AndroidSDK =AndroidApp->activity->sdkVersion;
  680. AssetManager=AndroidApp->activity->assetManager;
  681. LOG(S);
  682. LOG(S+"Start - Already Initialized: "+Initialized+", JVM: "+JVM+", JNI:"+Ptr(Jni)+", SDK:"+AndroidSDK);
  683. Jni.attach();
  684. if(Jni && Activity)if(ActivityClass=Activity)ActivityClass.makeGlobal();
  685. }
  686. static void JavaShut()
  687. {
  688. LOG("JavaShut");
  689. KeyCharacterMapClass.del();
  690. KeyCharacterMap .del();
  691. KeyCharacterMapLoad =null;
  692. KeyCharacterMapGet =null;
  693. InputDeviceClass .del();
  694. InputDeviceGetName =null;
  695. InputDeviceGetDevice=null;
  696. DefaultDisplay .del();
  697. Activity =null;
  698. getRotation =null;
  699. #if 0 // we need these even after 'JavaShut'
  700. ActivityClass .del();
  701. ClipboardManagerClass.del();
  702. ClipboardManager .del();
  703. REPAO(EsenthelLocationListener).del();
  704. LocationManager .del();
  705. GPS_PROVIDER.del(); NETWORK_PROVIDER.del();
  706. getLastKnownLocation=getLatitude=getLongitude=getAltitude=getAccuracy=getSpeed=getTime=requestLocationUpdates=removeUpdates=null;
  707. #endif
  708. #if __ANDROID_API__<14
  709. getButtonState=getRawAxisValue=null;
  710. MotionEventClass.del();
  711. #endif
  712. Jni.del();
  713. }
  714. static void JavaGetAppPackage()
  715. {
  716. LOG("JavaGetAppPackage");
  717. if(!AndroidPackageName.is() && ActivityClass && Activity)
  718. if(JMethodID getPackageName=Jni->GetMethodID(ActivityClass, "getPackageName", "()Ljava/lang/String;"))
  719. if(JString package_name=Jni->CallObjectMethod(Activity, getPackageName))
  720. AndroidPackageName=package_name.str();
  721. }
  722. static void JavaGetAppName()
  723. {
  724. LOG("JavaGetAppName");
  725. //JClass Context="android/content/Context")
  726. //JMethodID getApplicationContext=Jni->GetMethodID(Context, "getApplicationContext", "()Landroid/content/Context;");
  727. //JMethodID getPackageName=Jni->GetMethodID(Context, "getPackageName", "()Ljava/lang/String;");
  728. //JMethodID getApplicationContext=Jni->GetStaticMethodID(Context, "getApplicationContext", "()Landroid/content/Context;");
  729. //JMethodID getPackageName=Jni->GetStaticMethodID(Context, "getPackageName", "()Ljava/lang/String;");
  730. if(Jni && ActivityClass)
  731. if(JClass FileClass="java/io/File")
  732. if(JMethodID getAbsolutePath=Jni->GetMethodID(FileClass, "getAbsolutePath", "()Ljava/lang/String;"))
  733. {
  734. // code and assets
  735. if(JMethodID getPackageCodePath=Jni->GetMethodID (ActivityClass, "getPackageCodePath", "()Ljava/lang/String;"))
  736. if(JString code_path=Jni->CallObjectMethod(Activity , getPackageCodePath))
  737. AndroidAppPath= code_path.str().replace('/', '\\');
  738. // resources
  739. //if(JMethodID getPackageResourcePath=Jni->GetMethodID (ActivityClass, "getPackageResourcePath", "()Ljava/lang/String;"))
  740. //if(JString resource_path=Jni->CallObjectMethod(Activity , getPackageResourcePath)){}
  741. // app data private
  742. if(JMethodID getFilesDir =Jni->GetMethodID (ActivityClass, "getFilesDir", "()Ljava/io/File;"))
  743. if(JObject files_dir =Jni->CallObjectMethod(Activity , getFilesDir))
  744. if(JString files_dir_str=Jni->CallObjectMethod(files_dir , getAbsolutePath))
  745. AndroidAppDataPath=files_dir_str.str().replace('/', '\\');
  746. // app data public
  747. if(JMethodID getExternalFilesDir =Jni->GetMethodID (ActivityClass , "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"))
  748. if(JObject external_files_dir =Jni->CallObjectMethod(Activity , getExternalFilesDir, null))
  749. if(JString external_files_dir_str=Jni->CallObjectMethod(external_files_dir, getAbsolutePath))
  750. AndroidAppDataPublicPath=external_files_dir_str.str().replace('/', '\\');
  751. // public
  752. if(JClass Environment="android/os/Environment")
  753. if(JMethodID getExternalStorageDirectory =Jni-> GetStaticMethodID (Environment, "getExternalStorageDirectory", "()Ljava/io/File;"))
  754. if(JObject externalStorageDirectory =Jni->CallStaticObjectMethod(Environment, getExternalStorageDirectory))
  755. if(JString externalStorageDirectory_str=Jni->CallObjectMethod (externalStorageDirectory, getAbsolutePath))
  756. AndroidPublicPath=externalStorageDirectory_str.str().replace('/', '\\');
  757. // SD Card
  758. // this method gets array of files using 'getExternalFilesDirs', and ignores 'AndroidAppDataPublicPath' obtained with 'getExternalFilesDir', then it removes the shared end like "external/Android/App", "sd_card/Android/App" to leave only "sd_card"
  759. if(JMethodID getExternalFilesDirs=Jni->GetMethodID (ActivityClass, "getExternalFilesDirs", "(Ljava/lang/String;)[Ljava/io/File;"))
  760. if(JObjectArray dirs=Jni->CallObjectMethod(Activity, getExternalFilesDirs, null))
  761. {
  762. Int length=dirs.elms();
  763. FREP(length)
  764. if(JObject dir=dirs[i]) // check this because it can be null
  765. if(JString dir_path=Jni->CallObjectMethod(dir, getAbsolutePath))
  766. {
  767. Str path=dir_path.str(); if(path.is() && !EqualPath(path, AndroidAppDataPublicPath))
  768. {
  769. path.replace('/', '\\');
  770. Int same=0;
  771. FREPA(path)
  772. {
  773. Char a=path[path.length()-1-i], b=AndroidAppDataPublicPath[AndroidAppDataPublicPath.length()-1-i]; // last chars
  774. if(IsSlash(a) && IsSlash(b))same=i+1;else if(a!=b)break;
  775. }
  776. path.removeLast(same);
  777. AndroidSDCardPath=path;
  778. break;
  779. }
  780. }
  781. }
  782. if(Jni->ExceptionCheck())Jni->ExceptionClear(); // clear in case 'getExternalFilesDirs' was not found
  783. }
  784. if(LogInit && AndroidPublicPath.is() && !EqualPath(AndroidPublicPath, GetPath(LogName()))){Str temp=LogName(); LogN(S+"Found public storage directory and continuing log there: "+AndroidPublicPath); LogName(Str(AndroidPublicPath).tailSlash(true)+"Esenthel Log.txt"); LogN(S+"Continuing log from: "+temp);}
  785. }
  786. static void JavaLooper()
  787. {
  788. // prepare the Looper, this is needed for 'ClipboardManager' and 'LocationListener'
  789. LOG("JavaLooper");
  790. if(Jni)
  791. if(JClass LooperClass="android/os/Looper")
  792. if(JMethodID prepare=Jni->GetStaticMethodID(LooperClass, "prepare", "()V"))
  793. {
  794. Jni->CallStaticVoidMethod(LooperClass, prepare);
  795. if(Jni->ExceptionCheck())
  796. {
  797. #if DEBUG
  798. Jni->ExceptionDescribe();
  799. #endif
  800. Jni->ExceptionClear();
  801. }
  802. }
  803. }
  804. static void JavaGetInput()
  805. {
  806. LOG("JavaGetInput");
  807. #if __ANDROID_API__<14
  808. if(!MotionEventClass)
  809. if(Jni)
  810. if(MotionEventClass="android/view/MotionEvent")
  811. {
  812. getButtonState =Jni->GetStaticMethodID(MotionEventClass, "nativeGetButtonState" , X64 ? "(J)I" : "(I)I" ); if(Jni->ExceptionCheck()){getButtonState =null; Jni->ExceptionClear();} // Java exception can occur when methods not found
  813. getRawAxisValue=Jni->GetStaticMethodID(MotionEventClass, "nativeGetRawAxisValue", X64 ? "(JIII)F" : "(IIII)F"); if(Jni->ExceptionCheck()){getRawAxisValue=null; Jni->ExceptionClear();} // Java exception can occur when methods not found
  814. }
  815. #endif
  816. if(!InputDeviceClass)
  817. if(Jni)
  818. if(InputDeviceClass="android/view/InputDevice")
  819. {
  820. InputDeviceGetDevice=Jni->GetStaticMethodID(InputDeviceClass, "getDevice", "(I)Landroid/view/InputDevice;"); if(Jni->ExceptionCheck()){InputDeviceGetDevice=null; Jni->ExceptionClear();} // Java exception can occur when methods not found
  821. InputDeviceGetName =Jni->GetMethodID (InputDeviceClass, "getName" , "()Ljava/lang/String;" ); if(Jni->ExceptionCheck()){InputDeviceGetName =null; Jni->ExceptionClear();} // Java exception can occur when methods not found
  822. }
  823. }
  824. static void JavaGetScreenSize()
  825. {
  826. LOG("JavaGetScreenSize");
  827. if(!DefaultDisplay)
  828. if(Jni && ActivityClass)
  829. {
  830. // DisplayMetrics display_metrics=new DisplayMetrics();
  831. // getWindowManager().getDefaultDisplay().getMetrics(display_metrics);
  832. // w=display_metrics.widthPixels;
  833. // h=display_metrics.heightPixels;
  834. // DisplayMetrics display_metrics=new DisplayMetrics();
  835. //if(JClass DisplayMetricsClass="android/util/DisplayMetrics")
  836. //if(JMethodID DisplayMetricsCtor=Jni->GetMethodID(DisplayMetricsClass, "<init>", "()V"))
  837. //if(JObject display_metrics=Jni->NewObject(DisplayMetricsClass, DisplayMetricsCtor))
  838. // Get WindowManager
  839. if(JClass WindowManagerClass="android/view/WindowManager")
  840. if(JMethodID getWindowManager=Jni->GetMethodID(ActivityClass, "getWindowManager", "()Landroid/view/WindowManager;"))
  841. if(JObject window_manager=Jni->CallObjectMethod(Activity, getWindowManager))
  842. {
  843. // Get DefaultDisplay
  844. if(JClass Display="android/view/Display")
  845. if(JMethodID getDefaultDisplay=Jni->GetMethodID(WindowManagerClass, "getDefaultDisplay", "()Landroid/view/Display;"))
  846. if(DefaultDisplay=Jni->CallObjectMethod(window_manager, getDefaultDisplay))if(DefaultDisplay.makeGlobal())
  847. {
  848. // Get Display methods
  849. getRotation=Jni->GetMethodID(Display, "getRotation", "()I"); // set this as the last one so only one 'if' can be performed in 'UpdateOrientation'
  850. JMethodID getWidth =Jni->GetMethodID(Display, "getWidth" , "()I");
  851. JMethodID getHeight =Jni->GetMethodID(Display, "getHeight" , "()I");
  852. //JMethodID getMetrics =Jni->GetMethodID(Display, "getMetrics" , "(Landroid/util/DisplayMetrics;)V");
  853. Int rotation=(getRotation ? Jni->CallIntMethod(DefaultDisplay, getRotation) : ROTATION_0),
  854. width =(getWidth ? Jni->CallIntMethod(DefaultDisplay, getWidth ) : 800 ),
  855. height =(getHeight ? Jni->CallIntMethod(DefaultDisplay, getHeight ) : 600 );
  856. if(rotation==ROTATION_90 || rotation==ROTATION_270)Swap(width, height);
  857. App._desktop_size.set(width, height);
  858. }
  859. }
  860. }
  861. UpdateOrientation();
  862. }
  863. static void JavaKeyboard()
  864. {
  865. LOG("JavaKeyboard");
  866. if(!KeyCharacterMapGet)
  867. if(Jni)
  868. if(KeyCharacterMapClass="android/view/KeyCharacterMap")
  869. if(KeyCharacterMapLoad=Jni->GetStaticMethodID(KeyCharacterMapClass, "load", "(I)Landroid/view/KeyCharacterMap;"))
  870. KeyCharacterMapGet=Jni->GetMethodID(KeyCharacterMapClass, "get", "(II)I");
  871. }
  872. static void JavaClipboard()
  873. {
  874. LOG("JavaClipboard");
  875. if(!ClipboardManager)
  876. if(Jni && ActivityClass)
  877. if(JClass ContextClass="android/content/Context")
  878. if(JFieldID CLIPBOARD_SERVICEField=Jni->GetStaticFieldID(ContextClass, "CLIPBOARD_SERVICE", "Ljava/lang/String;"))
  879. if(JObject CLIPBOARD_SERVICE=Jni->GetStaticObjectField(ContextClass, CLIPBOARD_SERVICEField))
  880. if(ClipboardManagerClass="android/text/ClipboardManager")if(ClipboardManagerClass.makeGlobal()) // android/content/ClipboardManager
  881. if(JMethodID getSystemService=Jni->GetMethodID(ActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"))
  882. {
  883. ClipboardManager=Jni->CallObjectMethod(Activity, getSystemService, CLIPBOARD_SERVICE());
  884. if(Jni->ExceptionCheck())
  885. {
  886. #if DEBUG
  887. Jni->ExceptionDescribe();
  888. #endif
  889. ClipboardManager.clear(); ClipboardManagerClass.clear(); Jni->ExceptionClear(); LOG("ClipboardManager failed");
  890. }else ClipboardManager.makeGlobal();
  891. }
  892. }
  893. static void JavaLocation()
  894. {
  895. LOG("JavaLocation");
  896. if(!LocationManager && Jni && ActivityClass)
  897. if(JMethodID getClassLoader =Jni->GetMethodID(ActivityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"))
  898. if(JObject ClassLoader =Jni->CallObjectMethod(Activity, getClassLoader))
  899. if(JClass ClassLoaderClass="java/lang/ClassLoader")
  900. if(JMethodID loadClass=Jni->GetMethodID(ClassLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"))
  901. if(JClass ContextClass="android/content/Context")
  902. if(JFieldID LOCATION_SERVICEField=Jni->GetStaticFieldID(ContextClass, "LOCATION_SERVICE", "Ljava/lang/String;"))
  903. if(JObject LOCATION_SERVICE=Jni->GetStaticObjectField(ContextClass, LOCATION_SERVICEField))
  904. if(JClass LocationManagerClass="android/location/LocationManager")
  905. if(JMethodID getSystemService=Jni->GetMethodID(ActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"))
  906. {
  907. LocationManager=Jni->CallObjectMethod(Activity, getSystemService, LOCATION_SERVICE());
  908. if(Jni->ExceptionCheck())
  909. {
  910. #if DEBUG
  911. Jni->ExceptionDescribe();
  912. #endif
  913. LocationManager.clear(); LocationManagerClass.clear(); Jni->ExceptionClear(); LOG("LocationManager failed");
  914. }else LocationManager.makeGlobal();
  915. if(LocationManager)
  916. if(JClass LocationClass="android/location/Location")
  917. if(JFieldID GPS_PROVIDERField=Jni->GetStaticFieldID(LocationManagerClass, "GPS_PROVIDER", "Ljava/lang/String;"))
  918. if(JFieldID NETWORK_PROVIDERField=Jni->GetStaticFieldID(LocationManagerClass, "NETWORK_PROVIDER", "Ljava/lang/String;"))
  919. if( GPS_PROVIDER =Jni->GetStaticObjectField(LocationManagerClass, GPS_PROVIDERField))if( GPS_PROVIDER.makeGlobal())
  920. if(NETWORK_PROVIDER =Jni->GetStaticObjectField(LocationManagerClass, NETWORK_PROVIDERField))if(NETWORK_PROVIDER.makeGlobal())
  921. if(getLastKnownLocation =Jni->GetMethodID(LocationManagerClass, "getLastKnownLocation" , "(Ljava/lang/String;)Landroid/location/Location;"))
  922. if(getLatitude =Jni->GetMethodID(LocationClass , "getLatitude" , "()D"))
  923. if(getLongitude =Jni->GetMethodID(LocationClass , "getLongitude" , "()D"))
  924. if(getAltitude =Jni->GetMethodID(LocationClass , "getAltitude" , "()D"))
  925. if(getAccuracy =Jni->GetMethodID(LocationClass , "getAccuracy" , "()F"))
  926. if(getSpeed =Jni->GetMethodID(LocationClass , "getSpeed" , "()F"))
  927. if(getTime =Jni->GetMethodID(LocationClass , "getTime" , "()J")) // set this as the last one so only one 'if' can be performed in 'UpdateLocation'
  928. if(requestLocationUpdates=Jni->GetMethodID(LocationManagerClass, "requestLocationUpdates", "(Ljava/lang/String;JFLandroid/location/LocationListener;)V"))
  929. if(removeUpdates =Jni->GetMethodID(LocationManagerClass, "removeUpdates" , "(Landroid/location/LocationListener;)V"))
  930. if(JString EsenthelLocationListenerName=AndroidPackageName+"/EsenthelActivity$EsenthelLocationListener")
  931. {
  932. JClass EsenthelLocationListenerClass=(jclass)Jni->CallObjectMethod(ClassLoader, loadClass, EsenthelLocationListenerName());
  933. if(Jni->ExceptionCheck()){EsenthelLocationListenerClass.clear(); Jni->ExceptionClear(); LOG("EsenthelLocationListenerClass failed");}
  934. if(EsenthelLocationListenerClass)
  935. if(JMethodID EsenthelLocationListenerCtor=Jni->GetMethodID(EsenthelLocationListenerClass, "<init>", "(Z)V"))
  936. REP(2)
  937. {
  938. EsenthelLocationListener[i]=Jni->NewObject(EsenthelLocationListenerClass, EsenthelLocationListenerCtor, jboolean(i)); // set this as the last one so only one 'if' can be performed in 'SetLocationRefresh'
  939. if(Jni->ExceptionCheck()){EsenthelLocationListener[i].clear(); Jni->ExceptionClear(); LOG(S+"EsenthelLocationListener["+i+"] failed");}else EsenthelLocationListener[i].makeGlobal();
  940. }
  941. }
  942. }
  943. if(Jni)Jni->ExceptionClear(); // 'ExceptionClear' must be called in case some func returned null
  944. UpdateLocation(Jni);
  945. }
  946. /******************************************************************************/
  947. static void InitSensor()
  948. {
  949. LOG("InitSensor");
  950. if(!SensorEventQueue)
  951. if(ASensorManager *sensor_manager=ASensorManager_getInstance())
  952. SensorEventQueue=ASensorManager_createEventQueue(sensor_manager, AndroidApp->looper, LOOPER_ID_USER, null, null);
  953. }
  954. static void ShutSensor()
  955. {
  956. LOG("ShutSensor");
  957. InputDevices.acquire(false); // disable all sensors just in case
  958. if(SensorEventQueue)
  959. {
  960. if(ASensorManager *sensor_manager=ASensorManager_getInstance())
  961. ASensorManager_destroyEventQueue(sensor_manager, SensorEventQueue);
  962. SensorEventQueue=null;
  963. }
  964. }
  965. /******************************************************************************/
  966. static void InitKeyMap()
  967. {
  968. KeyMap[AKEYCODE_0]=KB_0;
  969. KeyMap[AKEYCODE_1]=KB_1;
  970. KeyMap[AKEYCODE_2]=KB_2;
  971. KeyMap[AKEYCODE_3]=KB_3;
  972. KeyMap[AKEYCODE_4]=KB_4;
  973. KeyMap[AKEYCODE_5]=KB_5;
  974. KeyMap[AKEYCODE_6]=KB_6;
  975. KeyMap[AKEYCODE_7]=KB_7;
  976. KeyMap[AKEYCODE_8]=KB_8;
  977. KeyMap[AKEYCODE_9]=KB_9;
  978. KeyMap[AKEYCODE_A]=KB_A;
  979. KeyMap[AKEYCODE_B]=KB_B;
  980. KeyMap[AKEYCODE_C]=KB_C;
  981. KeyMap[AKEYCODE_D]=KB_D;
  982. KeyMap[AKEYCODE_E]=KB_E;
  983. KeyMap[AKEYCODE_F]=KB_F;
  984. KeyMap[AKEYCODE_G]=KB_G;
  985. KeyMap[AKEYCODE_H]=KB_H;
  986. KeyMap[AKEYCODE_I]=KB_I;
  987. KeyMap[AKEYCODE_J]=KB_J;
  988. KeyMap[AKEYCODE_K]=KB_K;
  989. KeyMap[AKEYCODE_L]=KB_L;
  990. KeyMap[AKEYCODE_M]=KB_M;
  991. KeyMap[AKEYCODE_N]=KB_N;
  992. KeyMap[AKEYCODE_O]=KB_O;
  993. KeyMap[AKEYCODE_P]=KB_P;
  994. KeyMap[AKEYCODE_Q]=KB_Q;
  995. KeyMap[AKEYCODE_R]=KB_R;
  996. KeyMap[AKEYCODE_S]=KB_S;
  997. KeyMap[AKEYCODE_T]=KB_T;
  998. KeyMap[AKEYCODE_U]=KB_U;
  999. KeyMap[AKEYCODE_V]=KB_V;
  1000. KeyMap[AKEYCODE_W]=KB_W;
  1001. KeyMap[AKEYCODE_X]=KB_X;
  1002. KeyMap[AKEYCODE_Y]=KB_Y;
  1003. KeyMap[AKEYCODE_Z]=KB_Z;
  1004. KeyMap[131]=KB_F1;
  1005. KeyMap[132]=KB_F2;
  1006. KeyMap[133]=KB_F3;
  1007. KeyMap[134]=KB_F4;
  1008. KeyMap[135]=KB_F5;
  1009. KeyMap[136]=KB_F6;
  1010. KeyMap[137]=KB_F7;
  1011. KeyMap[138]=KB_F8;
  1012. KeyMap[139]=KB_F9;
  1013. KeyMap[140]=KB_F10;
  1014. KeyMap[141]=KB_F11;
  1015. KeyMap[142]=KB_F12;
  1016. KeyMap[111 ]=KB_ESC;
  1017. KeyMap[AKEYCODE_ENTER]=KB_ENTER;
  1018. KeyMap[AKEYCODE_SPACE]=KB_SPACE;
  1019. KeyMap[AKEYCODE_DEL ]=KB_BACK;
  1020. KeyMap[AKEYCODE_TAB ]=KB_TAB;
  1021. KeyMap[113 ]=KB_LCTRL;
  1022. KeyMap[114 ]=KB_RCTRL;
  1023. KeyMap[AKEYCODE_SHIFT_LEFT ]=KB_LSHIFT;
  1024. KeyMap[AKEYCODE_SHIFT_RIGHT]=KB_RSHIFT;
  1025. KeyMap[AKEYCODE_ALT_LEFT ]=KB_LALT;
  1026. KeyMap[AKEYCODE_ALT_RIGHT ]=KB_RALT;
  1027. KeyMap[117 ]=KB_LWIN;
  1028. KeyMap[118 ]=KB_RWIN;
  1029. KeyMap[AKEYCODE_MENU ]=KB_MENU;
  1030. KeyMap[AKEYCODE_DPAD_UP ]=KB_UP;
  1031. KeyMap[AKEYCODE_DPAD_DOWN ]=KB_DOWN;
  1032. KeyMap[AKEYCODE_DPAD_LEFT ]=KB_LEFT;
  1033. KeyMap[AKEYCODE_DPAD_RIGHT]=KB_RIGHT;
  1034. KeyMap[124 ]=KB_INS;
  1035. KeyMap[112 ]=KB_DEL;
  1036. KeyMap[122 ]=KB_HOME;
  1037. KeyMap[123 ]=KB_END;
  1038. KeyMap[AKEYCODE_PAGE_UP ]=KB_PGUP;
  1039. KeyMap[AKEYCODE_PAGE_DOWN]=KB_PGDN;
  1040. KeyMap[AKEYCODE_MINUS ]=KB_SUB;
  1041. KeyMap[AKEYCODE_EQUALS ]=KB_EQUAL;
  1042. KeyMap[AKEYCODE_LEFT_BRACKET ]=KB_LBRACKET;
  1043. KeyMap[AKEYCODE_RIGHT_BRACKET]=KB_RBRACKET;
  1044. KeyMap[AKEYCODE_SEMICOLON ]=KB_SEMICOLON;
  1045. KeyMap[AKEYCODE_APOSTROPHE ]=KB_APOSTROPHE;
  1046. KeyMap[AKEYCODE_COMMA ]=KB_COMMA;
  1047. KeyMap[AKEYCODE_PERIOD ]=KB_DOT;
  1048. KeyMap[AKEYCODE_SLASH ]=KB_SLASH;
  1049. KeyMap[AKEYCODE_BACKSLASH ]=KB_BACKSLASH;
  1050. KeyMap[AKEYCODE_GRAVE ]=KB_TILDE;
  1051. KeyMap[120 ]=KB_PRINT;
  1052. KeyMap[121 ]=KB_PAUSE;
  1053. KeyMap[115 ]=KB_CAPS;
  1054. KeyMap[143 ]=KB_NUM;
  1055. KeyMap[116 ]=KB_SCROLL;
  1056. KeyMap[154]=KB_NPDIV;
  1057. KeyMap[155]=KB_NPMUL;
  1058. KeyMap[156]=KB_NPSUB;
  1059. KeyMap[157]=KB_NPADD;
  1060. KeyMap[158]=KB_NPDEL;
  1061. KeyMap[144]=KB_NP0;
  1062. KeyMap[145]=KB_NP1;
  1063. KeyMap[146]=KB_NP2;
  1064. KeyMap[147]=KB_NP3;
  1065. KeyMap[148]=KB_NP4;
  1066. KeyMap[149]=KB_NP5;
  1067. KeyMap[150]=KB_NP6;
  1068. KeyMap[151]=KB_NP7;
  1069. KeyMap[152]=KB_NP8;
  1070. KeyMap[153]=KB_NP9;
  1071. KeyMap[AKEYCODE_VOLUME_DOWN ]=KB_VOL_DOWN;
  1072. KeyMap[AKEYCODE_VOLUME_UP ]=KB_VOL_UP;
  1073. KeyMap[AKEYCODE_MUTE ]=KB_VOL_MUTE;
  1074. KeyMap[AKEYCODE_BACK ]=KB_NAV_BACK;
  1075. KeyMap[AKEYCODE_MEDIA_PREVIOUS ]=KB_MEDIA_PREV;
  1076. KeyMap[AKEYCODE_MEDIA_NEXT ]=KB_MEDIA_NEXT;
  1077. KeyMap[AKEYCODE_MEDIA_PLAY_PAUSE]=KB_MEDIA_PLAY;
  1078. KeyMap[AKEYCODE_MEDIA_STOP ]=KB_MEDIA_STOP;
  1079. KeyMap[AKEYCODE_SEARCH ]=KB_FIND;
  1080. KeyMap[168]=KB_ZOOM_IN ; // AKEYCODE_ZOOM_IN
  1081. KeyMap[169]=KB_ZOOM_OUT; // AKEYCODE_ZOOM_OUT
  1082. JoyMap[AKEYCODE_BUTTON_A ]=1;
  1083. JoyMap[AKEYCODE_BUTTON_B ]=2;
  1084. JoyMap[AKEYCODE_BUTTON_X ]=3;
  1085. JoyMap[AKEYCODE_BUTTON_Y ]=4;
  1086. JoyMap[AKEYCODE_BUTTON_L1 ]=5;
  1087. JoyMap[AKEYCODE_BUTTON_R1 ]=6;
  1088. JoyMap[AKEYCODE_BUTTON_L2 ]=7;
  1089. JoyMap[AKEYCODE_BUTTON_R2 ]=8;
  1090. JoyMap[AKEYCODE_BUTTON_SELECT]=9;
  1091. JoyMap[AKEYCODE_BUTTON_START ]=10;
  1092. JoyMap[AKEYCODE_BUTTON_THUMBL]=11;
  1093. JoyMap[AKEYCODE_BUTTON_THUMBR]=12;
  1094. REPD(shift, 2)
  1095. REPD(caps , 2)if(shift || caps)
  1096. {
  1097. Int m=((shift | (caps<<1))-1);
  1098. REPA(ShiftMap[m])ShiftMap[m][i]=((shift==caps) ? Char8(i) : CaseUp(Char8(i)));
  1099. if(shift)
  1100. {
  1101. ShiftMap[m][Byte('`')]='~';
  1102. ShiftMap[m][Byte('1')]='!';
  1103. ShiftMap[m][Byte('2')]='@';
  1104. ShiftMap[m][Byte('3')]='#';
  1105. ShiftMap[m][Byte('4')]='$';
  1106. ShiftMap[m][Byte('5')]='%';
  1107. ShiftMap[m][Byte('6')]='^';
  1108. ShiftMap[m][Byte('7')]='&';
  1109. ShiftMap[m][Byte('8')]='*';
  1110. ShiftMap[m][Byte('9')]='(';
  1111. ShiftMap[m][Byte('0')]=')';
  1112. ShiftMap[m][Byte('-')]='_';
  1113. ShiftMap[m][Byte('=')]='+';
  1114. ShiftMap[m][Byte('[')]='{';
  1115. ShiftMap[m][Byte(']')]='}';
  1116. ShiftMap[m][Byte(';')]=':';
  1117. ShiftMap[m][Byte('\'')]='"';
  1118. ShiftMap[m][Byte('\\')]='|';
  1119. ShiftMap[m][Byte(',')]='<';
  1120. ShiftMap[m][Byte('.')]='>';
  1121. ShiftMap[m][Byte('/')]='?';
  1122. }
  1123. }
  1124. }
  1125. /******************************************************************************/
  1126. } // namespace EE
  1127. /******************************************************************************/
  1128. extern "C"
  1129. {
  1130. JNIEXPORT void JNICALL Java_com_esenthel_Native_connected(JNIEnv *env, jclass clazz, jboolean supports_items, jboolean supports_subs);
  1131. JNIEXPORT void JNICALL Java_com_esenthel_Native_location (JNIEnv *env, jclass clazz, jboolean gps, jobject location) {JNI jni(env); UpdateLocation(location, gps!=0, jni);}
  1132. }
  1133. /******************************************************************************/
  1134. //jint JNI_OnLoad(JavaVM *vm, void *reserved) {LOG("JNI_OnLoad"); return JNI_VERSION_1_6;} don't use since it's called not before 'android_main' but during the first 'ALooper_pollAll'
  1135. void android_main(android_app *app)
  1136. {
  1137. // !! call before 'JavaInit' !!
  1138. if(BackgroundThread.created()) // stop thread if running
  1139. {
  1140. BackgroundThread.stop(); // request thread to be stopped
  1141. BackgroundThread._resume.on(); // wake up, Warning: here we reuse its 'SyncEvent' because it's used only for pausing/resuming
  1142. BackgroundThread.del(); // wait and delete
  1143. }
  1144. // strip prevention
  1145. Java_com_esenthel_Native_connected(null, null, Store.supportsItems(), Store.supportsSubscriptions()); // don't remove this, or the linker will strip all Java Native functions
  1146. // init
  1147. if(LogInit)
  1148. {
  1149. if(FExistSystem("/sdcard" ))LogName( "/sdcard/Esenthel Log.txt");else
  1150. if(FExistSystem("/mnt/sdcard" ))LogName( "/mnt/sdcard/Esenthel Log.txt");else
  1151. if(FExistSystem("/mnt/sdcard-ext"))LogName("/mnt/sdcard-ext/Esenthel Log.txt");
  1152. }
  1153. AndroidApp=app;
  1154. JavaInit();
  1155. if(!Initialized) // these can be called only once
  1156. {
  1157. InitKeyMap ();
  1158. JavaGetAppPackage();
  1159. JavaGetAppName ();
  1160. }
  1161. JavaLooper ();
  1162. JavaGetInput ();
  1163. JavaGetScreenSize();
  1164. JavaKeyboard ();
  1165. JavaClipboard ();
  1166. JavaLocation ();
  1167. InitSensor ();
  1168. LOG("LoopStart");
  1169. for(;;)
  1170. {
  1171. VecI2 old_posi=Ms.desktopPos();
  1172. LOG2("ALooper_pollAll");
  1173. Bool wait_end_set=false; Int wait=(App.active() ? App.active_wait : 0); UInt wait_end; // don't wait for !active, because we already wait after the event loop, and that would make 2 waits
  1174. Int id, events; android_poll_source *source;
  1175. wait:
  1176. while((id=ALooper_pollAll(wait, null, &events, (void**)&source))>=0) // process all events, using negative 'wait' for 'ALooper_pollAll' means unlimited wait
  1177. {
  1178. LOG2(S+"ALooper Source:"+Ptr(source)+", id:"+id);
  1179. if(source)source->process(AndroidApp, source); // process this event
  1180. LOG2(S+"ALooper processed");
  1181. if(id==LOOPER_ID_USER) // sensor data
  1182. {
  1183. ASensorEvent event;
  1184. #if DEBUG
  1185. if(!SensorEventQueue)LOG("Received sensor event but the 'SensorEventQueue' is null");
  1186. #endif
  1187. while(ASensorEventQueue_getEvents(SensorEventQueue, &event, 1)>0)switch(event.type)
  1188. {
  1189. case ASENSOR_TYPE_ACCELEROMETER : AccelerometerValue.set(-event.acceleration.x, -event.acceleration.y, event.acceleration.z); break;
  1190. case ASENSOR_TYPE_GYROSCOPE : GyroscopeValue.set( event.vector .x, event.vector .y, -event.vector .z); break;
  1191. case ASENSOR_TYPE_MAGNETIC_FIELD: MagnetometerValue.set( event.magnetic .x, event.magnetic .y, -event.magnetic .z); break;
  1192. }
  1193. }
  1194. wait=0; // don't wait for next events, in case app 'activate' status changed or it was requested to be closed or destroyed
  1195. // no need to check for 'App._close' or 'AndroidApp->destroyRequested' here, because we will do it below since we're setting wait=0 we don't risk with unlimited waits
  1196. }
  1197. if(App._close) // first check if we want to close manually
  1198. {
  1199. App.del(); // manually call shut down
  1200. ExitNow(); // do complete reset (including state of global variables) by killing the process
  1201. }
  1202. if(AndroidApp->destroyRequested)break; // this is triggered by 'ANativeActivity_finish', just break out of the loop so app can get minimized
  1203. if(!App.active()) // we may need to wait
  1204. if(wait=((App.flag&APP_WORK_IN_BACKGROUND) ? App.background_wait : -1)) // how long
  1205. {
  1206. if(wait>0) // finite wait
  1207. {
  1208. if(wait_end_set) // if we already set the end time limit
  1209. {
  1210. wait=wait_end-Time.curTimeMs(); if(wait<=0)goto stop; // calculate remaining time
  1211. }else
  1212. {
  1213. wait_end=Time.curTimeMs()+wait;
  1214. wait_end_set=true;
  1215. }
  1216. }//else wait=-1; // use negative wait to force unlimited wait in 'ALooper_pollAll', no need for this, because 'wait' is already negative
  1217. goto wait;
  1218. }
  1219. stop:
  1220. // process input
  1221. Ms._deltai = Ms. desktopPos()-old_posi;
  1222. Ms._delta_relative.x= Ms._deltai.x;
  1223. Ms._delta_relative.y=-Ms._deltai.y;
  1224. LOG2(S+"AndroidApp->window:"+(AndroidApp->window!=null));
  1225. #if DEBUG
  1226. if(!eglGetCurrentContext())LOG("No current EGL Context available on the main thread");
  1227. #endif
  1228. if(AndroidApp->window) // this may be unavailable if device is being (dis)connected to dock, or app is working in the background
  1229. UpdateSize(*AndroidApp->window);
  1230. App.update();
  1231. }
  1232. LOG("LoopEnd");
  1233. KeyboardLoaded=false;
  1234. ShutSensor(); // !! call before 'JavaShut' !!
  1235. JavaShut(); // !! call before 'BackgroundThread' !!
  1236. // app had input callback set, if there were some touches then they won't be unregistered now since app will be deleted, and because memory state can be preserved on Android, then manually remove them, without this touches may be preserved when rotating the screen, even though we're no longer touching it
  1237. REPA(Touches)
  1238. {
  1239. Touch &touch=Touches[i];
  1240. if(touch.on()) // if was pressed then release it (so application can process release)
  1241. {
  1242. touch._remove= true;
  1243. touch._state|= BS_RELEASED;
  1244. touch._state&=~BS_ON;
  1245. }else // if not pressed then just remove it
  1246. {
  1247. Touches.remove(i, true);
  1248. }
  1249. }
  1250. if((App.flag&APP_WORK_IN_BACKGROUND) && App.background_wait>=0)
  1251. {
  1252. LOG("BackgroundThread.create");
  1253. BackgroundThread.create(BackgroundFunc, null, 0, false, "BackgroundThread"); // !! call after 'JavaShut' !!
  1254. }
  1255. LOG("End");
  1256. }
  1257. /******************************************************************************/
  1258. #endif // ANDROID
  1259. /******************************************************************************/