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.

988 lines
33KB

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