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.

1178 lines
38KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. package com.juce.jucedemo;
  18. import android.app.Activity;
  19. import android.app.AlertDialog;
  20. import android.content.DialogInterface;
  21. import android.content.Context;
  22. import android.content.Intent;
  23. import android.content.res.Configuration;
  24. import android.content.pm.PackageManager;
  25. import android.net.Uri;
  26. import android.os.Bundle;
  27. import android.os.Looper;
  28. import android.os.Handler;
  29. import android.os.Build;
  30. import android.os.Process;
  31. import android.os.ParcelUuid;
  32. import android.view.*;
  33. import android.view.inputmethod.BaseInputConnection;
  34. import android.view.inputmethod.EditorInfo;
  35. import android.view.inputmethod.InputConnection;
  36. import android.view.inputmethod.InputMethodManager;
  37. import android.graphics.*;
  38. import android.text.ClipboardManager;
  39. import android.text.InputType;
  40. import android.util.DisplayMetrics;
  41. import android.util.Log;
  42. import java.lang.Runnable;
  43. import java.util.List;
  44. import java.util.Arrays;
  45. import java.util.ArrayList;
  46. import java.util.HashSet;
  47. import java.util.Hashtable;
  48. import java.util.TimerTask;
  49. import java.io.*;
  50. import java.net.URL;
  51. import java.net.HttpURLConnection;
  52. import android.media.AudioManager;
  53. import android.media.MediaScannerConnection;
  54. import android.media.MediaScannerConnection.MediaScannerConnectionClient;
  55. //==============================================================================
  56. public class JuceDemo extends Activity
  57. {
  58. //==============================================================================
  59. static
  60. {
  61. System.loadLibrary ("juce_jni");
  62. }
  63. //==============================================================================
  64. public static class MidiPortID extends Object
  65. {
  66. public MidiPortID (int index, boolean direction)
  67. {
  68. androidIndex = index;
  69. isInput = direction;
  70. }
  71. public int androidIndex;
  72. public boolean isInput;
  73. @Override
  74. public int hashCode()
  75. {
  76. Integer i = new Integer (androidIndex);
  77. return i.hashCode() * (isInput ? -1 : 1);
  78. }
  79. @Override
  80. public boolean equals (Object obj)
  81. {
  82. if (obj == null)
  83. return false;
  84. if (getClass() != obj.getClass())
  85. return false;
  86. MidiPortID other = (MidiPortID) obj;
  87. return (androidIndex == other.androidIndex && isInput == other.isInput);
  88. }
  89. }
  90. public interface JuceMidiPort
  91. {
  92. boolean isInputPort();
  93. // start, stop does nothing on an output port
  94. void start();
  95. void stop();
  96. void close();
  97. MidiPortID getPortId();
  98. // send will do nothing on an input port
  99. void sendMidi (byte[] msg, int offset, int count);
  100. }
  101. //==============================================================================
  102. //==============================================================================
  103. public class BluetoothManager
  104. {
  105. BluetoothManager()
  106. {
  107. }
  108. public String[] getMidiBluetoothAddresses()
  109. {
  110. String[] bluetoothAddresses = new String[0];
  111. return bluetoothAddresses;
  112. }
  113. public String getHumanReadableStringForBluetoothAddress (String address)
  114. {
  115. return address;
  116. }
  117. public boolean isBluetoothDevicePaired (String address)
  118. {
  119. return false;
  120. }
  121. public boolean pairBluetoothMidiDevice(String address)
  122. {
  123. return false;
  124. }
  125. public void unpairBluetoothMidiDevice (String address)
  126. {
  127. }
  128. }
  129. //==============================================================================
  130. public class MidiDeviceManager
  131. {
  132. public MidiDeviceManager()
  133. {
  134. }
  135. public String[] getJuceAndroidMidiInputDevices()
  136. {
  137. return new String[0];
  138. }
  139. public String[] getJuceAndroidMidiOutputDevices()
  140. {
  141. return new String[0];
  142. }
  143. public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
  144. {
  145. return null;
  146. }
  147. public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
  148. {
  149. return null;
  150. }
  151. public String getInputPortNameForJuceIndex (int index)
  152. {
  153. return "";
  154. }
  155. public String getOutputPortNameForJuceIndex (int index)
  156. {
  157. return "";
  158. }
  159. }
  160. public MidiDeviceManager getAndroidMidiDeviceManager()
  161. {
  162. return null;
  163. }
  164. public BluetoothManager getAndroidBluetoothManager()
  165. {
  166. return null;
  167. }
  168. //==============================================================================
  169. @Override
  170. public void onCreate (Bundle savedInstanceState)
  171. {
  172. super.onCreate (savedInstanceState);
  173. isScreenSaverEnabled = true;
  174. viewHolder = new ViewHolder (this);
  175. setContentView (viewHolder);
  176. setVolumeControlStream (AudioManager.STREAM_MUSIC);
  177. }
  178. @Override
  179. protected void onDestroy()
  180. {
  181. quitApp();
  182. super.onDestroy();
  183. clearDataCache();
  184. }
  185. @Override
  186. protected void onPause()
  187. {
  188. suspendApp();
  189. super.onPause();
  190. }
  191. @Override
  192. protected void onResume()
  193. {
  194. super.onResume();
  195. resumeApp();
  196. }
  197. @Override
  198. public void onConfigurationChanged (Configuration cfg)
  199. {
  200. super.onConfigurationChanged (cfg);
  201. setContentView (viewHolder);
  202. }
  203. private void callAppLauncher()
  204. {
  205. launchApp (getApplicationInfo().publicSourceDir,
  206. getApplicationInfo().dataDir);
  207. }
  208. //==============================================================================
  209. private native void launchApp (String appFile, String appDataDir);
  210. private native void quitApp();
  211. private native void suspendApp();
  212. private native void resumeApp();
  213. private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
  214. //==============================================================================
  215. public native void deliverMessage (long value);
  216. private android.os.Handler messageHandler = new android.os.Handler();
  217. public final void postMessage (long value)
  218. {
  219. messageHandler.post (new MessageCallback (value));
  220. }
  221. private final class MessageCallback implements Runnable
  222. {
  223. public MessageCallback (long value_) { value = value_; }
  224. public final void run() { deliverMessage (value); }
  225. private long value;
  226. }
  227. //==============================================================================
  228. private ViewHolder viewHolder;
  229. private MidiDeviceManager midiDeviceManager = null;
  230. private BluetoothManager bluetoothManager = null;
  231. private boolean isScreenSaverEnabled;
  232. private java.util.Timer keepAliveTimer;
  233. public final ComponentPeerView createNewView (boolean opaque, long host)
  234. {
  235. ComponentPeerView v = new ComponentPeerView (this, opaque, host);
  236. viewHolder.addView (v);
  237. return v;
  238. }
  239. public final void deleteView (ComponentPeerView view)
  240. {
  241. ViewGroup group = (ViewGroup) (view.getParent());
  242. if (group != null)
  243. group.removeView (view);
  244. }
  245. public final void deleteNativeSurfaceView (NativeSurfaceView view)
  246. {
  247. ViewGroup group = (ViewGroup) (view.getParent());
  248. if (group != null)
  249. group.removeView (view);
  250. }
  251. final class ViewHolder extends ViewGroup
  252. {
  253. public ViewHolder (Context context)
  254. {
  255. super (context);
  256. setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
  257. setFocusable (false);
  258. }
  259. protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
  260. {
  261. setScreenSize (getWidth(), getHeight(), getDPI());
  262. if (isFirstResize)
  263. {
  264. isFirstResize = false;
  265. callAppLauncher();
  266. }
  267. }
  268. private final int getDPI()
  269. {
  270. DisplayMetrics metrics = new DisplayMetrics();
  271. getWindowManager().getDefaultDisplay().getMetrics (metrics);
  272. return metrics.densityDpi;
  273. }
  274. private boolean isFirstResize = true;
  275. }
  276. public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
  277. {
  278. canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
  279. }
  280. //==============================================================================
  281. public final void setScreenSaver (boolean enabled)
  282. {
  283. if (isScreenSaverEnabled != enabled)
  284. {
  285. isScreenSaverEnabled = enabled;
  286. if (keepAliveTimer != null)
  287. {
  288. keepAliveTimer.cancel();
  289. keepAliveTimer = null;
  290. }
  291. if (enabled)
  292. {
  293. getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  294. }
  295. else
  296. {
  297. getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  298. // If no user input is received after about 3 seconds, the OS will lower the
  299. // task's priority, so this timer forces it to be kept active.
  300. keepAliveTimer = new java.util.Timer();
  301. keepAliveTimer.scheduleAtFixedRate (new TimerTask()
  302. {
  303. @Override
  304. public void run()
  305. {
  306. android.app.Instrumentation instrumentation = new android.app.Instrumentation();
  307. try
  308. {
  309. instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_BREAK);
  310. }
  311. catch (Exception e)
  312. {
  313. }
  314. }
  315. }, 2000, 2000);
  316. }
  317. }
  318. }
  319. public final boolean getScreenSaver()
  320. {
  321. return isScreenSaverEnabled;
  322. }
  323. //==============================================================================
  324. public final String getClipboardContent()
  325. {
  326. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  327. return clipboard.getText().toString();
  328. }
  329. public final void setClipboardContent (String newText)
  330. {
  331. ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
  332. clipboard.setText (newText);
  333. }
  334. //==============================================================================
  335. public final void showMessageBox (String title, String message, final long callback)
  336. {
  337. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  338. builder.setTitle (title)
  339. .setMessage (message)
  340. .setCancelable (true)
  341. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  342. {
  343. public void onClick (DialogInterface dialog, int id)
  344. {
  345. dialog.cancel();
  346. JuceDemo.this.alertDismissed (callback, 0);
  347. }
  348. });
  349. builder.create().show();
  350. }
  351. public final void showOkCancelBox (String title, String message, final long callback)
  352. {
  353. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  354. builder.setTitle (title)
  355. .setMessage (message)
  356. .setCancelable (true)
  357. .setPositiveButton ("OK", new DialogInterface.OnClickListener()
  358. {
  359. public void onClick (DialogInterface dialog, int id)
  360. {
  361. dialog.cancel();
  362. JuceDemo.this.alertDismissed (callback, 1);
  363. }
  364. })
  365. .setNegativeButton ("Cancel", new DialogInterface.OnClickListener()
  366. {
  367. public void onClick (DialogInterface dialog, int id)
  368. {
  369. dialog.cancel();
  370. JuceDemo.this.alertDismissed (callback, 0);
  371. }
  372. });
  373. builder.create().show();
  374. }
  375. public final void showYesNoCancelBox (String title, String message, final long callback)
  376. {
  377. AlertDialog.Builder builder = new AlertDialog.Builder (this);
  378. builder.setTitle (title)
  379. .setMessage (message)
  380. .setCancelable (true)
  381. .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
  382. {
  383. public void onClick (DialogInterface dialog, int id)
  384. {
  385. dialog.cancel();
  386. JuceDemo.this.alertDismissed (callback, 1);
  387. }
  388. })
  389. .setNegativeButton ("No", new DialogInterface.OnClickListener()
  390. {
  391. public void onClick (DialogInterface dialog, int id)
  392. {
  393. dialog.cancel();
  394. JuceDemo.this.alertDismissed (callback, 2);
  395. }
  396. })
  397. .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
  398. {
  399. public void onClick (DialogInterface dialog, int id)
  400. {
  401. dialog.cancel();
  402. JuceDemo.this.alertDismissed (callback, 0);
  403. }
  404. });
  405. builder.create().show();
  406. }
  407. public native void alertDismissed (long callback, int id);
  408. //==============================================================================
  409. public final class ComponentPeerView extends ViewGroup
  410. implements View.OnFocusChangeListener
  411. {
  412. public ComponentPeerView (Context context, boolean opaque_, long host)
  413. {
  414. super (context);
  415. this.host = host;
  416. setWillNotDraw (false);
  417. opaque = opaque_;
  418. setFocusable (true);
  419. setFocusableInTouchMode (true);
  420. setOnFocusChangeListener (this);
  421. requestFocus();
  422. // swap red and blue colours to match internal opengl texture format
  423. ColorMatrix colorMatrix = new ColorMatrix();
  424. float[] colorTransform = { 0, 0, 1.0f, 0, 0,
  425. 0, 1.0f, 0, 0, 0,
  426. 1.0f, 0, 0, 0, 0,
  427. 0, 0, 0, 1.0f, 0 };
  428. colorMatrix.set (colorTransform);
  429. paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
  430. }
  431. //==============================================================================
  432. private native void handlePaint (long host, Canvas canvas, Paint paint);
  433. @Override
  434. public void onDraw (Canvas canvas)
  435. {
  436. handlePaint (host, canvas, paint);
  437. }
  438. @Override
  439. public boolean isOpaque()
  440. {
  441. return opaque;
  442. }
  443. private boolean opaque;
  444. private long host;
  445. private Paint paint = new Paint();
  446. //==============================================================================
  447. private native void handleMouseDown (long host, int index, float x, float y, long time);
  448. private native void handleMouseDrag (long host, int index, float x, float y, long time);
  449. private native void handleMouseUp (long host, int index, float x, float y, long time);
  450. @Override
  451. public boolean onTouchEvent (MotionEvent event)
  452. {
  453. int action = event.getAction();
  454. long time = event.getEventTime();
  455. switch (action & MotionEvent.ACTION_MASK)
  456. {
  457. case MotionEvent.ACTION_DOWN:
  458. handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time);
  459. return true;
  460. case MotionEvent.ACTION_CANCEL:
  461. case MotionEvent.ACTION_UP:
  462. handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time);
  463. return true;
  464. case MotionEvent.ACTION_MOVE:
  465. {
  466. int n = event.getPointerCount();
  467. for (int i = 0; i < n; ++i)
  468. handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  469. return true;
  470. }
  471. case MotionEvent.ACTION_POINTER_UP:
  472. {
  473. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  474. handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  475. return true;
  476. }
  477. case MotionEvent.ACTION_POINTER_DOWN:
  478. {
  479. int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  480. handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
  481. return true;
  482. }
  483. default:
  484. break;
  485. }
  486. return false;
  487. }
  488. //==============================================================================
  489. private native void handleKeyDown (long host, int keycode, int textchar);
  490. private native void handleKeyUp (long host, int keycode, int textchar);
  491. public void showKeyboard (String type)
  492. {
  493. InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
  494. if (imm != null)
  495. {
  496. if (type.length() > 0)
  497. {
  498. imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
  499. imm.setInputMethod (getWindowToken(), type);
  500. }
  501. else
  502. {
  503. imm.hideSoftInputFromWindow (getWindowToken(), 0);
  504. }
  505. }
  506. }
  507. @Override
  508. public boolean onKeyDown (int keyCode, KeyEvent event)
  509. {
  510. switch (keyCode)
  511. {
  512. case KeyEvent.KEYCODE_VOLUME_UP:
  513. case KeyEvent.KEYCODE_VOLUME_DOWN:
  514. return super.onKeyDown (keyCode, event);
  515. default: break;
  516. }
  517. handleKeyDown (host, keyCode, event.getUnicodeChar());
  518. return true;
  519. }
  520. @Override
  521. public boolean onKeyUp (int keyCode, KeyEvent event)
  522. {
  523. handleKeyUp (host, keyCode, event.getUnicodeChar());
  524. return true;
  525. }
  526. @Override
  527. public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
  528. {
  529. if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
  530. return super.onKeyMultiple (keyCode, count, event);
  531. if (event.getCharacters() != null)
  532. {
  533. int utf8Char = event.getCharacters().codePointAt (0);
  534. handleKeyDown (host, utf8Char, utf8Char);
  535. return true;
  536. }
  537. return false;
  538. }
  539. // this is here to make keyboard entry work on a Galaxy Tab2 10.1
  540. @Override
  541. public InputConnection onCreateInputConnection (EditorInfo outAttrs)
  542. {
  543. outAttrs.actionLabel = "";
  544. outAttrs.hintText = "";
  545. outAttrs.initialCapsMode = 0;
  546. outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
  547. outAttrs.label = "";
  548. outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
  549. outAttrs.inputType = InputType.TYPE_NULL;
  550. return new BaseInputConnection (this, false);
  551. }
  552. //==============================================================================
  553. @Override
  554. protected void onSizeChanged (int w, int h, int oldw, int oldh)
  555. {
  556. super.onSizeChanged (w, h, oldw, oldh);
  557. viewSizeChanged (host);
  558. }
  559. @Override
  560. protected void onLayout (boolean changed, int left, int top, int right, int bottom)
  561. {
  562. for (int i = getChildCount(); --i >= 0;)
  563. requestTransparentRegion (getChildAt (i));
  564. }
  565. private native void viewSizeChanged (long host);
  566. @Override
  567. public void onFocusChange (View v, boolean hasFocus)
  568. {
  569. if (v == this)
  570. focusChanged (host, hasFocus);
  571. }
  572. private native void focusChanged (long host, boolean hasFocus);
  573. public void setViewName (String newName) {}
  574. public boolean isVisible() { return getVisibility() == VISIBLE; }
  575. public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
  576. public boolean containsPoint (int x, int y)
  577. {
  578. return true; //xxx needs to check overlapping views
  579. }
  580. }
  581. //==============================================================================
  582. public static class NativeSurfaceView extends SurfaceView
  583. implements SurfaceHolder.Callback
  584. {
  585. private long nativeContext = 0;
  586. NativeSurfaceView (Context context, long nativeContextPtr)
  587. {
  588. super (context);
  589. nativeContext = nativeContextPtr;
  590. }
  591. public Surface getNativeSurface()
  592. {
  593. Surface retval = null;
  594. SurfaceHolder holder = getHolder();
  595. if (holder != null)
  596. retval = holder.getSurface();
  597. return retval;
  598. }
  599. //==============================================================================
  600. @Override
  601. public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
  602. {
  603. surfaceChangedNative (nativeContext, holder, format, width, height);
  604. }
  605. @Override
  606. public void surfaceCreated (SurfaceHolder holder)
  607. {
  608. surfaceCreatedNative (nativeContext, holder);
  609. }
  610. @Override
  611. public void surfaceDestroyed (SurfaceHolder holder)
  612. {
  613. surfaceDestroyedNative (nativeContext, holder);
  614. }
  615. @Override
  616. protected void dispatchDraw (Canvas canvas)
  617. {
  618. super.dispatchDraw (canvas);
  619. dispatchDrawNative (nativeContext, canvas);
  620. }
  621. //==============================================================================
  622. @Override
  623. protected void onAttachedToWindow ()
  624. {
  625. super.onAttachedToWindow();
  626. getHolder().addCallback (this);
  627. }
  628. @Override
  629. protected void onDetachedFromWindow ()
  630. {
  631. super.onDetachedFromWindow();
  632. getHolder().removeCallback (this);
  633. }
  634. //==============================================================================
  635. private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
  636. private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
  637. private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
  638. private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
  639. int format, int width, int height);
  640. }
  641. public NativeSurfaceView createNativeSurfaceView(long nativeSurfacePtr)
  642. {
  643. return new NativeSurfaceView (this, nativeSurfacePtr);
  644. }
  645. //==============================================================================
  646. public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds)
  647. {
  648. Path p = new Path();
  649. paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p);
  650. RectF boundsF = new RectF();
  651. p.computeBounds (boundsF, true);
  652. matrix.mapRect (boundsF);
  653. boundsF.roundOut (bounds);
  654. bounds.left--;
  655. bounds.right++;
  656. final int w = bounds.width();
  657. final int h = Math.max (1, bounds.height());
  658. Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
  659. Canvas c = new Canvas (bm);
  660. matrix.postTranslate (-bounds.left, -bounds.top);
  661. c.setMatrix (matrix);
  662. c.drawPath (p, paint);
  663. final int sizeNeeded = w * h;
  664. if (cachedRenderArray.length < sizeNeeded)
  665. cachedRenderArray = new int [sizeNeeded];
  666. bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
  667. bm.recycle();
  668. return cachedRenderArray;
  669. }
  670. private int[] cachedRenderArray = new int [256];
  671. //==============================================================================
  672. public static class HTTPStream
  673. {
  674. public HTTPStream (HttpURLConnection connection_,
  675. int[] statusCode, StringBuffer responseHeaders) throws IOException
  676. {
  677. connection = connection_;
  678. try
  679. {
  680. inputStream = new BufferedInputStream (connection.getInputStream());
  681. }
  682. catch (IOException e)
  683. {
  684. if (connection.getResponseCode() < 400)
  685. throw e;
  686. }
  687. finally
  688. {
  689. statusCode[0] = connection.getResponseCode();
  690. }
  691. if (statusCode[0] >= 400)
  692. inputStream = connection.getErrorStream();
  693. else
  694. inputStream = connection.getInputStream();
  695. for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
  696. if (entry.getKey() != null && entry.getValue() != null)
  697. responseHeaders.append (entry.getKey() + ": "
  698. + android.text.TextUtils.join (",", entry.getValue()) + "\n");
  699. }
  700. public final void release()
  701. {
  702. try
  703. {
  704. inputStream.close();
  705. }
  706. catch (IOException e)
  707. {}
  708. connection.disconnect();
  709. }
  710. public final int read (byte[] buffer, int numBytes)
  711. {
  712. int num = 0;
  713. try
  714. {
  715. num = inputStream.read (buffer, 0, numBytes);
  716. }
  717. catch (IOException e)
  718. {}
  719. if (num > 0)
  720. position += num;
  721. return num;
  722. }
  723. public final long getPosition() { return position; }
  724. public final long getTotalLength() { return -1; }
  725. public final boolean isExhausted() { return false; }
  726. public final boolean setPosition (long newPos) { return false; }
  727. private HttpURLConnection connection;
  728. private InputStream inputStream;
  729. private long position;
  730. }
  731. public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
  732. String headers, int timeOutMs, int[] statusCode,
  733. StringBuffer responseHeaders, int numRedirectsToFollow,
  734. String httpRequestCmd)
  735. {
  736. // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
  737. if (timeOutMs < 0)
  738. timeOutMs = 0;
  739. else if (timeOutMs == 0)
  740. timeOutMs = 30000;
  741. // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
  742. // So convert headers string to an array, with an element for each line
  743. String headerLines[] = headers.split("\\n");
  744. for (;;)
  745. {
  746. try
  747. {
  748. HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection());
  749. if (connection != null)
  750. {
  751. try
  752. {
  753. connection.setInstanceFollowRedirects (false);
  754. connection.setConnectTimeout (timeOutMs);
  755. connection.setReadTimeout (timeOutMs);
  756. // Set request headers
  757. for (int i = 0; i < headerLines.length; ++i)
  758. {
  759. int pos = headerLines[i].indexOf (":");
  760. if (pos > 0 && pos < headerLines[i].length())
  761. {
  762. String field = headerLines[i].substring (0, pos);
  763. String value = headerLines[i].substring (pos + 1);
  764. if (value.length() > 0)
  765. connection.setRequestProperty (field, value);
  766. }
  767. }
  768. connection.setRequestMethod (httpRequestCmd);
  769. if (isPost)
  770. {
  771. connection.setDoOutput (true);
  772. if (postData != null)
  773. {
  774. OutputStream out = connection.getOutputStream();
  775. out.write(postData);
  776. out.flush();
  777. }
  778. }
  779. HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders);
  780. // Process redirect & continue as necessary
  781. int status = statusCode[0];
  782. if (--numRedirectsToFollow >= 0
  783. && (status == 301 || status == 302 || status == 303 || status == 307))
  784. {
  785. // Assumes only one occurrence of "Location"
  786. int pos1 = responseHeaders.indexOf ("Location:") + 10;
  787. int pos2 = responseHeaders.indexOf ("\n", pos1);
  788. if (pos2 > pos1)
  789. {
  790. String newLocation = responseHeaders.substring(pos1, pos2);
  791. // Handle newLocation whether it's absolute or relative
  792. URL baseUrl = new URL (address);
  793. URL newUrl = new URL (baseUrl, newLocation);
  794. String transformedNewLocation = newUrl.toString();
  795. if (transformedNewLocation != address)
  796. {
  797. address = transformedNewLocation;
  798. // Clear responseHeaders before next iteration
  799. responseHeaders.delete (0, responseHeaders.length());
  800. continue;
  801. }
  802. }
  803. }
  804. return httpStream;
  805. }
  806. catch (Throwable e)
  807. {
  808. connection.disconnect();
  809. }
  810. }
  811. }
  812. catch (Throwable e) {}
  813. return null;
  814. }
  815. }
  816. public final void launchURL (String url)
  817. {
  818. startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
  819. }
  820. public static final String getLocaleValue (boolean isRegion)
  821. {
  822. java.util.Locale locale = java.util.Locale.getDefault();
  823. return isRegion ? locale.getDisplayCountry (java.util.Locale.US)
  824. : locale.getDisplayLanguage (java.util.Locale.US);
  825. }
  826. //==============================================================================
  827. private final class SingleMediaScanner implements MediaScannerConnectionClient
  828. {
  829. public SingleMediaScanner (Context context, String filename)
  830. {
  831. file = filename;
  832. msc = new MediaScannerConnection (context, this);
  833. msc.connect();
  834. }
  835. @Override
  836. public void onMediaScannerConnected()
  837. {
  838. msc.scanFile (file, null);
  839. }
  840. @Override
  841. public void onScanCompleted (String path, Uri uri)
  842. {
  843. msc.disconnect();
  844. }
  845. private MediaScannerConnection msc;
  846. private String file;
  847. }
  848. public final void scanFile (String filename)
  849. {
  850. new SingleMediaScanner (this, filename);
  851. }
  852. public final Typeface getTypeFaceFromAsset (String assetName)
  853. {
  854. try
  855. {
  856. return Typeface.createFromAsset (this.getResources().getAssets(), assetName);
  857. }
  858. catch (Throwable e) {}
  859. return null;
  860. }
  861. final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
  862. public static String bytesToHex (byte[] bytes)
  863. {
  864. char[] hexChars = new char[bytes.length * 2];
  865. for (int j = 0; j < bytes.length; ++j)
  866. {
  867. int v = bytes[j] & 0xff;
  868. hexChars[j * 2] = hexArray[v >>> 4];
  869. hexChars[j * 2 + 1] = hexArray[v & 0x0f];
  870. }
  871. return new String (hexChars);
  872. }
  873. final private java.util.Map dataCache = new java.util.HashMap();
  874. synchronized private final File getDataCacheFile (byte[] data)
  875. {
  876. try
  877. {
  878. java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5");
  879. digest.update (data);
  880. String key = bytesToHex (digest.digest());
  881. if (dataCache.containsKey (key))
  882. return (File) dataCache.get (key);
  883. File f = new File (this.getCacheDir(), "bindata_" + key);
  884. f.delete();
  885. FileOutputStream os = new FileOutputStream (f);
  886. os.write (data, 0, data.length);
  887. dataCache.put (key, f);
  888. return f;
  889. }
  890. catch (Throwable e) {}
  891. return null;
  892. }
  893. private final void clearDataCache()
  894. {
  895. java.util.Iterator it = dataCache.values().iterator();
  896. while (it.hasNext())
  897. {
  898. File f = (File) it.next();
  899. f.delete();
  900. }
  901. }
  902. public final Typeface getTypeFaceFromByteArray (byte[] data)
  903. {
  904. try
  905. {
  906. File f = getDataCacheFile (data);
  907. if (f != null)
  908. return Typeface.createFromFile (f);
  909. }
  910. catch (Exception e)
  911. {
  912. Log.e ("JUCE", e.toString());
  913. }
  914. return null;
  915. }
  916. public final int getAndroidSDKVersion()
  917. {
  918. return android.os.Build.VERSION.SDK_INT;
  919. }
  920. public final String audioManagerGetProperty (String property)
  921. {
  922. Object obj = getSystemService (AUDIO_SERVICE);
  923. if (obj == null)
  924. return null;
  925. java.lang.reflect.Method method;
  926. try {
  927. method = obj.getClass().getMethod ("getProperty", String.class);
  928. } catch (SecurityException e) {
  929. return null;
  930. } catch (NoSuchMethodException e) {
  931. return null;
  932. }
  933. if (method == null)
  934. return null;
  935. try {
  936. return (String) method.invoke (obj, property);
  937. } catch (java.lang.IllegalArgumentException e) {
  938. } catch (java.lang.IllegalAccessException e) {
  939. } catch (java.lang.reflect.InvocationTargetException e) {
  940. }
  941. return null;
  942. }
  943. public final int setCurrentThreadPriority (int priority)
  944. {
  945. android.os.Process.setThreadPriority (android.os.Process.myTid(), priority);
  946. return android.os.Process.getThreadPriority (android.os.Process.myTid());
  947. }
  948. public final boolean hasSystemFeature (String property)
  949. {
  950. return getPackageManager().hasSystemFeature (property);
  951. }
  952. private static class JuceThread extends Thread
  953. {
  954. public JuceThread (long host)
  955. {
  956. _this = host;
  957. }
  958. public void run()
  959. {
  960. runThread(_this);
  961. }
  962. private native void runThread (long host);
  963. private long _this;
  964. }
  965. public final Thread createNewThread(long host)
  966. {
  967. return new JuceThread(host);
  968. }
  969. }