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.

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