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.

531 lines
18KB

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