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.

647 lines
22KB

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