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.

596 lines
20KB

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