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.

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