JuceAppActivity.java 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. package com.juce;
  18. import android.app.Activity;
  19. import android.app.AlertDialog;
  20. import android.content.DialogInterface;
  21. import android.content.Context;
  22. import android.content.Intent;
  23. import android.content.res.Configuration;
  24. import android.content.pm.PackageInfo;
  25. import android.content.pm.PackageManager;
  26. import android.net.Uri;
  27. import android.os.Bundle;
  28. import android.os.Looper;
  29. import android.os.Handler;
  30. import android.os.ParcelUuid;
  31. import android.os.Environment;
  32. import android.view.*;
  33. import android.view.inputmethod.BaseInputConnection;
  34. import android.view.inputmethod.EditorInfo;
  35. import android.view.inputmethod.InputConnection;
  36. import android.view.inputmethod.InputMethodManager;
  37. import android.graphics.*;
  38. import android.text.ClipboardManager;
  39. import android.text.InputType;
  40. import android.util.DisplayMetrics;
  41. import android.util.Log;
  42. import java.lang.Runnable;
  43. import java.util.*;
  44. import java.io.*;
  45. import java.net.URL;
  46. import java.net.HttpURLConnection;
  47. import android.media.AudioManager;
  48. import android.media.MediaScannerConnection;
  49. import android.media.MediaScannerConnection.MediaScannerConnectionClient;
  50. import android.support.v4.content.ContextCompat;
  51. import android.support.v4.app.ActivityCompat;
  52. import android.Manifest;
  53. $$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the Projucer!
  54. //==============================================================================
  55. public class JuceAppActivity extends Activity
  56. {
  57. //==============================================================================
  58. static
  59. {
  60. System.loadLibrary ("juce_jni");
  61. }
  62. //==============================================================================
  63. public boolean isPermissionDeclaredInManifest (int permissionID)
  64. {
  65. String permissionToCheck = getAndroidPermissionName(permissionID);
  66. try
  67. {
  68. PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
  69. if (info.requestedPermissions != null)
  70. for (String permission : info.requestedPermissions)
  71. if (permission.equals (permissionToCheck))
  72. return true;
  73. }
  74. catch (PackageManager.NameNotFoundException e)
  75. {
  76. Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString());
  77. }
  78. Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck);
  79. return false;
  80. }
  81. //==============================================================================
  82. // these have to match the values of enum PermissionID in C++ class RuntimePermissions:
  83. private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1;
  84. private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2;
  85. private static String getAndroidPermissionName (int permissionID)
  86. {
  87. switch (permissionID)
  88. {
  89. case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO;
  90. case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION;
  91. }
  92. // unknown permission ID!
  93. assert false;
  94. return new String();
  95. }
  96. public boolean isPermissionGranted (int permissionID)
  97. {
  98. return ContextCompat.checkSelfPermission (this, getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED;
  99. }
  100. private Map<Integer, Long> permissionCallbackPtrMap;
  101. public void requestRuntimePermission (int permissionID, long ptrToCallback)
  102. {
  103. String permissionName = getAndroidPermissionName (permissionID);
  104. if (ContextCompat.checkSelfPermission (this, permissionName) != PackageManager.PERMISSION_GRANTED)
  105. {
  106. // remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously
  107. permissionCallbackPtrMap.put (permissionID, ptrToCallback);
  108. ActivityCompat.requestPermissions (this, new String[]{permissionName}, permissionID);
  109. }
  110. else
  111. {
  112. // permissions were already granted before, we can call callback directly
  113. androidRuntimePermissionsCallback (true, ptrToCallback);
  114. }
  115. }
  116. private native void androidRuntimePermissionsCallback (boolean permissionWasGranted, long ptrToCallback);
  117. $$JuceAndroidRuntimePermissionsCode$$ // If you get an error here, you need to re-save your project with the Projucer!
  118. //==============================================================================
  119. public static class MidiPortID extends Object
  120. {
  121. public MidiPortID (int index, boolean direction)
  122. {
  123. androidIndex = index;
  124. isInput = direction;
  125. }
  126. public int androidIndex;
  127. public boolean isInput;
  128. @Override
  129. public int hashCode()
  130. {
  131. Integer i = new Integer (androidIndex);
  132. return i.hashCode() * (isInput ? -1 : 1);
  133. }
  134. @Override
  135. public boolean equals (Object obj)
  136. {
  137. if (obj == null)
  138. return false;
  139. if (getClass() != obj.getClass())
  140. return false;
  141. MidiPortID other = (MidiPortID) obj;
  142. return (androidIndex == other.androidIndex && isInput == other.isInput);
  143. }
  144. }
  145. public interface JuceMidiPort
  146. {
  147. boolean isInputPort();
  148. // start, stop does nothing on an output port
  149. void start();
  150. void stop();
  151. void close();
  152. MidiPortID getPortId();
  153. // send will do nothing on an input port
  154. void sendMidi (byte[] msg, int offset, int count);
  155. }
  156. //==============================================================================
  157. $$JuceAndroidMidiCode$$ // If you get an error here, you need to re-save your project with the Projucer!
  158. //==============================================================================
  159. @Override
  160. public void onCreate (Bundle savedInstanceState)
  161. {
  162. super.onCreate (savedInstanceState);
  163. isScreenSaverEnabled = true;
  164. hideActionBar();
  165. viewHolder = new ViewHolder (this);
  166. setContentView (viewHolder);
  167. setVolumeControlStream (AudioManager.STREAM_MUSIC);
  168. permissionCallbackPtrMap = new HashMap<Integer, Long>();
  169. }
  170. @Override
  171. protected void onDestroy()
  172. {
  173. quitApp();
  174. super.onDestroy();
  175. clearDataCache();
  176. }
  177. @Override
  178. protected void onPause()
  179. {
  180. suspendApp();
  181. try
  182. {
  183. Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down
  184. // openGL glitches when pausing/resuming apps..
  185. } catch (InterruptedException e) {}
  186. super.onPause();
  187. }
  188. @Override
  189. protected void onResume()
  190. {
  191. super.onResume();
  192. resumeApp();
  193. }
  194. @Override
  195. public void onConfigurationChanged (Configuration cfg)
  196. {
  197. super.onConfigurationChanged (cfg);
  198. setContentView (viewHolder);
  199. }
  200. private void callAppLauncher()
  201. {
  202. launchApp (getApplicationInfo().publicSourceDir,
  203. getApplicationInfo().dataDir);
  204. }
  205. private void hideActionBar()
  206. {
  207. // get "getActionBar" method
  208. java.lang.reflect.Method getActionBarMethod = null;
  209. try
  210. {
  211. getActionBarMethod = this.getClass().getMethod ("getActionBar");
  212. }
  213. catch (SecurityException e) { return; }
  214. catch (NoSuchMethodException e) { return; }
  215. if (getActionBarMethod == null) return;
  216. // invoke "getActionBar" method
  217. Object actionBar = null;
  218. try
  219. {
  220. actionBar = getActionBarMethod.invoke (this);
  221. }
  222. catch (java.lang.IllegalArgumentException e) { return; }
  223. catch (java.lang.IllegalAccessException e) { return; }
  224. catch (java.lang.reflect.InvocationTargetException e) { return; }
  225. if (actionBar == null) return;
  226. // get "hide" method
  227. java.lang.reflect.Method actionBarHideMethod = null;
  228. try
  229. {
  230. actionBarHideMethod = actionBar.getClass().getMethod ("hide");
  231. }
  232. catch (SecurityException e) { return; }
  233. catch (NoSuchMethodException e) { return; }
  234. if (actionBarHideMethod == null) return;
  235. // invoke "hide" method
  236. try
  237. {
  238. actionBarHideMethod.invoke (actionBar);
  239. }
  240. catch (java.lang.IllegalArgumentException e) {}
  241. catch (java.lang.IllegalAccessException e) {}
  242. catch (java.lang.reflect.InvocationTargetException e) {}
  243. }
  244. //==============================================================================
  245. private native void launchApp (String appFile, String appDataDir);
  246. private native void quitApp();
  247. private native void suspendApp();
  248. private native void resumeApp();
  249. private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
  250. //==============================================================================
  251. public native void deliverMessage (long value);
  252. private android.os.Handler messageHandler = new android.os.Handler();
  253. public final void postMessage (long value)
  254. {
  255. messageHandler.post (new MessageCallback (value));
  256. }
  257. private final class MessageCallback implements Runnable
  258. {
  259. public MessageCallback (long value_) { value = value_; }
  260. public final void run() { deliverMessage (value); }
  261. private long value;
  262. }
  263. //==============================================================================
  264. private ViewHolder viewHolder;
  265. private MidiDeviceManager midiDeviceManager = null;
  266. private BluetoothManager bluetoothManager = null;
  267. private boolean isScreenSaverEnabled;
  268. private java.util.Timer keepAliveTimer;
  269. public final ComponentPeerView createNewView (boolean opaque, long host)
  270. {
  271. ComponentPeerView v = new ComponentPeerView (this, opaque, host);
  272. viewHolder.addView (v);
  273. return v;
  274. }
  275. public final void deleteView (ComponentPeerView view)
  276. {
  277. ViewGroup group = (ViewGroup) (view.getParent());
  278. if (group != null)
  279. group.removeView (view);
  280. }
  281. public final void deleteNativeSurfaceView (NativeSurfaceView view)
  282. {
  283. ViewGroup group = (ViewGroup) (view.getParent());
  284. if (group != null)
  285. group.removeView (view);
  286. }
  287. final class ViewHolder extends ViewGroup
  288. {
  289. public ViewHolder (Context context)
  290. {
  291. super (context);
  292. setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
  293. setFocusable (false);
  294. }
  295. protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
  296. {
  297. setScreenSize (getWidth(), getHeight(), getDPI());
  298. if (isFirstResize)
  299. {
  300. isFirstResize = false;
  301. callAppLauncher();
  302. }
  303. }
  304. private final int getDPI()
  305. {
  306. DisplayMetrics metrics = new DisplayMetrics();
  307. getWindowManager().getDefaultDisplay().getMetrics (metrics);
  308. return metrics.densityDpi;
  309. }
  310. private boolean isFirstResize = true;
  311. }
  312. public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
  313. {
  314. canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
  315. }
  316. //==============================================================================
  317. public final void setScreenSaver (boolean enabled)
  318. {
  319. if (isScreenSaverEnabled != enabled)
  320. {
  321. isScreenSaverEnabled = enabled;
  322. if (keepAliveTimer != null)
  323. {
  324. keepAliveTimer.cancel();
  325. keepAliveTimer = null;
  326. }
  327. if (enabled)
  328. {
  329. getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  330. }
  331. else
  332. {
  333. getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  334. // If no user input is received after about 3 seconds, the OS will lower the
  335. // task's priority, so this timer forces it to be kept active.
  336. keepAliveTimer = new java.util.Timer();
  337. keepAliveTimer.scheduleAtFixedRate (new TimerTask()
  338. {
  339. @Override
  340. public void run()
  341. {
  342. android.app.Instrumentation instrumentation = new android.app.Instrumentation();
  343. try
  344. {
  345. instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_UNKNOWN);
  346. }
  347. catch (Exception e)
  348. {
  349. }
  350. }
  351. }, 2000, 2000);
  352. }
  353. }
  354. }
  355. public final boolean getScreenSaver()
  356. {
  357. return isScreenSaverEnabled;
  358. }
  359. //==============================================================================
  360. public final String getClipboardContent()
  361. {
  362. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  363. return clipboard.getText().toString();
  364. }
  365. public final void setClipboardContent (String newText)
  366. {
  367. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  368. clipboard.setText (newText);
  369. }
  370. //==============================================================================
  371. public final void showMessageBox (String title, String message, final long callback)
  372. {
  373. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  374. builder.setTitle (title)
  375. .setMessage (message)
  376. .setCancelable (true)
  377. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  378. {
  379. public void onClick (DialogInterface dialog, int id)
  380. {
  381. dialog.cancel();
  382. JuceAppActivity.this.alertDismissed (callback, 0);
  383. }
  384. });
  385. builder.create().show();
  386. }
  387. public final void showOkCancelBox (String title, String message, final long callback)
  388. {
  389. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  390. builder.setTitle (title)
  391. .setMessage (message)
  392. .setCancelable (true)
  393. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  394. {
  395. public void onClick (DialogInterface dialog, int id)
  396. {
  397. dialog.cancel();
  398. JuceAppActivity.this.alertDismissed (callback, 1);
  399. }
  400. })
  401. .setNegativeButton ("Cancel", new DialogInterface.OnClickListener()
  402. {
  403. public void onClick (DialogInterface dialog, int id)
  404. {
  405. dialog.cancel();
  406. JuceAppActivity.this.alertDismissed (callback, 0);
  407. }
  408. });
  409. builder.create().show();
  410. }
  411. public final void showYesNoCancelBox (String title, String message, final long callback)
  412. {
  413. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  414. builder.setTitle (title)
  415. .setMessage (message)
  416. .setCancelable (true)
  417. .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
  418. {
  419. public void onClick (DialogInterface dialog, int id)
  420. {
  421. dialog.cancel();
  422. JuceAppActivity.this.alertDismissed (callback, 1);
  423. }
  424. })
  425. .setNegativeButton ("No", new DialogInterface.OnClickListener()
  426. {
  427. public void onClick (DialogInterface dialog, int id)
  428. {
  429. dialog.cancel();
  430. JuceAppActivity.this.alertDismissed (callback, 2);
  431. }
  432. })
  433. .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
  434. {
  435. public void onClick (DialogInterface dialog, int id)
  436. {
  437. dialog.cancel();
  438. JuceAppActivity.this.alertDismissed (callback, 0);
  439. }
  440. });
  441. builder.create().show();
  442. }
  443. public native void alertDismissed (long callback, int id);
  444. //==============================================================================
  445. public final class ComponentPeerView extends ViewGroup
  446. implements View.OnFocusChangeListener
  447. {
  448. public ComponentPeerView (Context context, boolean opaque_, long host)
  449. {
  450. super (context);
  451. this.host = host;
  452. setWillNotDraw (false);
  453. opaque = opaque_;
  454. setFocusable (true);
  455. setFocusableInTouchMode (true);
  456. setOnFocusChangeListener (this);
  457. requestFocus();
  458. // swap red and blue colours to match internal opengl texture format
  459. ColorMatrix colorMatrix = new ColorMatrix();
  460. float[] colorTransform = { 0, 0, 1.0f, 0, 0,
  461. 0, 1.0f, 0, 0, 0,
  462. 1.0f, 0, 0, 0, 0,
  463. 0, 0, 0, 1.0f, 0 };
  464. colorMatrix.set (colorTransform);
  465. paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
  466. }
  467. //==============================================================================
  468. private native void handlePaint (long host, Canvas canvas, Paint paint);
  469. @Override
  470. public void onDraw (Canvas canvas)
  471. {
  472. handlePaint (host, canvas, paint);
  473. }
  474. @Override
  475. public boolean isOpaque()
  476. {
  477. return opaque;
  478. }
  479. private boolean opaque;
  480. private long host;
  481. private Paint paint = new Paint();
  482. //==============================================================================
  483. private native void handleMouseDown (long host, int index, float x, float y, long time);
  484. private native void handleMouseDrag (long host, int index, float x, float y, long time);
  485. private native void handleMouseUp (long host, int index, float x, float y, long time);
  486. @Override
  487. public boolean onTouchEvent (MotionEvent event)
  488. {
  489. int action = event.getAction();
  490. long time = event.getEventTime();
  491. switch (action & MotionEvent.ACTION_MASK)
  492. {
  493. case MotionEvent.ACTION_DOWN:
  494. handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time);
  495. return true;
  496. case MotionEvent.ACTION_CANCEL:
  497. case MotionEvent.ACTION_UP:
  498. handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time);
  499. return true;
  500. case MotionEvent.ACTION_MOVE:
  501. {
  502. int n = event.getPointerCount();
  503. for (int i = 0; i < n; ++i)
  504. handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  505. return true;
  506. }
  507. case MotionEvent.ACTION_POINTER_UP:
  508. {
  509. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  510. handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  511. return true;
  512. }
  513. case MotionEvent.ACTION_POINTER_DOWN:
  514. {
  515. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  516. handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  517. return true;
  518. }
  519. default:
  520. break;
  521. }
  522. return false;
  523. }
  524. //==============================================================================
  525. private native void handleKeyDown (long host, int keycode, int textchar);
  526. private native void handleKeyUp (long host, int keycode, int textchar);
  527. public void showKeyboard (String type)
  528. {
  529. InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
  530. if (imm != null)
  531. {
  532. if (type.length() > 0)
  533. {
  534. imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
  535. imm.setInputMethod (getWindowToken(), type);
  536. }
  537. else
  538. {
  539. imm.hideSoftInputFromWindow (getWindowToken(), 0);
  540. }
  541. }
  542. }
  543. @Override
  544. public boolean onKeyDown (int keyCode, KeyEvent event)
  545. {
  546. switch (keyCode)
  547. {
  548. case KeyEvent.KEYCODE_VOLUME_UP:
  549. case KeyEvent.KEYCODE_VOLUME_DOWN:
  550. return super.onKeyDown (keyCode, event);
  551. default: break;
  552. }
  553. handleKeyDown (host, keyCode, event.getUnicodeChar());
  554. return true;
  555. }
  556. @Override
  557. public boolean onKeyUp (int keyCode, KeyEvent event)
  558. {
  559. handleKeyUp (host, keyCode, event.getUnicodeChar());
  560. return true;
  561. }
  562. @Override
  563. public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
  564. {
  565. if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
  566. return super.onKeyMultiple (keyCode, count, event);
  567. if (event.getCharacters() != null)
  568. {
  569. int utf8Char = event.getCharacters().codePointAt (0);
  570. handleKeyDown (host, utf8Char, utf8Char);
  571. return true;
  572. }
  573. return false;
  574. }
  575. // this is here to make keyboard entry work on a Galaxy Tab2 10.1
  576. @Override
  577. public InputConnection onCreateInputConnection (EditorInfo outAttrs)
  578. {
  579. outAttrs.actionLabel = "";
  580. outAttrs.hintText = "";
  581. outAttrs.initialCapsMode = 0;
  582. outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
  583. outAttrs.label = "";
  584. outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
  585. outAttrs.inputType = InputType.TYPE_NULL;
  586. return new BaseInputConnection (this, false);
  587. }
  588. //==============================================================================
  589. @Override
  590. protected void onSizeChanged (int w, int h, int oldw, int oldh)
  591. {
  592. super.onSizeChanged (w, h, oldw, oldh);
  593. viewSizeChanged (host);
  594. }
  595. @Override
  596. protected void onLayout (boolean changed, int left, int top, int right, int bottom)
  597. {
  598. for (int i = getChildCount(); --i >= 0;)
  599. requestTransparentRegion (getChildAt (i));
  600. }
  601. private native void viewSizeChanged (long host);
  602. @Override
  603. public void onFocusChange (View v, boolean hasFocus)
  604. {
  605. if (v == this)
  606. focusChanged (host, hasFocus);
  607. }
  608. private native void focusChanged (long host, boolean hasFocus);
  609. public void setViewName (String newName) {}
  610. public boolean isVisible() { return getVisibility() == VISIBLE; }
  611. public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
  612. public boolean containsPoint (int x, int y)
  613. {
  614. return true; //xxx needs to check overlapping views
  615. }
  616. }
  617. //==============================================================================
  618. public static class NativeSurfaceView extends SurfaceView
  619. implements SurfaceHolder.Callback
  620. {
  621. private long nativeContext = 0;
  622. NativeSurfaceView (Context context, long nativeContextPtr)
  623. {
  624. super (context);
  625. nativeContext = nativeContextPtr;
  626. }
  627. public Surface getNativeSurface()
  628. {
  629. Surface retval = null;
  630. SurfaceHolder holder = getHolder();
  631. if (holder != null)
  632. retval = holder.getSurface();
  633. return retval;
  634. }
  635. //==============================================================================
  636. @Override
  637. public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
  638. {
  639. surfaceChangedNative (nativeContext, holder, format, width, height);
  640. }
  641. @Override
  642. public void surfaceCreated (SurfaceHolder holder)
  643. {
  644. surfaceCreatedNative (nativeContext, holder);
  645. }
  646. @Override
  647. public void surfaceDestroyed (SurfaceHolder holder)
  648. {
  649. surfaceDestroyedNative (nativeContext, holder);
  650. }
  651. @Override
  652. protected void dispatchDraw (Canvas canvas)
  653. {
  654. super.dispatchDraw (canvas);
  655. dispatchDrawNative (nativeContext, canvas);
  656. }
  657. //==============================================================================
  658. @Override
  659. protected void onAttachedToWindow ()
  660. {
  661. super.onAttachedToWindow();
  662. getHolder().addCallback (this);
  663. }
  664. @Override
  665. protected void onDetachedFromWindow ()
  666. {
  667. super.onDetachedFromWindow();
  668. getHolder().removeCallback (this);
  669. }
  670. //==============================================================================
  671. private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
  672. private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
  673. private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
  674. private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
  675. int format, int width, int height);
  676. }
  677. public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
  678. {
  679. return new NativeSurfaceView (this, nativeSurfacePtr);
  680. }
  681. //==============================================================================
  682. public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds)
  683. {
  684. Path p = new Path();
  685. paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p);
  686. RectF boundsF = new RectF();
  687. p.computeBounds (boundsF, true);
  688. matrix.mapRect (boundsF);
  689. boundsF.roundOut (bounds);
  690. bounds.left--;
  691. bounds.right++;
  692. final int w = bounds.width();
  693. final int h = Math.max (1, bounds.height());
  694. Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
  695. Canvas c = new Canvas (bm);
  696. matrix.postTranslate (-bounds.left, -bounds.top);
  697. c.setMatrix (matrix);
  698. c.drawPath (p, paint);
  699. final int sizeNeeded = w * h;
  700. if (cachedRenderArray.length < sizeNeeded)
  701. cachedRenderArray = new int [sizeNeeded];
  702. bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
  703. bm.recycle();
  704. return cachedRenderArray;
  705. }
  706. private int[] cachedRenderArray = new int [256];
  707. //==============================================================================
  708. public static class HTTPStream
  709. {
  710. public HTTPStream (HttpURLConnection connection_,
  711. int[] statusCode, StringBuffer responseHeaders) throws IOException
  712. {
  713. connection = connection_;
  714. try
  715. {
  716. inputStream = new BufferedInputStream (connection.getInputStream());
  717. }
  718. catch (IOException e)
  719. {
  720. if (connection.getResponseCode() < 400)
  721. throw e;
  722. }
  723. finally
  724. {
  725. statusCode[0] = connection.getResponseCode();
  726. }
  727. if (statusCode[0] >= 400)
  728. inputStream = connection.getErrorStream();
  729. else
  730. inputStream = connection.getInputStream();
  731. for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
  732. if (entry.getKey() != null && entry.getValue() != null)
  733. responseHeaders.append (entry.getKey() + ": "
  734. + android.text.TextUtils.join (",", entry.getValue()) + "\n");
  735. }
  736. public final void release()
  737. {
  738. try
  739. {
  740. inputStream.close();
  741. }
  742. catch (IOException e)
  743. {}
  744. connection.disconnect();
  745. }
  746. public final int read (byte[] buffer, int numBytes)
  747. {
  748. int num = 0;
  749. try
  750. {
  751. num = inputStream.read (buffer, 0, numBytes);
  752. }
  753. catch (IOException e)
  754. {}
  755. if (num > 0)
  756. position += num;
  757. return num;
  758. }
  759. public final long getPosition() { return position; }
  760. public final long getTotalLength() { return -1; }
  761. public final boolean isExhausted() { return false; }
  762. public final boolean setPosition (long newPos) { return false; }
  763. private HttpURLConnection connection;
  764. private InputStream inputStream;
  765. private long position;
  766. }
  767. public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
  768. String headers, int timeOutMs, int[] statusCode,
  769. StringBuffer responseHeaders, int numRedirectsToFollow,
  770. String httpRequestCmd)
  771. {
  772. // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
  773. if (timeOutMs < 0)
  774. timeOutMs = 0;
  775. else if (timeOutMs == 0)
  776. timeOutMs = 30000;
  777. // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
  778. // So convert headers string to an array, with an element for each line
  779. String headerLines[] = headers.split("\\n");
  780. for (;;)
  781. {
  782. try
  783. {
  784. HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection());
  785. if (connection != null)
  786. {
  787. try
  788. {
  789. connection.setInstanceFollowRedirects (false);
  790. connection.setConnectTimeout (timeOutMs);
  791. connection.setReadTimeout (timeOutMs);
  792. // Set request headers
  793. for (int i = 0; i < headerLines.length; ++i)
  794. {
  795. int pos = headerLines[i].indexOf (":");
  796. if (pos > 0 && pos < headerLines[i].length())
  797. {
  798. String field = headerLines[i].substring (0, pos);
  799. String value = headerLines[i].substring (pos + 1);
  800. if (value.length() > 0)
  801. connection.setRequestProperty (field, value);
  802. }
  803. }
  804. connection.setRequestMethod (httpRequestCmd);
  805. if (isPost)
  806. {
  807. connection.setDoOutput (true);
  808. if (postData != null)
  809. {
  810. OutputStream out = connection.getOutputStream();
  811. out.write(postData);
  812. out.flush();
  813. }
  814. }
  815. HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders);
  816. // Process redirect & continue as necessary
  817. int status = statusCode[0];
  818. if (--numRedirectsToFollow >= 0
  819. && (status == 301 || status == 302 || status == 303 || status == 307))
  820. {
  821. // Assumes only one occurrence of "Location"
  822. int pos1 = responseHeaders.indexOf ("Location:") + 10;
  823. int pos2 = responseHeaders.indexOf ("\n", pos1);
  824. if (pos2 > pos1)
  825. {
  826. String newLocation = responseHeaders.substring(pos1, pos2);
  827. // Handle newLocation whether it's absolute or relative
  828. URL baseUrl = new URL (address);
  829. URL newUrl = new URL (baseUrl, newLocation);
  830. String transformedNewLocation = newUrl.toString();
  831. if (transformedNewLocation != address)
  832. {
  833. address = transformedNewLocation;
  834. // Clear responseHeaders before next iteration
  835. responseHeaders.delete (0, responseHeaders.length());
  836. continue;
  837. }
  838. }
  839. }
  840. return httpStream;
  841. }
  842. catch (Throwable e)
  843. {
  844. connection.disconnect();
  845. }
  846. }
  847. }
  848. catch (Throwable e) {}
  849. return null;
  850. }
  851. }
  852. public final void launchURL (String url)
  853. {
  854. startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
  855. }
  856. public static final String getLocaleValue (boolean isRegion)
  857. {
  858. java.util.Locale locale = java.util.Locale.getDefault();
  859. return isRegion ? locale.getDisplayCountry (java.util.Locale.US)
  860. : locale.getDisplayLanguage (java.util.Locale.US);
  861. }
  862. private static final String getFileLocation (String type)
  863. {
  864. return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath();
  865. }
  866. public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); }
  867. public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); }
  868. public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); }
  869. public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); }
  870. public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
  871. //==============================================================================
  872. private final class SingleMediaScanner implements MediaScannerConnectionClient
  873. {
  874. public SingleMediaScanner (Context context, String filename)
  875. {
  876. file = filename;
  877. msc = new MediaScannerConnection (context, this);
  878. msc.connect();
  879. }
  880. @Override
  881. public void onMediaScannerConnected()
  882. {
  883. msc.scanFile (file, null);
  884. }
  885. @Override
  886. public void onScanCompleted (String path, Uri uri)
  887. {
  888. msc.disconnect();
  889. }
  890. private MediaScannerConnection msc;
  891. private String file;
  892. }
  893. public final void scanFile (String filename)
  894. {
  895. new SingleMediaScanner (this, filename);
  896. }
  897. public final Typeface getTypeFaceFromAsset (String assetName)
  898. {
  899. try
  900. {
  901. return Typeface.createFromAsset (this.getResources().getAssets(), assetName);
  902. }
  903. catch (Throwable e) {}
  904. return null;
  905. }
  906. final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
  907. public static String bytesToHex (byte[] bytes)
  908. {
  909. char[] hexChars = new char[bytes.length * 2];
  910. for (int j = 0; j < bytes.length; ++j)
  911. {
  912. int v = bytes[j] & 0xff;
  913. hexChars[j * 2] = hexArray[v >>> 4];
  914. hexChars[j * 2 + 1] = hexArray[v & 0x0f];
  915. }
  916. return new String (hexChars);
  917. }
  918. final private java.util.Map dataCache = new java.util.HashMap();
  919. synchronized private final File getDataCacheFile (byte[] data)
  920. {
  921. try
  922. {
  923. java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5");
  924. digest.update (data);
  925. String key = bytesToHex (digest.digest());
  926. if (dataCache.containsKey (key))
  927. return (File) dataCache.get (key);
  928. File f = new File (this.getCacheDir(), "bindata_" + key);
  929. f.delete();
  930. FileOutputStream os = new FileOutputStream (f);
  931. os.write (data, 0, data.length);
  932. dataCache.put (key, f);
  933. return f;
  934. }
  935. catch (Throwable e) {}
  936. return null;
  937. }
  938. private final void clearDataCache()
  939. {
  940. java.util.Iterator it = dataCache.values().iterator();
  941. while (it.hasNext())
  942. {
  943. File f = (File) it.next();
  944. f.delete();
  945. }
  946. }
  947. public final Typeface getTypeFaceFromByteArray (byte[] data)
  948. {
  949. try
  950. {
  951. File f = getDataCacheFile (data);
  952. if (f != null)
  953. return Typeface.createFromFile (f);
  954. }
  955. catch (Exception e)
  956. {
  957. Log.e ("JUCE", e.toString());
  958. }
  959. return null;
  960. }
  961. public final int getAndroidSDKVersion()
  962. {
  963. return android.os.Build.VERSION.SDK_INT;
  964. }
  965. public final String audioManagerGetProperty (String property)
  966. {
  967. Object obj = getSystemService (AUDIO_SERVICE);
  968. if (obj == null)
  969. return null;
  970. java.lang.reflect.Method method;
  971. try
  972. {
  973. method = obj.getClass().getMethod ("getProperty", String.class);
  974. }
  975. catch (SecurityException e) { return null; }
  976. catch (NoSuchMethodException e) { return null; }
  977. if (method == null)
  978. return null;
  979. try
  980. {
  981. return (String) method.invoke (obj, property);
  982. }
  983. catch (java.lang.IllegalArgumentException e) {}
  984. catch (java.lang.IllegalAccessException e) {}
  985. catch (java.lang.reflect.InvocationTargetException e) {}
  986. return null;
  987. }
  988. public final int setCurrentThreadPriority (int priority)
  989. {
  990. android.os.Process.setThreadPriority (android.os.Process.myTid(), priority);
  991. return android.os.Process.getThreadPriority (android.os.Process.myTid());
  992. }
  993. public final boolean hasSystemFeature (String property)
  994. {
  995. return getPackageManager().hasSystemFeature (property);
  996. }
  997. private static class JuceThread extends Thread
  998. {
  999. public JuceThread (long host, String threadName, long threadStackSize)
  1000. {
  1001. super (null, null, threadName, threadStackSize);
  1002. _this = host;
  1003. }
  1004. public void run()
  1005. {
  1006. runThread(_this);
  1007. }
  1008. private native void runThread (long host);
  1009. private long _this;
  1010. }
  1011. public final Thread createNewThread(long host, String threadName, long threadStackSize)
  1012. {
  1013. return new JuceThread(host, threadName, threadStackSize);
  1014. }
  1015. }