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.

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