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.

948 lines
32KB

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