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.

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