2 ==============================================================================
\r
4 This file is part of the JUCE library.
\r
5 Copyright (c) 2017 - ROLI Ltd.
\r
7 JUCE is an open source library subject to commercial or open-source
\r
10 The code included in this file is provided under the terms of the ISC license
\r
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
\r
12 To use, copy, modify, and/or distribute this software for any purpose with or
\r
13 without fee is hereby granted provided that the above copyright notice and
\r
14 this permission notice appear in all copies.
\r
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
\r
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
\r
20 ==============================================================================
\r
23 package com.juce.networkgraphicsdemo;
\r
25 import android.app.Activity;
\r
26 import android.app.AlertDialog;
\r
27 import android.content.DialogInterface;
\r
28 import android.content.Context;
\r
29 import android.content.Intent;
\r
30 import android.content.res.Configuration;
\r
31 import android.content.pm.PackageInfo;
\r
32 import android.content.pm.PackageManager;
\r
33 import android.net.http.SslError;
\r
34 import android.net.Uri;
\r
35 import android.os.Bundle;
\r
36 import android.os.Looper;
\r
37 import android.os.Handler;
\r
38 import android.os.Message;
\r
39 import android.os.ParcelUuid;
\r
40 import android.os.Environment;
\r
41 import android.view.*;
\r
42 import android.view.inputmethod.BaseInputConnection;
\r
43 import android.view.inputmethod.EditorInfo;
\r
44 import android.view.inputmethod.InputConnection;
\r
45 import android.view.inputmethod.InputMethodManager;
\r
46 import android.graphics.*;
\r
47 import android.text.ClipboardManager;
\r
48 import android.text.InputType;
\r
49 import android.util.DisplayMetrics;
\r
50 import android.util.Log;
\r
51 import android.util.Pair;
\r
52 import android.webkit.SslErrorHandler;
\r
53 import android.webkit.WebChromeClient;
\r
54 import android.webkit.WebView;
\r
55 import android.webkit.WebViewClient;
\r
56 import java.lang.Runnable;
\r
57 import java.lang.ref.WeakReference;
\r
58 import java.lang.reflect.*;
\r
61 import java.net.URL;
\r
62 import java.net.HttpURLConnection;
\r
63 import android.media.AudioManager;
\r
64 import android.Manifest;
\r
65 import java.util.concurrent.CancellationException;
\r
66 import java.util.concurrent.Future;
\r
67 import java.util.concurrent.Executors;
\r
68 import java.util.concurrent.ExecutorService;
\r
69 import java.util.concurrent.ExecutionException;
\r
70 import java.util.concurrent.TimeUnit;
\r
71 import java.util.concurrent.Callable;
\r
72 import java.util.concurrent.TimeoutException;
\r
73 import java.util.concurrent.locks.ReentrantLock;
\r
74 import java.util.concurrent.atomic.*;
\r
78 //==============================================================================
\r
79 public class JUCENetworkGraphicsDemo extends Activity
\r
81 //==============================================================================
\r
84 System.loadLibrary ("juce_jni");
\r
87 //==============================================================================
\r
88 public boolean isPermissionDeclaredInManifest (int permissionID)
\r
90 String permissionToCheck = getAndroidPermissionName(permissionID);
\r
94 PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
\r
96 if (info.requestedPermissions != null)
\r
97 for (String permission : info.requestedPermissions)
\r
98 if (permission.equals (permissionToCheck))
\r
101 catch (PackageManager.NameNotFoundException e)
\r
103 Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString());
\r
106 Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck);
\r
110 //==============================================================================
\r
111 // these have to match the values of enum PermissionID in C++ class RuntimePermissions:
\r
112 private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1;
\r
113 private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2;
\r
114 private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3;
\r
115 private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4;
\r
116 private static final int JUCE_PERMISSIONS_CAMERA = 5;
\r
118 private static String getAndroidPermissionName (int permissionID)
\r
120 switch (permissionID)
\r
122 case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO;
\r
123 case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION;
\r
124 // use string value as this is not defined in SDKs < 16
\r
125 case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE";
\r
126 case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE;
\r
127 case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA;
\r
130 // unknown permission ID!
\r
132 return new String();
\r
135 public boolean isPermissionGranted (int permissionID)
\r
137 return getApplicationContext().checkCallingOrSelfPermission (getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED;
\r
140 private Map<Integer, Long> permissionCallbackPtrMap;
\r
142 public void requestRuntimePermission (int permissionID, long ptrToCallback)
\r
144 String permissionName = getAndroidPermissionName (permissionID);
\r
146 if (getApplicationContext().checkCallingOrSelfPermission (permissionName) != PackageManager.PERMISSION_GRANTED)
\r
148 // remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously
\r
149 permissionCallbackPtrMap.put (permissionID, ptrToCallback);
\r
150 requestPermissionsCompat (new String[]{permissionName}, permissionID);
\r
154 // permissions were already granted before, we can call callback directly
\r
155 androidRuntimePermissionsCallback (true, ptrToCallback);
\r
159 private native void androidRuntimePermissionsCallback (boolean permissionWasGranted, long ptrToCallback);
\r
162 //==============================================================================
\r
163 public interface JuceMidiPort
\r
165 boolean isInputPort();
\r
167 // start, stop does nothing on an output port
\r
173 // send will do nothing on an input port
\r
174 void sendMidi (byte[] msg, int offset, int count);
\r
177 //==============================================================================
\r
178 //==============================================================================
\r
179 public class BluetoothManager
\r
185 public String[] getMidiBluetoothAddresses()
\r
187 String[] bluetoothAddresses = new String[0];
\r
188 return bluetoothAddresses;
\r
191 public String getHumanReadableStringForBluetoothAddress (String address)
\r
196 public int getBluetoothDeviceStatus (String address)
\r
201 public void startStopScan (boolean shouldStart)
\r
205 public boolean pairBluetoothMidiDevice(String address)
\r
210 public void unpairBluetoothMidiDevice (String address)
\r
215 //==============================================================================
\r
216 public class MidiDeviceManager
\r
218 public MidiDeviceManager()
\r
222 public String[] getJuceAndroidMidiInputDevices()
\r
224 return new String[0];
\r
227 public String[] getJuceAndroidMidiOutputDevices()
\r
229 return new String[0];
\r
232 public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
\r
237 public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
\r
242 public String getInputPortNameForJuceIndex (int index)
\r
247 public String getOutputPortNameForJuceIndex (int index)
\r
254 public MidiDeviceManager getAndroidMidiDeviceManager()
\r
259 public BluetoothManager getAndroidBluetoothManager()
\r
264 //==============================================================================
\r
266 public void onCreate (Bundle savedInstanceState)
\r
268 super.onCreate (savedInstanceState);
\r
270 isScreenSaverEnabled = true;
\r
272 viewHolder = new ViewHolder (this);
\r
273 setContentView (viewHolder);
\r
275 setVolumeControlStream (AudioManager.STREAM_MUSIC);
\r
277 permissionCallbackPtrMap = new HashMap<Integer, Long>();
\r
278 appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>();
\r
282 protected void onDestroy()
\r
291 protected void onPause()
\r
295 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
297 for (Long k : keys)
\r
298 appPausedResumedListeners.get (k).appPaused();
\r
302 Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down
\r
303 // openGL glitches when pausing/resuming apps..
\r
304 } catch (InterruptedException e) {}
\r
310 protected void onResume()
\r
315 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
317 for (Long k : keys)
\r
318 appPausedResumedListeners.get (k).appResumed();
\r
322 public void onConfigurationChanged (Configuration cfg)
\r
324 super.onConfigurationChanged (cfg);
\r
325 setContentView (viewHolder);
\r
328 private void callAppLauncher()
\r
330 launchApp (getApplicationInfo().publicSourceDir,
\r
331 getApplicationInfo().dataDir);
\r
334 // Need to override this as the default implementation always finishes the activity.
\r
336 public void onBackPressed()
\r
338 ComponentPeerView focusedView = getViewWithFocusOrDefaultView();
\r
340 if (focusedView == null)
\r
343 focusedView.backButtonPressed();
\r
346 private ComponentPeerView getViewWithFocusOrDefaultView()
\r
348 for (int i = 0; i < viewHolder.getChildCount(); ++i)
\r
350 if (viewHolder.getChildAt (i).hasFocus())
\r
351 return (ComponentPeerView) viewHolder.getChildAt (i);
\r
354 if (viewHolder.getChildCount() > 0)
\r
355 return (ComponentPeerView) viewHolder.getChildAt (0);
\r
360 //==============================================================================
\r
361 private void hideActionBar()
\r
363 // get "getActionBar" method
\r
364 java.lang.reflect.Method getActionBarMethod = null;
\r
367 getActionBarMethod = this.getClass().getMethod ("getActionBar");
\r
369 catch (SecurityException e) { return; }
\r
370 catch (NoSuchMethodException e) { return; }
\r
371 if (getActionBarMethod == null) return;
\r
373 // invoke "getActionBar" method
\r
374 Object actionBar = null;
\r
377 actionBar = getActionBarMethod.invoke (this);
\r
379 catch (java.lang.IllegalArgumentException e) { return; }
\r
380 catch (java.lang.IllegalAccessException e) { return; }
\r
381 catch (java.lang.reflect.InvocationTargetException e) { return; }
\r
382 if (actionBar == null) return;
\r
384 // get "hide" method
\r
385 java.lang.reflect.Method actionBarHideMethod = null;
\r
388 actionBarHideMethod = actionBar.getClass().getMethod ("hide");
\r
390 catch (SecurityException e) { return; }
\r
391 catch (NoSuchMethodException e) { return; }
\r
392 if (actionBarHideMethod == null) return;
\r
394 // invoke "hide" method
\r
397 actionBarHideMethod.invoke (actionBar);
\r
399 catch (java.lang.IllegalArgumentException e) {}
\r
400 catch (java.lang.IllegalAccessException e) {}
\r
401 catch (java.lang.reflect.InvocationTargetException e) {}
\r
404 void requestPermissionsCompat (String[] permissions, int requestCode)
\r
406 Method requestPermissionsMethod = null;
\r
409 requestPermissionsMethod = this.getClass().getMethod ("requestPermissions",
\r
410 String[].class, int.class);
\r
412 catch (SecurityException e) { return; }
\r
413 catch (NoSuchMethodException e) { return; }
\r
414 if (requestPermissionsMethod == null) return;
\r
418 requestPermissionsMethod.invoke (this, permissions, requestCode);
\r
420 catch (java.lang.IllegalArgumentException e) {}
\r
421 catch (java.lang.IllegalAccessException e) {}
\r
422 catch (java.lang.reflect.InvocationTargetException e) {}
\r
425 //==============================================================================
\r
426 private native void launchApp (String appFile, String appDataDir);
\r
427 private native void quitApp();
\r
428 private native void suspendApp();
\r
429 private native void resumeApp();
\r
430 private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
\r
431 private native void appActivityResult (int requestCode, int resultCode, Intent data);
\r
432 private native void appNewIntent (Intent intent);
\r
434 //==============================================================================
\r
435 private ViewHolder viewHolder;
\r
436 private MidiDeviceManager midiDeviceManager = null;
\r
437 private BluetoothManager bluetoothManager = null;
\r
438 private boolean isScreenSaverEnabled;
\r
439 private java.util.Timer keepAliveTimer;
\r
441 public final ComponentPeerView createNewView (boolean opaque, long host)
\r
443 ComponentPeerView v = new ComponentPeerView (this, opaque, host);
\r
444 viewHolder.addView (v);
\r
445 addAppPausedResumedListener (v, host);
\r
449 public final void deleteView (ComponentPeerView view)
\r
451 removeAppPausedResumedListener (view, view.host);
\r
455 ViewGroup group = (ViewGroup) (view.getParent());
\r
458 group.removeView (view);
\r
461 public final void deleteNativeSurfaceView (NativeSurfaceView view)
\r
463 ViewGroup group = (ViewGroup) (view.getParent());
\r
466 group.removeView (view);
\r
469 final class ViewHolder extends ViewGroup
\r
471 public ViewHolder (Context context)
\r
474 setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
\r
475 setFocusable (false);
\r
478 protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
480 setScreenSize (getWidth(), getHeight(), getDPI());
\r
484 isFirstResize = false;
\r
489 private final int getDPI()
\r
491 DisplayMetrics metrics = new DisplayMetrics();
\r
492 getWindowManager().getDefaultDisplay().getMetrics (metrics);
\r
493 return metrics.densityDpi;
\r
496 private boolean isFirstResize = true;
\r
499 public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
\r
501 canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
\r
504 //==============================================================================
\r
505 public final void setScreenSaver (boolean enabled)
\r
507 if (isScreenSaverEnabled != enabled)
\r
509 isScreenSaverEnabled = enabled;
\r
511 if (keepAliveTimer != null)
\r
513 keepAliveTimer.cancel();
\r
514 keepAliveTimer = null;
\r
519 getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
523 getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
525 // If no user input is received after about 3 seconds, the OS will lower the
\r
526 // task's priority, so this timer forces it to be kept active.
\r
527 keepAliveTimer = new java.util.Timer();
\r
529 keepAliveTimer.scheduleAtFixedRate (new TimerTask()
\r
534 android.app.Instrumentation instrumentation = new android.app.Instrumentation();
\r
538 instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_UNKNOWN);
\r
540 catch (Exception e)
\r
549 public final boolean getScreenSaver()
\r
551 return isScreenSaverEnabled;
\r
554 //==============================================================================
\r
555 public final String getClipboardContent()
\r
557 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
558 return clipboard.getText().toString();
\r
561 public final void setClipboardContent (String newText)
\r
563 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
564 clipboard.setText (newText);
\r
567 //==============================================================================
\r
568 public final void showMessageBox (String title, String message, final long callback)
\r
570 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
571 builder.setTitle (title)
\r
572 .setMessage (message)
\r
573 .setCancelable (true)
\r
574 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
576 public void onCancel (DialogInterface dialog)
\r
578 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
581 .setPositiveButton ("OK", new DialogInterface.OnClickListener()
\r
583 public void onClick (DialogInterface dialog, int id)
\r
586 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
590 builder.create().show();
\r
593 public final void showOkCancelBox (String title, String message, final long callback,
\r
594 String okButtonText, String cancelButtonText)
\r
596 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
597 builder.setTitle (title)
\r
598 .setMessage (message)
\r
599 .setCancelable (true)
\r
600 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
602 public void onCancel (DialogInterface dialog)
\r
604 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
607 .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener()
\r
609 public void onClick (DialogInterface dialog, int id)
\r
612 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1);
\r
615 .setNegativeButton (cancelButtonText.isEmpty() ? "Cancel" : cancelButtonText, new DialogInterface.OnClickListener()
\r
617 public void onClick (DialogInterface dialog, int id)
\r
620 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
624 builder.create().show();
\r
627 public final void showYesNoCancelBox (String title, String message, final long callback)
\r
629 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
630 builder.setTitle (title)
\r
631 .setMessage (message)
\r
632 .setCancelable (true)
\r
633 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
635 public void onCancel (DialogInterface dialog)
\r
637 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
640 .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
\r
642 public void onClick (DialogInterface dialog, int id)
\r
645 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1);
\r
648 .setNegativeButton ("No", new DialogInterface.OnClickListener()
\r
650 public void onClick (DialogInterface dialog, int id)
\r
653 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 2);
\r
656 .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
\r
658 public void onClick (DialogInterface dialog, int id)
\r
661 JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0);
\r
665 builder.create().show();
\r
668 public native void alertDismissed (long callback, int id);
\r
670 //==============================================================================
\r
671 public interface AppPausedResumedListener
\r
677 private Map<Long, AppPausedResumedListener> appPausedResumedListeners;
\r
679 public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
681 appPausedResumedListeners.put (new Long (listenerHost), l);
\r
684 public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
686 appPausedResumedListeners.remove (new Long (listenerHost));
\r
689 //==============================================================================
\r
690 public final class ComponentPeerView extends ViewGroup
\r
691 implements View.OnFocusChangeListener, AppPausedResumedListener
\r
693 public ComponentPeerView (Context context, boolean opaque_, long host)
\r
697 setWillNotDraw (false);
\r
700 setFocusable (true);
\r
701 setFocusableInTouchMode (true);
\r
702 setOnFocusChangeListener (this);
\r
704 // swap red and blue colours to match internal opengl texture format
\r
705 ColorMatrix colorMatrix = new ColorMatrix();
\r
707 float[] colorTransform = { 0, 0, 1.0f, 0, 0,
\r
710 0, 0, 0, 1.0f, 0 };
\r
712 colorMatrix.set (colorTransform);
\r
713 paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
\r
715 java.lang.reflect.Method method = null;
\r
719 method = getClass().getMethod ("setLayerType", int.class, Paint.class);
\r
721 catch (SecurityException e) {}
\r
722 catch (NoSuchMethodException e) {}
\r
724 if (method != null)
\r
728 int layerTypeNone = 0;
\r
729 method.invoke (this, layerTypeNone, null);
\r
731 catch (java.lang.IllegalArgumentException e) {}
\r
732 catch (java.lang.IllegalAccessException e) {}
\r
733 catch (java.lang.reflect.InvocationTargetException e) {}
\r
737 //==============================================================================
\r
738 private native void handlePaint (long host, Canvas canvas, Paint paint);
\r
741 public void onDraw (Canvas canvas)
\r
746 handlePaint (host, canvas, paint);
\r
750 public boolean isOpaque()
\r
755 private boolean opaque;
\r
757 private Paint paint = new Paint();
\r
759 //==============================================================================
\r
760 private native void handleMouseDown (long host, int index, float x, float y, long time);
\r
761 private native void handleMouseDrag (long host, int index, float x, float y, long time);
\r
762 private native void handleMouseUp (long host, int index, float x, float y, long time);
\r
765 public boolean onTouchEvent (MotionEvent event)
\r
770 int action = event.getAction();
\r
771 long time = event.getEventTime();
\r
773 switch (action & MotionEvent.ACTION_MASK)
\r
775 case MotionEvent.ACTION_DOWN:
\r
776 handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
779 case MotionEvent.ACTION_CANCEL:
\r
780 case MotionEvent.ACTION_UP:
\r
781 handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
784 case MotionEvent.ACTION_MOVE:
\r
786 int n = event.getPointerCount();
\r
787 for (int i = 0; i < n; ++i)
\r
788 handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
793 case MotionEvent.ACTION_POINTER_UP:
\r
795 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
796 handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
800 case MotionEvent.ACTION_POINTER_DOWN:
\r
802 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
803 handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
814 //==============================================================================
\r
815 private native void handleKeyDown (long host, int keycode, int textchar);
\r
816 private native void handleKeyUp (long host, int keycode, int textchar);
\r
817 private native void handleBackButton (long host);
\r
818 private native void handleKeyboardHidden (long host);
\r
820 public void showKeyboard (String type)
\r
822 InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
\r
826 if (type.length() > 0)
\r
828 imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
\r
829 imm.setInputMethod (getWindowToken(), type);
\r
830 keyboardDismissListener.startListening();
\r
834 imm.hideSoftInputFromWindow (getWindowToken(), 0);
\r
835 keyboardDismissListener.stopListening();
\r
840 public void backButtonPressed()
\r
845 handleBackButton (host);
\r
849 public boolean onKeyDown (int keyCode, KeyEvent event)
\r
856 case KeyEvent.KEYCODE_VOLUME_UP:
\r
857 case KeyEvent.KEYCODE_VOLUME_DOWN:
\r
858 return super.onKeyDown (keyCode, event);
\r
859 case KeyEvent.KEYCODE_BACK:
\r
861 ((Activity) getContext()).onBackPressed();
\r
869 handleKeyDown (host, keyCode, event.getUnicodeChar());
\r
874 public boolean onKeyUp (int keyCode, KeyEvent event)
\r
879 handleKeyUp (host, keyCode, event.getUnicodeChar());
\r
884 public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
\r
889 if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
\r
890 return super.onKeyMultiple (keyCode, count, event);
\r
892 if (event.getCharacters() != null)
\r
894 int utf8Char = event.getCharacters().codePointAt (0);
\r
895 handleKeyDown (host, utf8Char, utf8Char);
\r
902 //==============================================================================
\r
903 private final class KeyboardDismissListener
\r
905 public KeyboardDismissListener (ComponentPeerView viewToUse)
\r
910 private void startListening()
\r
912 view.getViewTreeObserver().addOnGlobalLayoutListener(viewTreeObserver);
\r
915 private void stopListening()
\r
917 view.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserver);
\r
920 private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
\r
924 keyboardShown = false;
\r
928 public void onGlobalLayout()
\r
930 Rect r = new Rect();
\r
932 ViewGroup parentView = (ViewGroup) getParent();
\r
934 if (parentView == null)
\r
937 parentView.getWindowVisibleDisplayFrame (r);
\r
939 int diff = parentView.getHeight() - (r.bottom - r.top);
\r
941 // Arbitrary threshold, surely keyboard would take more than 20 pix.
\r
942 if (diff < 20 && keyboardShown)
\r
944 keyboardShown = false;
\r
945 handleKeyboardHidden (view.host);
\r
948 if (! keyboardShown && diff > 20)
\r
949 keyboardShown = true;
\r
952 private boolean keyboardShown;
\r
955 private ComponentPeerView view;
\r
956 private TreeObserver viewTreeObserver = new TreeObserver();
\r
959 private KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener(this);
\r
961 // this is here to make keyboard entry work on a Galaxy Tab2 10.1
\r
963 public InputConnection onCreateInputConnection (EditorInfo outAttrs)
\r
965 outAttrs.actionLabel = "";
\r
966 outAttrs.hintText = "";
\r
967 outAttrs.initialCapsMode = 0;
\r
968 outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
\r
969 outAttrs.label = "";
\r
970 outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
\r
971 outAttrs.inputType = InputType.TYPE_NULL;
\r
973 return new BaseInputConnection (this, false);
\r
976 //==============================================================================
\r
978 protected void onSizeChanged (int w, int h, int oldw, int oldh)
\r
983 super.onSizeChanged (w, h, oldw, oldh);
\r
984 viewSizeChanged (host);
\r
988 protected void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
990 for (int i = getChildCount(); --i >= 0;)
\r
991 requestTransparentRegion (getChildAt (i));
\r
994 private native void viewSizeChanged (long host);
\r
997 public void onFocusChange (View v, boolean hasFocus)
\r
1003 focusChanged (host, hasFocus);
\r
1006 private native void focusChanged (long host, boolean hasFocus);
\r
1008 public void setViewName (String newName) {}
\r
1010 public void setSystemUiVisibilityCompat (int visibility)
\r
1012 Method systemUIVisibilityMethod = null;
\r
1015 systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class);
\r
1017 catch (SecurityException e) { return; }
\r
1018 catch (NoSuchMethodException e) { return; }
\r
1019 if (systemUIVisibilityMethod == null) return;
\r
1023 systemUIVisibilityMethod.invoke (this, visibility);
\r
1025 catch (java.lang.IllegalArgumentException e) {}
\r
1026 catch (java.lang.IllegalAccessException e) {}
\r
1027 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1030 public boolean isVisible() { return getVisibility() == VISIBLE; }
\r
1031 public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
\r
1033 public boolean containsPoint (int x, int y)
\r
1035 return true; //xxx needs to check overlapping views
\r
1038 //==============================================================================
\r
1039 private native void handleAppPaused (long host);
\r
1040 private native void handleAppResumed (long host);
\r
1043 public void appPaused()
\r
1048 handleAppPaused (host);
\r
1052 public void appResumed()
\r
1057 // Ensure that navigation/status bar visibility is correctly restored.
\r
1058 handleAppResumed (host);
\r
1062 //==============================================================================
\r
1063 public static class NativeSurfaceView extends SurfaceView
\r
1064 implements SurfaceHolder.Callback
\r
1066 private long nativeContext = 0;
\r
1068 NativeSurfaceView (Context context, long nativeContextPtr)
\r
1071 nativeContext = nativeContextPtr;
\r
1074 public Surface getNativeSurface()
\r
1076 Surface retval = null;
\r
1078 SurfaceHolder holder = getHolder();
\r
1079 if (holder != null)
\r
1080 retval = holder.getSurface();
\r
1085 //==============================================================================
\r
1087 public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
\r
1089 surfaceChangedNative (nativeContext, holder, format, width, height);
\r
1093 public void surfaceCreated (SurfaceHolder holder)
\r
1095 surfaceCreatedNative (nativeContext, holder);
\r
1099 public void surfaceDestroyed (SurfaceHolder holder)
\r
1101 surfaceDestroyedNative (nativeContext, holder);
\r
1105 protected void dispatchDraw (Canvas canvas)
\r
1107 super.dispatchDraw (canvas);
\r
1108 dispatchDrawNative (nativeContext, canvas);
\r
1111 //==============================================================================
\r
1113 protected void onAttachedToWindow ()
\r
1115 super.onAttachedToWindow();
\r
1116 getHolder().addCallback (this);
\r
1120 protected void onDetachedFromWindow ()
\r
1122 super.onDetachedFromWindow();
\r
1123 getHolder().removeCallback (this);
\r
1126 //==============================================================================
\r
1127 private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
\r
1128 private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
\r
1129 private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
\r
1130 private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
\r
1131 int format, int width, int height);
\r
1134 public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
\r
1136 return new NativeSurfaceView (this, nativeSurfacePtr);
\r
1139 //==============================================================================
\r
1140 public final int[] renderGlyph (char glyph1, char glyph2, Paint paint, android.graphics.Matrix matrix, Rect bounds)
\r
1142 Path p = new Path();
\r
1144 char[] str = { glyph1, glyph2 };
\r
1145 paint.getTextPath (str, 0, (glyph2 != 0 ? 2 : 1), 0.0f, 0.0f, p);
\r
1147 RectF boundsF = new RectF();
\r
1148 p.computeBounds (boundsF, true);
\r
1149 matrix.mapRect (boundsF);
\r
1151 boundsF.roundOut (bounds);
\r
1155 final int w = bounds.width();
\r
1156 final int h = Math.max (1, bounds.height());
\r
1158 Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
\r
1160 Canvas c = new Canvas (bm);
\r
1161 matrix.postTranslate (-bounds.left, -bounds.top);
\r
1162 c.setMatrix (matrix);
\r
1163 c.drawPath (p, paint);
\r
1165 final int sizeNeeded = w * h;
\r
1166 if (cachedRenderArray.length < sizeNeeded)
\r
1167 cachedRenderArray = new int [sizeNeeded];
\r
1169 bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
\r
1171 return cachedRenderArray;
\r
1174 private int[] cachedRenderArray = new int [256];
\r
1176 //==============================================================================
\r
1177 public static class NativeInvocationHandler implements InvocationHandler
\r
1179 public NativeInvocationHandler (Activity activityToUse, long nativeContextRef)
\r
1181 activity = activityToUse;
\r
1182 nativeContext = nativeContextRef;
\r
1185 public void nativeContextDeleted()
\r
1187 nativeContext = 0;
\r
1191 public void finalize()
\r
1193 activity.runOnUiThread (new Runnable()
\r
1198 if (nativeContext != 0)
\r
1199 dispatchFinalize (nativeContext);
\r
1205 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
\r
1207 return dispatchInvoke (nativeContext, proxy, method, args);
\r
1210 //==============================================================================
\r
1211 Activity activity;
\r
1212 private long nativeContext = 0;
\r
1214 private native void dispatchFinalize (long nativeContextRef);
\r
1215 private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
\r
1218 public InvocationHandler createInvocationHandler (long nativeContextRef)
\r
1220 return new NativeInvocationHandler (this, nativeContextRef);
\r
1223 public void invocationHandlerContextDeleted (InvocationHandler handler)
\r
1225 ((NativeInvocationHandler) handler).nativeContextDeleted();
\r
1228 //==============================================================================
\r
1229 public static class HTTPStream
\r
1231 public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse,
\r
1232 String headersToUse, int timeOutMsToUse,
\r
1233 int[] statusCodeToUse, StringBuffer responseHeadersToUse,
\r
1234 int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
\r
1236 isPost = isPostToUse;
\r
1237 postData = postDataToUse;
\r
1238 headers = headersToUse;
\r
1239 timeOutMs = timeOutMsToUse;
\r
1240 statusCode = statusCodeToUse;
\r
1241 responseHeaders = responseHeadersToUse;
\r
1243 numRedirectsToFollow = numRedirectsToFollowToUse;
\r
1244 httpRequestCmd = httpRequestCmdToUse;
\r
1246 connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd);
\r
1249 private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData,
\r
1250 String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
\r
1252 HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
\r
1256 newConnection.setInstanceFollowRedirects (false);
\r
1257 newConnection.setConnectTimeout (timeOutMs);
\r
1258 newConnection.setReadTimeout (timeOutMs);
\r
1260 // 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.
\r
1261 // So convert headers string to an array, with an element for each line
\r
1262 String headerLines[] = headers.split("\\n");
\r
1264 // Set request headers
\r
1265 for (int i = 0; i < headerLines.length; ++i)
\r
1267 int pos = headerLines[i].indexOf (":");
\r
1269 if (pos > 0 && pos < headerLines[i].length())
\r
1271 String field = headerLines[i].substring (0, pos);
\r
1272 String value = headerLines[i].substring (pos + 1);
\r
1274 if (value.length() > 0)
\r
1275 newConnection.setRequestProperty (field, value);
\r
1279 newConnection.setRequestMethod (httpRequestCmd);
\r
1283 newConnection.setDoOutput (true);
\r
1285 if (postData != null)
\r
1287 OutputStream out = newConnection.getOutputStream();
\r
1288 out.write(postData);
\r
1293 return newConnection;
\r
1295 catch (Throwable e)
\r
1297 newConnection.disconnect();
\r
1298 throw new IOException ("Connection error");
\r
1302 private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException
\r
1304 synchronized (createFutureLock)
\r
1306 if (hasBeenCancelled.get())
\r
1309 streamFuture = executor.submit (new Callable<BufferedInputStream>()
\r
1312 public BufferedInputStream call() throws IOException
\r
1314 return new BufferedInputStream (isInput ? connection.getInputStream()
\r
1315 : connection.getErrorStream());
\r
1322 return streamFuture.get();
\r
1324 catch (InterruptedException e)
\r
1328 catch (CancellationException e)
\r
1334 public final boolean connect()
\r
1336 boolean result = false;
\r
1337 int numFollowedRedirects = 0;
\r
1341 result = doConnect();
\r
1346 if (++numFollowedRedirects > numRedirectsToFollow)
\r
1349 int status = statusCode[0];
\r
1351 if (status == 301 || status == 302 || status == 303 || status == 307)
\r
1353 // Assumes only one occurrence of "Location"
\r
1354 int pos1 = responseHeaders.indexOf ("Location:") + 10;
\r
1355 int pos2 = responseHeaders.indexOf ("\n", pos1);
\r
1359 String currentLocation = connection.getURL().toString();
\r
1360 String newLocation = responseHeaders.substring (pos1, pos2);
\r
1364 // Handle newLocation whether it's absolute or relative
\r
1365 URL baseUrl = new URL (currentLocation);
\r
1366 URL newUrl = new URL (baseUrl, newLocation);
\r
1367 String transformedNewLocation = newUrl.toString();
\r
1369 if (transformedNewLocation != currentLocation)
\r
1371 // Clear responseHeaders before next iteration
\r
1372 responseHeaders.delete (0, responseHeaders.length());
\r
1374 synchronized (createStreamLock)
\r
1376 if (hasBeenCancelled.get())
\r
1379 connection.disconnect();
\r
1383 connection = createConnection (transformedNewLocation, isPost,
\r
1384 postData, headers, timeOutMs,
\r
1387 catch (Throwable e)
\r
1398 catch (Throwable e)
\r
1417 private final boolean doConnect()
\r
1419 synchronized (createStreamLock)
\r
1421 if (hasBeenCancelled.get())
\r
1428 inputStream = getCancellableStream (true);
\r
1430 catch (ExecutionException e)
\r
1432 if (connection.getResponseCode() < 400)
\r
1434 statusCode[0] = connection.getResponseCode();
\r
1435 connection.disconnect();
\r
1441 statusCode[0] = connection.getResponseCode();
\r
1446 if (statusCode[0] >= 400)
\r
1447 inputStream = getCancellableStream (false);
\r
1449 inputStream = getCancellableStream (true);
\r
1451 catch (ExecutionException e)
\r
1454 for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
\r
1456 if (entry.getKey() != null && entry.getValue() != null)
\r
1458 responseHeaders.append(entry.getKey() + ": "
\r
1459 + android.text.TextUtils.join(",", entry.getValue()) + "\n");
\r
1461 if (entry.getKey().compareTo ("Content-Length") == 0)
\r
1462 totalLength = Integer.decode (entry.getValue().get (0));
\r
1468 catch (IOException e)
\r
1475 static class DisconnectionRunnable implements Runnable
\r
1477 public DisconnectionRunnable (HttpURLConnection theConnection,
\r
1478 InputStream theInputStream,
\r
1479 ReentrantLock theCreateStreamLock,
\r
1480 Object theCreateFutureLock,
\r
1481 Future<BufferedInputStream> theStreamFuture)
\r
1483 connectionToDisconnect = theConnection;
\r
1484 inputStream = theInputStream;
\r
1485 createStreamLock = theCreateStreamLock;
\r
1486 createFutureLock = theCreateFutureLock;
\r
1487 streamFuture = theStreamFuture;
\r
1494 if (! createStreamLock.tryLock())
\r
1496 synchronized (createFutureLock)
\r
1498 if (streamFuture != null)
\r
1499 streamFuture.cancel (true);
\r
1502 createStreamLock.lock();
\r
1505 if (connectionToDisconnect != null)
\r
1506 connectionToDisconnect.disconnect();
\r
1508 if (inputStream != null)
\r
1509 inputStream.close();
\r
1511 catch (IOException e)
\r
1515 createStreamLock.unlock();
\r
1519 private HttpURLConnection connectionToDisconnect;
\r
1520 private InputStream inputStream;
\r
1521 private ReentrantLock createStreamLock;
\r
1522 private Object createFutureLock;
\r
1523 Future<BufferedInputStream> streamFuture;
\r
1526 public final void release()
\r
1528 DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable (connection,
\r
1534 synchronized (createStreamLock)
\r
1536 hasBeenCancelled.set (true);
\r
1538 connection = null;
\r
1541 Thread disconnectionThread = new Thread(disconnectionRunnable);
\r
1542 disconnectionThread.start();
\r
1545 public final int read (byte[] buffer, int numBytes)
\r
1551 synchronized (createStreamLock)
\r
1553 if (inputStream != null)
\r
1554 num = inputStream.read (buffer, 0, numBytes);
\r
1557 catch (IOException e)
\r
1566 public final long getPosition() { return position; }
\r
1567 public final long getTotalLength() { return totalLength; }
\r
1568 public final boolean isExhausted() { return false; }
\r
1569 public final boolean setPosition (long newPos) { return false; }
\r
1571 private boolean isPost;
\r
1572 private byte[] postData;
\r
1573 private String headers;
\r
1574 private int timeOutMs;
\r
1575 String httpRequestCmd;
\r
1576 private HttpURLConnection connection;
\r
1577 private int[] statusCode;
\r
1578 private StringBuffer responseHeaders;
\r
1579 private int totalLength;
\r
1580 private int numRedirectsToFollow;
\r
1581 private InputStream inputStream;
\r
1582 private long position;
\r
1583 private final ReentrantLock createStreamLock = new ReentrantLock();
\r
1584 private final Object createFutureLock = new Object();
\r
1585 private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
\r
1587 private final ExecutorService executor = Executors.newCachedThreadPool (Executors.defaultThreadFactory());
\r
1588 Future<BufferedInputStream> streamFuture;
\r
1591 public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
\r
1592 String headers, int timeOutMs, int[] statusCode,
\r
1593 StringBuffer responseHeaders, int numRedirectsToFollow,
\r
1594 String httpRequestCmd)
\r
1596 // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
\r
1597 if (timeOutMs < 0)
\r
1599 else if (timeOutMs == 0)
\r
1600 timeOutMs = 30000;
\r
1606 HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers,
\r
1607 timeOutMs, statusCode, responseHeaders,
\r
1608 numRedirectsToFollow, httpRequestCmd);
\r
1610 return httpStream;
\r
1612 catch (Throwable e) {}
\r
1618 public final void launchURL (String url)
\r
1620 startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
\r
1623 private native boolean webViewPageLoadStarted (long host, WebView view, String url);
\r
1624 private native void webViewPageLoadFinished (long host, WebView view, String url);
\r
1625 private native void webViewReceivedSslError (long host, WebView view, SslErrorHandler handler, SslError error);
\r
1626 private native void webViewCloseWindowRequest (long host, WebView view);
\r
1627 private native void webViewCreateWindowRequest (long host, WebView view);
\r
1629 //==============================================================================
\r
1630 public class JuceWebViewClient extends WebViewClient
\r
1632 public JuceWebViewClient (long hostToUse)
\r
1637 public void hostDeleted()
\r
1639 synchronized (hostLock)
\r
1646 public void onPageFinished (WebView view, String url)
\r
1651 webViewPageLoadFinished (host, view, url);
\r
1655 public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
\r
1660 webViewReceivedSslError (host, view, handler, error);
\r
1664 public void onPageStarted (WebView view, String url, Bitmap favicon)
\r
1667 webViewPageLoadStarted (host, view, url);
\r
1670 private long host;
\r
1671 private final Object hostLock = new Object();
\r
1674 public class JuceWebChromeClient extends WebChromeClient
\r
1676 public JuceWebChromeClient (long hostToUse)
\r
1682 public void onCloseWindow (WebView window)
\r
1684 webViewCloseWindowRequest (host, window);
\r
1688 public boolean onCreateWindow (WebView view, boolean isDialog,
\r
1689 boolean isUserGesture, Message resultMsg)
\r
1691 webViewCreateWindowRequest (host, view);
\r
1695 private long host;
\r
1696 private final Object hostLock = new Object();
\r
1700 //==============================================================================
\r
1701 public static final String getLocaleValue (boolean isRegion)
\r
1703 java.util.Locale locale = java.util.Locale.getDefault();
\r
1705 return isRegion ? locale.getCountry()
\r
1706 : locale.getLanguage();
\r
1709 private static final String getFileLocation (String type)
\r
1711 return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath();
\r
1714 public static final String getDocumentsFolder()
\r
1716 if (getAndroidSDKVersion() >= 19)
\r
1717 return getFileLocation ("Documents");
\r
1719 return Environment.getDataDirectory().getAbsolutePath();
\r
1722 public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); }
\r
1723 public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); }
\r
1724 public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); }
\r
1725 public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
\r
1727 //==============================================================================
\r
1729 protected void onActivityResult (int requestCode, int resultCode, Intent data)
\r
1731 appActivityResult (requestCode, resultCode, data);
\r
1735 protected void onNewIntent (Intent intent)
\r
1737 super.onNewIntent(intent);
\r
1738 setIntent(intent);
\r
1740 appNewIntent (intent);
\r
1743 //==============================================================================
\r
1744 public final Typeface getTypeFaceFromAsset (String assetName)
\r
1748 return Typeface.createFromAsset (this.getResources().getAssets(), assetName);
\r
1750 catch (Throwable e) {}
\r
1755 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
\r
1757 public static String bytesToHex (byte[] bytes)
\r
1759 char[] hexChars = new char[bytes.length * 2];
\r
1761 for (int j = 0; j < bytes.length; ++j)
\r
1763 int v = bytes[j] & 0xff;
\r
1764 hexChars[j * 2] = hexArray[v >>> 4];
\r
1765 hexChars[j * 2 + 1] = hexArray[v & 0x0f];
\r
1768 return new String (hexChars);
\r
1771 final private java.util.Map dataCache = new java.util.HashMap();
\r
1773 synchronized private final File getDataCacheFile (byte[] data)
\r
1777 java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5");
\r
1778 digest.update (data);
\r
1780 String key = bytesToHex (digest.digest());
\r
1782 if (dataCache.containsKey (key))
\r
1783 return (File) dataCache.get (key);
\r
1785 File f = new File (this.getCacheDir(), "bindata_" + key);
\r
1787 FileOutputStream os = new FileOutputStream (f);
\r
1788 os.write (data, 0, data.length);
\r
1789 dataCache.put (key, f);
\r
1792 catch (Throwable e) {}
\r
1797 private final void clearDataCache()
\r
1799 java.util.Iterator it = dataCache.values().iterator();
\r
1801 while (it.hasNext())
\r
1803 File f = (File) it.next();
\r
1808 public final Typeface getTypeFaceFromByteArray (byte[] data)
\r
1812 File f = getDataCacheFile (data);
\r
1815 return Typeface.createFromFile (f);
\r
1817 catch (Exception e)
\r
1819 Log.e ("JUCE", e.toString());
\r
1825 public static final int getAndroidSDKVersion()
\r
1827 return android.os.Build.VERSION.SDK_INT;
\r
1830 public final String audioManagerGetProperty (String property)
\r
1832 Object obj = getSystemService (AUDIO_SERVICE);
\r
1836 java.lang.reflect.Method method;
\r
1840 method = obj.getClass().getMethod ("getProperty", String.class);
\r
1842 catch (SecurityException e) { return null; }
\r
1843 catch (NoSuchMethodException e) { return null; }
\r
1845 if (method == null)
\r
1850 return (String) method.invoke (obj, property);
\r
1852 catch (java.lang.IllegalArgumentException e) {}
\r
1853 catch (java.lang.IllegalAccessException e) {}
\r
1854 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1859 public final boolean hasSystemFeature (String property)
\r
1861 return getPackageManager().hasSystemFeature (property);
\r