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.

929 lines
31KB

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