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.

503 lines
17KB

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