The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

698 lines
23KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. package com.juce;
  19. import android.app.Activity;
  20. import android.app.AlertDialog;
  21. import android.content.DialogInterface;
  22. import android.content.Context;
  23. import android.content.Intent;
  24. import android.content.res.Configuration;
  25. import android.net.Uri;
  26. import android.os.Bundle;
  27. import android.view.*;
  28. import android.view.inputmethod.BaseInputConnection;
  29. import android.view.inputmethod.EditorInfo;
  30. import android.view.inputmethod.InputConnection;
  31. import android.view.inputmethod.InputMethodManager;
  32. import android.graphics.*;
  33. import android.opengl.*;
  34. import android.text.ClipboardManager;
  35. import android.text.InputType;
  36. import java.io.BufferedInputStream;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import java.io.OutputStream;
  40. import java.net.URL;
  41. import java.net.HttpURLConnection;
  42. import javax.microedition.khronos.egl.EGLConfig;
  43. import javax.microedition.khronos.opengles.GL10;
  44. import android.media.MediaScannerConnection;
  45. import android.media.MediaScannerConnection.MediaScannerConnectionClient;
  46. //==============================================================================
  47. public final class JuceAppActivity extends Activity
  48. {
  49. //==============================================================================
  50. static
  51. {
  52. System.loadLibrary ("juce_jni");
  53. }
  54. @Override
  55. public final void onCreate (Bundle savedInstanceState)
  56. {
  57. super.onCreate (savedInstanceState);
  58. viewHolder = new ViewHolder (this);
  59. setContentView (viewHolder);
  60. }
  61. @Override
  62. protected final void onDestroy()
  63. {
  64. quitApp();
  65. super.onDestroy();
  66. }
  67. @Override
  68. protected final void onPause()
  69. {
  70. if (viewHolder != null)
  71. viewHolder.onPause();
  72. suspendApp();
  73. super.onPause();
  74. }
  75. @Override
  76. protected final void onResume()
  77. {
  78. super.onResume();
  79. if (viewHolder != null)
  80. viewHolder.onResume();
  81. resumeApp();
  82. }
  83. @Override
  84. public void onConfigurationChanged (Configuration cfg)
  85. {
  86. super.onConfigurationChanged (cfg);
  87. setContentView (viewHolder);
  88. }
  89. private void callAppLauncher()
  90. {
  91. launchApp (getApplicationInfo().publicSourceDir,
  92. getApplicationInfo().dataDir);
  93. }
  94. //==============================================================================
  95. private native void launchApp (String appFile, String appDataDir);
  96. private native void quitApp();
  97. private native void suspendApp();
  98. private native void resumeApp();
  99. private native void setScreenSize (int screenWidth, int screenHeight);
  100. //==============================================================================
  101. public native void deliverMessage (long value);
  102. private android.os.Handler messageHandler = new android.os.Handler();
  103. public final void postMessage (long value)
  104. {
  105. messageHandler.post (new MessageCallback (value));
  106. }
  107. private final class MessageCallback implements Runnable
  108. {
  109. public MessageCallback (long value_) { value = value_; }
  110. public final void run() { deliverMessage (value); }
  111. private long value;
  112. }
  113. //==============================================================================
  114. private ViewHolder viewHolder;
  115. public final ComponentPeerView createNewView (boolean opaque)
  116. {
  117. ComponentPeerView v = new ComponentPeerView (this, opaque);
  118. viewHolder.addView (v);
  119. return v;
  120. }
  121. public final void deleteView (ComponentPeerView view)
  122. {
  123. ViewGroup group = (ViewGroup) (view.getParent());
  124. if (group != null)
  125. group.removeView (view);
  126. }
  127. final class ViewHolder extends ViewGroup
  128. {
  129. public ViewHolder (Context context)
  130. {
  131. super (context);
  132. setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
  133. setFocusable (false);
  134. }
  135. protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
  136. {
  137. setScreenSize (getWidth(), getHeight());
  138. if (isFirstResize)
  139. {
  140. isFirstResize = false;
  141. callAppLauncher();
  142. }
  143. }
  144. public final void onPause()
  145. {
  146. for (int i = getChildCount(); --i >= 0;)
  147. {
  148. View v = getChildAt (i);
  149. if (v instanceof ComponentPeerView)
  150. ((ComponentPeerView) v).onPause();
  151. }
  152. }
  153. public final void onResume()
  154. {
  155. for (int i = getChildCount(); --i >= 0;)
  156. {
  157. View v = getChildAt (i);
  158. if (v instanceof ComponentPeerView)
  159. ((ComponentPeerView) v).onResume();
  160. }
  161. }
  162. private boolean isFirstResize = true;
  163. }
  164. public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
  165. {
  166. canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
  167. }
  168. //==============================================================================
  169. public final String getClipboardContent()
  170. {
  171. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  172. return clipboard.getText().toString();
  173. }
  174. public final void setClipboardContent (String newText)
  175. {
  176. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  177. clipboard.setText (newText);
  178. }
  179. //==============================================================================
  180. public final void showMessageBox (String title, String message, final long callback)
  181. {
  182. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  183. builder.setTitle (title)
  184. .setMessage (message)
  185. .setCancelable (true)
  186. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  187. {
  188. public void onClick (DialogInterface dialog, int id)
  189. {
  190. dialog.cancel();
  191. JuceAppActivity.this.alertDismissed (callback, 0);
  192. }
  193. });
  194. builder.create().show();
  195. }
  196. public final void showOkCancelBox (String title, String message, final long callback)
  197. {
  198. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  199. builder.setTitle (title)
  200. .setMessage (message)
  201. .setCancelable (true)
  202. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  203. {
  204. public void onClick (DialogInterface dialog, int id)
  205. {
  206. dialog.cancel();
  207. JuceAppActivity.this.alertDismissed (callback, 1);
  208. }
  209. })
  210. .setNegativeButton ("Cancel", new DialogInterface.OnClickListener()
  211. {
  212. public void onClick (DialogInterface dialog, int id)
  213. {
  214. dialog.cancel();
  215. JuceAppActivity.this.alertDismissed (callback, 0);
  216. }
  217. });
  218. builder.create().show();
  219. }
  220. public final void showYesNoCancelBox (String title, String message, final long callback)
  221. {
  222. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  223. builder.setTitle (title)
  224. .setMessage (message)
  225. .setCancelable (true)
  226. .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
  227. {
  228. public void onClick (DialogInterface dialog, int id)
  229. {
  230. dialog.cancel();
  231. JuceAppActivity.this.alertDismissed (callback, 1);
  232. }
  233. })
  234. .setNegativeButton ("No", new DialogInterface.OnClickListener()
  235. {
  236. public void onClick (DialogInterface dialog, int id)
  237. {
  238. dialog.cancel();
  239. JuceAppActivity.this.alertDismissed (callback, 2);
  240. }
  241. })
  242. .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
  243. {
  244. public void onClick (DialogInterface dialog, int id)
  245. {
  246. dialog.cancel();
  247. JuceAppActivity.this.alertDismissed (callback, 0);
  248. }
  249. });
  250. builder.create().show();
  251. }
  252. public native void alertDismissed (long callback, int id);
  253. //==============================================================================
  254. public final class ComponentPeerView extends ViewGroup
  255. implements View.OnFocusChangeListener
  256. {
  257. public ComponentPeerView (Context context, boolean opaque_)
  258. {
  259. super (context);
  260. setWillNotDraw (false);
  261. opaque = opaque_;
  262. setFocusable (true);
  263. setFocusableInTouchMode (true);
  264. setOnFocusChangeListener (this);
  265. requestFocus();
  266. }
  267. //==============================================================================
  268. private native void handlePaint (Canvas canvas);
  269. @Override
  270. public void draw (Canvas canvas)
  271. {
  272. super.draw (canvas);
  273. handlePaint (canvas);
  274. }
  275. @Override
  276. public boolean isOpaque()
  277. {
  278. return opaque;
  279. }
  280. private boolean opaque;
  281. //==============================================================================
  282. private native void handleMouseDown (int index, float x, float y, long time);
  283. private native void handleMouseDrag (int index, float x, float y, long time);
  284. private native void handleMouseUp (int index, float x, float y, long time);
  285. @Override
  286. public boolean onTouchEvent (MotionEvent event)
  287. {
  288. int action = event.getAction();
  289. long time = event.getEventTime();
  290. switch (action & MotionEvent.ACTION_MASK)
  291. {
  292. case MotionEvent.ACTION_DOWN:
  293. handleMouseDown (event.getPointerId(0), event.getX(), event.getY(), time);
  294. return true;
  295. case MotionEvent.ACTION_CANCEL:
  296. case MotionEvent.ACTION_UP:
  297. handleMouseUp (event.getPointerId(0), event.getX(), event.getY(), time);
  298. return true;
  299. case MotionEvent.ACTION_MOVE:
  300. {
  301. int n = event.getPointerCount();
  302. for (int i = 0; i < n; ++i)
  303. handleMouseDrag (event.getPointerId(i), event.getX(i), event.getY(i), time);
  304. return true;
  305. }
  306. case MotionEvent.ACTION_POINTER_UP:
  307. {
  308. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  309. handleMouseUp (event.getPointerId(i), event.getX(i), event.getY(i), time);
  310. return true;
  311. }
  312. case MotionEvent.ACTION_POINTER_DOWN:
  313. {
  314. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  315. handleMouseDown (event.getPointerId(i), event.getX(i), event.getY(i), time);
  316. return true;
  317. }
  318. default:
  319. break;
  320. }
  321. return false;
  322. }
  323. //==============================================================================
  324. private native void handleKeyDown (int keycode, int textchar);
  325. private native void handleKeyUp (int keycode, int textchar);
  326. public void showKeyboard (boolean shouldShow)
  327. {
  328. InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
  329. if (imm != null)
  330. {
  331. if (shouldShow)
  332. imm.showSoftInput (this, InputMethodManager.SHOW_FORCED);
  333. else
  334. imm.hideSoftInputFromWindow (getWindowToken(), 0);
  335. }
  336. }
  337. @Override
  338. public boolean onKeyDown (int keyCode, KeyEvent event)
  339. {
  340. handleKeyDown (keyCode, event.getUnicodeChar());
  341. return true;
  342. }
  343. @Override
  344. public boolean onKeyUp (int keyCode, KeyEvent event)
  345. {
  346. handleKeyUp (keyCode, event.getUnicodeChar());
  347. return true;
  348. }
  349. // this is here to make keyboard entry work on a Galaxy Tab2 10.1
  350. @Override
  351. public InputConnection onCreateInputConnection (EditorInfo outAttrs)
  352. {
  353. outAttrs.actionLabel = "";
  354. outAttrs.hintText = "";
  355. outAttrs.initialCapsMode = 0;
  356. outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
  357. outAttrs.label = "";
  358. outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
  359. outAttrs.inputType = InputType.TYPE_NULL;
  360. return new BaseInputConnection (this, false);
  361. }
  362. //==============================================================================
  363. @Override
  364. protected void onSizeChanged (int w, int h, int oldw, int oldh)
  365. {
  366. super.onSizeChanged (w, h, oldw, oldh);
  367. viewSizeChanged();
  368. }
  369. @Override
  370. protected void onLayout (boolean changed, int left, int top, int right, int bottom)
  371. {
  372. for (int i = getChildCount(); --i >= 0;)
  373. requestTransparentRegion (getChildAt (i));
  374. }
  375. private native void viewSizeChanged();
  376. @Override
  377. public void onFocusChange (View v, boolean hasFocus)
  378. {
  379. if (v == this)
  380. focusChanged (hasFocus);
  381. }
  382. private native void focusChanged (boolean hasFocus);
  383. public void setViewName (String newName) {}
  384. public boolean isVisible() { return getVisibility() == VISIBLE; }
  385. public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
  386. public boolean containsPoint (int x, int y)
  387. {
  388. return true; //xxx needs to check overlapping views
  389. }
  390. public final void onPause()
  391. {
  392. for (int i = getChildCount(); --i >= 0;)
  393. {
  394. View v = getChildAt (i);
  395. if (v instanceof OpenGLView)
  396. ((OpenGLView) v).onPause();
  397. }
  398. }
  399. public final void onResume()
  400. {
  401. for (int i = getChildCount(); --i >= 0;)
  402. {
  403. View v = getChildAt (i);
  404. if (v instanceof OpenGLView)
  405. ((OpenGLView) v).onResume();
  406. }
  407. }
  408. public OpenGLView createGLView()
  409. {
  410. OpenGLView glView = new OpenGLView (getContext());
  411. addView (glView);
  412. return glView;
  413. }
  414. }
  415. //==============================================================================
  416. public final class OpenGLView extends GLSurfaceView
  417. implements GLSurfaceView.Renderer
  418. {
  419. OpenGLView (Context context)
  420. {
  421. super (context);
  422. setEGLContextClientVersion (2);
  423. setRenderer (this);
  424. setRenderMode (RENDERMODE_WHEN_DIRTY);
  425. }
  426. @Override
  427. public void onSurfaceCreated (GL10 unused, EGLConfig config)
  428. {
  429. contextCreated();
  430. }
  431. @Override
  432. public void onSurfaceChanged (GL10 unused, int width, int height)
  433. {
  434. contextChangedSize();
  435. }
  436. @Override
  437. public void onDrawFrame (GL10 unused)
  438. {
  439. render();
  440. }
  441. private native void contextCreated();
  442. private native void contextChangedSize();
  443. private native void render();
  444. }
  445. //==============================================================================
  446. public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds)
  447. {
  448. Path p = new Path();
  449. paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p);
  450. RectF boundsF = new RectF();
  451. p.computeBounds (boundsF, true);
  452. matrix.mapRect (boundsF);
  453. boundsF.roundOut (bounds);
  454. bounds.left--;
  455. bounds.right++;
  456. final int w = bounds.width();
  457. final int h = Math.max (1, bounds.height());
  458. Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
  459. Canvas c = new Canvas (bm);
  460. matrix.postTranslate (-bounds.left, -bounds.top);
  461. c.setMatrix (matrix);
  462. c.drawPath (p, paint);
  463. final int sizeNeeded = w * h;
  464. if (cachedRenderArray.length < sizeNeeded)
  465. cachedRenderArray = new int [sizeNeeded];
  466. bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
  467. bm.recycle();
  468. return cachedRenderArray;
  469. }
  470. private int[] cachedRenderArray = new int [256];
  471. //==============================================================================
  472. public static class HTTPStream
  473. {
  474. public HTTPStream (HttpURLConnection connection_) throws IOException
  475. {
  476. connection = connection_;
  477. inputStream = new BufferedInputStream (connection.getInputStream());
  478. }
  479. public final void release()
  480. {
  481. try
  482. {
  483. inputStream.close();
  484. }
  485. catch (IOException e)
  486. {}
  487. connection.disconnect();
  488. }
  489. public final int read (byte[] buffer, int numBytes)
  490. {
  491. int num = 0;
  492. try
  493. {
  494. num = inputStream.read (buffer, 0, numBytes);
  495. }
  496. catch (IOException e)
  497. {}
  498. if (num > 0)
  499. position += num;
  500. return num;
  501. }
  502. public final long getPosition() { return position; }
  503. public final long getTotalLength() { return -1; }
  504. public final boolean isExhausted() { return false; }
  505. public final boolean setPosition (long newPos) { return false; }
  506. private HttpURLConnection connection;
  507. private InputStream inputStream;
  508. private long position;
  509. }
  510. public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
  511. String headers, int timeOutMs,
  512. java.lang.StringBuffer responseHeaders)
  513. {
  514. try
  515. {
  516. HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection());
  517. if (connection != null)
  518. {
  519. try
  520. {
  521. if (isPost)
  522. {
  523. connection.setConnectTimeout (timeOutMs);
  524. connection.setDoOutput (true);
  525. connection.setChunkedStreamingMode (0);
  526. OutputStream out = connection.getOutputStream();
  527. out.write (postData);
  528. out.flush();
  529. }
  530. return new HTTPStream (connection);
  531. }
  532. catch (Throwable e)
  533. {
  534. connection.disconnect();
  535. }
  536. }
  537. }
  538. catch (Throwable e)
  539. {}
  540. return null;
  541. }
  542. public final void launchURL (String url)
  543. {
  544. startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
  545. }
  546. public static final String getLocaleValue (boolean isRegion)
  547. {
  548. java.util.Locale locale = java.util.Locale.getDefault();
  549. return isRegion ? locale.getDisplayCountry (java.util.Locale.US)
  550. : locale.getDisplayLanguage (java.util.Locale.US);
  551. }
  552. //==============================================================================
  553. private final class SingleMediaScanner implements MediaScannerConnectionClient
  554. {
  555. public SingleMediaScanner (Context context, String filename)
  556. {
  557. file = filename;
  558. msc = new MediaScannerConnection (context, this);
  559. msc.connect();
  560. }
  561. @Override
  562. public void onMediaScannerConnected()
  563. {
  564. msc.scanFile (file, null);
  565. }
  566. @Override
  567. public void onScanCompleted (String path, Uri uri)
  568. {
  569. msc.disconnect();
  570. }
  571. private MediaScannerConnection msc;
  572. private String file;
  573. }
  574. public final void scanFile (String filename)
  575. {
  576. new SingleMediaScanner (this, filename);
  577. }
  578. }