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.

650 lines
22KB

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