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.audioperformancetest;
\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.hardware.camera2.*;
\r
34 import android.net.http.SslError;
\r
35 import android.net.Uri;
\r
36 import android.os.Bundle;
\r
37 import android.os.Looper;
\r
38 import android.os.Handler;
\r
39 import android.os.Message;
\r
40 import android.os.ParcelUuid;
\r
41 import android.os.Environment;
\r
42 import android.view.*;
\r
43 import android.view.inputmethod.BaseInputConnection;
\r
44 import android.view.inputmethod.EditorInfo;
\r
45 import android.view.inputmethod.InputConnection;
\r
46 import android.view.inputmethod.InputMethodManager;
\r
47 import android.graphics.*;
\r
48 import android.text.ClipboardManager;
\r
49 import android.text.InputType;
\r
50 import android.util.DisplayMetrics;
\r
51 import android.util.Log;
\r
52 import android.util.Pair;
\r
53 import android.webkit.SslErrorHandler;
\r
54 import android.webkit.WebChromeClient;
\r
55 import android.webkit.WebResourceError;
\r
56 import android.webkit.WebResourceRequest;
\r
57 import android.webkit.WebResourceResponse;
\r
58 import android.webkit.WebView;
\r
59 import android.webkit.WebViewClient;
\r
60 import java.lang.Runnable;
\r
61 import java.lang.ref.WeakReference;
\r
62 import java.lang.reflect.*;
\r
65 import java.net.URL;
\r
66 import java.net.HttpURLConnection;
\r
67 import android.media.AudioManager;
\r
68 import android.Manifest;
\r
69 import java.util.concurrent.CancellationException;
\r
70 import java.util.concurrent.Future;
\r
71 import java.util.concurrent.Executors;
\r
72 import java.util.concurrent.ExecutorService;
\r
73 import java.util.concurrent.ExecutionException;
\r
74 import java.util.concurrent.TimeUnit;
\r
75 import java.util.concurrent.Callable;
\r
76 import java.util.concurrent.TimeoutException;
\r
77 import java.util.concurrent.locks.ReentrantLock;
\r
78 import java.util.concurrent.atomic.*;
\r
80 import android.media.midi.*;
\r
81 import android.bluetooth.*;
\r
82 import android.bluetooth.le.*;
\r
85 //==============================================================================
\r
86 public class AudioPerformanceTest extends Activity
\r
88 //==============================================================================
\r
91 System.loadLibrary ("juce_jni");
\r
94 //==============================================================================
\r
95 public boolean isPermissionDeclaredInManifest (int permissionID)
\r
97 String permissionToCheck = getAndroidPermissionName(permissionID);
\r
101 PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
\r
103 if (info.requestedPermissions != null)
\r
104 for (String permission : info.requestedPermissions)
\r
105 if (permission.equals (permissionToCheck))
\r
108 catch (PackageManager.NameNotFoundException e)
\r
110 Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString());
\r
113 Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck);
\r
117 //==============================================================================
\r
118 // these have to match the values of enum PermissionID in C++ class RuntimePermissions:
\r
119 private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1;
\r
120 private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2;
\r
121 private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3;
\r
122 private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4;
\r
123 private static final int JUCE_PERMISSIONS_CAMERA = 5;
\r
125 private static String getAndroidPermissionName (int permissionID)
\r
127 switch (permissionID)
\r
129 case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO;
\r
130 case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION;
\r
131 // use string value as this is not defined in SDKs < 16
\r
132 case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE";
\r
133 case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE;
\r
134 case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA;
\r
137 // unknown permission ID!
\r
139 return new String();
\r
142 public boolean isPermissionGranted (int permissionID)
\r
144 return getApplicationContext().checkCallingOrSelfPermission (getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED;
\r
147 private Map<Integer, Long> permissionCallbackPtrMap;
\r
149 public void requestRuntimePermission (int permissionID, long ptrToCallback)
\r
151 String permissionName = getAndroidPermissionName (permissionID);
\r
153 if (getApplicationContext().checkCallingOrSelfPermission (permissionName) != PackageManager.PERMISSION_GRANTED)
\r
155 // remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously
\r
156 permissionCallbackPtrMap.put (permissionID, ptrToCallback);
\r
157 requestPermissionsCompat (new String[]{permissionName}, permissionID);
\r
161 // permissions were already granted before, we can call callback directly
\r
162 androidRuntimePermissionsCallback (true, ptrToCallback);
\r
166 private native void androidRuntimePermissionsCallback (boolean permissionWasGranted, long ptrToCallback);
\r
169 public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults)
\r
171 boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
\r
173 if (! permissionsGranted)
\r
174 Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID));
\r
176 Long ptrToCallback = permissionCallbackPtrMap.get (permissionID);
\r
177 permissionCallbackPtrMap.remove (permissionID);
\r
178 androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback);
\r
181 //==============================================================================
\r
182 public interface JuceMidiPort
\r
184 boolean isInputPort();
\r
186 // start, stop does nothing on an output port
\r
192 // send will do nothing on an input port
\r
193 void sendMidi (byte[] msg, int offset, int count);
\r
196 //==============================================================================
\r
197 //==============================================================================
\r
198 public class BluetoothManager extends ScanCallback
\r
204 public String[] getMidiBluetoothAddresses()
\r
206 return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
\r
209 public String getHumanReadableStringForBluetoothAddress (String address)
\r
211 BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
\r
212 return btDevice.getName();
\r
215 public int getBluetoothDeviceStatus (String address)
\r
217 return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
\r
220 public void startStopScan (boolean shouldStart)
\r
222 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
\r
224 if (bluetoothAdapter == null)
\r
226 Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
\r
230 BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
\r
232 if (bluetoothLeScanner == null)
\r
234 Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
\r
240 ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
\r
241 scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
\r
243 ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
\r
244 scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
\r
245 .setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
\r
246 .setScanMode (ScanSettings.MATCH_MODE_STICKY);
\r
248 bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
\r
249 scanSettingsBuilder.build(),
\r
254 bluetoothLeScanner.stopScan (this);
\r
258 public boolean pairBluetoothMidiDevice(String address)
\r
260 BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
\r
262 if (btDevice == null)
\r
264 Log.d ("JUCE", "failed to create buletooth device from address");
\r
268 return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
\r
271 public void unpairBluetoothMidiDevice (String address)
\r
273 getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
\r
276 public void onScanFailed (int errorCode)
\r
280 public void onScanResult (int callbackType, ScanResult result)
\r
282 if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
\r
283 || callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
\r
285 BluetoothDevice device = result.getDevice();
\r
287 if (device != null)
\r
288 bluetoothMidiDevices.add (device.getAddress());
\r
291 if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
\r
293 Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
\r
294 BluetoothDevice device = result.getDevice();
\r
296 if (device != null)
\r
298 bluetoothMidiDevices.remove (device.getAddress());
\r
299 unpairBluetoothMidiDevice (device.getAddress());
\r
304 public void onBatchScanResults (List<ScanResult> results)
\r
306 for (ScanResult result : results)
\r
307 onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
\r
310 private BluetoothLeScanner scanner;
\r
311 private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
\r
313 private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
\r
316 public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
\r
318 private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
\r
320 public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
\r
323 androidPort = actualPort;
\r
324 portPath = portPathToUse;
\r
325 juceHost = hostToUse;
\r
326 isConnected = false;
\r
330 protected void finalize() throws Throwable
\r
337 public boolean isInputPort()
\r
343 public void start()
\r
345 if (owner != null && androidPort != null && ! isConnected) {
\r
346 androidPort.connect(this);
\r
347 isConnected = true;
\r
354 if (owner != null && androidPort != null && isConnected) {
\r
355 androidPort.disconnect(this);
\r
356 isConnected = false;
\r
361 public void close()
\r
363 if (androidPort != null) {
\r
365 androidPort.close();
\r
366 } catch (IOException exception) {
\r
367 Log.d("JUCE", "IO Exception while closing port");
\r
372 owner.removePort (portPath);
\r
375 androidPort = null;
\r
379 public void onSend (byte[] msg, int offset, int count, long timestamp)
\r
382 handleReceive (juceHost, msg, offset, count, timestamp);
\r
386 public void onFlush()
\r
390 public void sendMidi (byte[] msg, int offset, int count)
\r
394 MidiDeviceManager owner;
\r
395 MidiOutputPort androidPort;
\r
396 MidiPortPath portPath;
\r
398 boolean isConnected;
\r
401 public static class JuceMidiOutputPort implements JuceMidiPort
\r
403 public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
\r
406 androidPort = actualPort;
\r
407 portPath = portPathToUse;
\r
411 protected void finalize() throws Throwable
\r
418 public boolean isInputPort()
\r
424 public void start()
\r
434 public void sendMidi (byte[] msg, int offset, int count)
\r
436 if (androidPort != null)
\r
439 androidPort.send(msg, offset, count);
\r
440 } catch (IOException exception)
\r
442 Log.d ("JUCE", "send midi had IO exception");
\r
448 public void close()
\r
450 if (androidPort != null) {
\r
452 androidPort.close();
\r
453 } catch (IOException exception) {
\r
454 Log.d("JUCE", "IO Exception while closing port");
\r
459 owner.removePort (portPath);
\r
462 androidPort = null;
\r
465 MidiDeviceManager owner;
\r
466 MidiInputPort androidPort;
\r
467 MidiPortPath portPath;
\r
470 private static class MidiPortPath extends Object
\r
472 public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
\r
474 deviceId = deviceIdToUse;
\r
475 isInput = direction;
\r
476 portIndex = androidIndex;
\r
480 public int deviceId;
\r
481 public int portIndex;
\r
482 public boolean isInput;
\r
485 public int hashCode()
\r
487 Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
\r
488 return i.hashCode() * (isInput ? -1 : 1);
\r
492 public boolean equals (Object obj)
\r
497 if (getClass() != obj.getClass())
\r
500 MidiPortPath other = (MidiPortPath) obj;
\r
501 return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
\r
505 //==============================================================================
\r
506 public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
\r
508 //==============================================================================
\r
509 private class DummyBluetoothGattCallback extends BluetoothGattCallback
\r
511 public DummyBluetoothGattCallback (MidiDeviceManager mm)
\r
517 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
\r
519 if (newState == BluetoothProfile.STATE_CONNECTED)
\r
521 gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
\r
522 owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
\r
525 public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
\r
526 public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
\r
527 public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
\r
528 public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
\r
529 public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
\r
530 public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
\r
531 public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
\r
532 public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
\r
533 public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
\r
535 private MidiDeviceManager owner;
\r
538 //==============================================================================
\r
539 private class MidiDeviceOpenTask extends java.util.TimerTask
\r
541 public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
\r
543 owner = deviceManager;
\r
544 midiDevice = device;
\r
545 btGatt = gattToUse;
\r
549 public boolean cancel()
\r
551 synchronized (MidiDeviceOpenTask.class)
\r
554 boolean retval = super.cancel();
\r
556 if (btGatt != null)
\r
558 btGatt.disconnect();
\r
564 if (midiDevice != null)
\r
568 midiDevice.close();
\r
570 catch (IOException e)
\r
580 public String getBluetoothAddress()
\r
582 synchronized (MidiDeviceOpenTask.class)
\r
584 if (midiDevice != null)
\r
586 MidiDeviceInfo info = midiDevice.getInfo();
\r
587 if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
\r
589 BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
\r
590 if (btDevice != null)
\r
591 return btDevice.getAddress();
\r
599 public BluetoothGatt getGatt() { return btGatt; }
\r
603 return midiDevice.getInfo().getId();
\r
609 synchronized (MidiDeviceOpenTask.class)
\r
611 if (owner != null && midiDevice != null)
\r
612 owner.onDeviceOpenedDelayed (midiDevice);
\r
616 private MidiDeviceManager owner;
\r
617 private MidiDevice midiDevice;
\r
618 private BluetoothGatt btGatt;
\r
621 //==============================================================================
\r
622 public MidiDeviceManager()
\r
624 manager = (MidiManager) getSystemService (MIDI_SERVICE);
\r
626 if (manager == null)
\r
628 Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
\r
632 openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
\r
633 midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
\r
634 openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
\r
635 btDevicesPairing = new HashMap<String, BluetoothGatt>();
\r
637 MidiDeviceInfo[] foundDevices = manager.getDevices();
\r
638 for (MidiDeviceInfo info : foundDevices)
\r
639 onDeviceAdded (info);
\r
641 manager.registerDeviceCallback (this, null);
\r
644 protected void finalize() throws Throwable
\r
646 manager.unregisterDeviceCallback (this);
\r
648 synchronized (MidiDeviceManager.class)
\r
650 btDevicesPairing.clear();
\r
652 for (Integer deviceID : openTasks.keySet())
\r
653 openTasks.get (deviceID).cancel();
\r
658 for (MidiPortPath key : openPorts.keySet())
\r
659 openPorts.get (key).get().close();
\r
663 for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
\r
665 if (device.second != null)
\r
667 device.second.disconnect();
\r
668 device.second.close();
\r
671 device.first.close();
\r
674 midiDevices.clear();
\r
679 public String[] getJuceAndroidMidiInputDevices()
\r
681 return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
\r
684 public String[] getJuceAndroidMidiOutputDevices()
\r
686 return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
\r
689 private String[] getJuceAndroidMidiDevices (int portType)
\r
691 // only update the list when JUCE asks for a new list
\r
692 synchronized (MidiDeviceManager.class)
\r
694 deviceInfos = getDeviceInfos();
\r
697 ArrayList<String> portNames = new ArrayList<String>();
\r
700 for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
\r
701 portNames.add (getPortName (portInfo));
\r
703 String[] names = new String[portNames.size()];
\r
704 return portNames.toArray (names);
\r
707 private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
\r
709 synchronized (MidiDeviceManager.class)
\r
711 int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
\r
712 MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
\r
714 if (portInfo != null)
\r
716 // ports must be opened exclusively!
\r
717 if (openPorts.containsKey (portInfo))
\r
720 Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
\r
722 if (devicePair != null)
\r
724 MidiDevice device = devicePair.first;
\r
725 if (device != null)
\r
727 JuceMidiPort juceMidiPort = null;
\r
731 MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
\r
733 if (outputPort != null)
\r
734 juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
\r
738 MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
\r
740 if (inputPort != null)
\r
741 juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
\r
744 if (juceMidiPort != null)
\r
746 openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
\r
748 return juceMidiPort;
\r
758 public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
\r
760 return openMidiPortWithJuceIndex (index, host, true);
\r
763 public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
\r
765 return openMidiPortWithJuceIndex (index, 0, false);
\r
768 /* 0: unpaired, 1: paired, 2: pairing */
\r
769 public int getBluetoothDeviceStatus (String address)
\r
771 synchronized (MidiDeviceManager.class)
\r
773 if (! address.isEmpty())
\r
775 if (findMidiDeviceForBluetoothAddress (address) != null)
\r
778 if (btDevicesPairing.containsKey (address))
\r
781 if (findOpenTaskForBluetoothAddress (address) != null)
\r
789 public boolean pairBluetoothDevice (BluetoothDevice btDevice)
\r
791 String btAddress = btDevice.getAddress();
\r
792 if (btAddress.isEmpty())
\r
795 synchronized (MidiDeviceManager.class)
\r
797 if (getBluetoothDeviceStatus (btAddress) != 0)
\r
801 btDevicesPairing.put (btDevice.getAddress(), null);
\r
802 BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
\r
806 btDevicesPairing.put (btDevice.getAddress(), gatt);
\r
810 pairBluetoothDeviceStepTwo (btDevice);
\r
817 public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
\r
819 manager.openBluetoothDevice(btDevice, this, null);
\r
822 public void unpairBluetoothDevice (String address)
\r
824 if (address.isEmpty())
\r
827 synchronized (MidiDeviceManager.class)
\r
829 if (btDevicesPairing.containsKey (address))
\r
831 BluetoothGatt gatt = btDevicesPairing.get (address);
\r
838 btDevicesPairing.remove (address);
\r
841 MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
\r
842 if (openTask != null)
\r
844 int deviceID = openTask.getID();
\r
846 openTasks.remove (deviceID);
\r
849 Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
\r
850 if (midiDevicePair != null)
\r
852 MidiDevice midiDevice = midiDevicePair.first;
\r
853 onDeviceRemoved (midiDevice.getInfo());
\r
856 midiDevice.close();
\r
858 catch (IOException exception)
\r
860 Log.d ("JUCE", "IOException while closing midi device");
\r
866 private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
\r
868 for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
\r
870 MidiDeviceInfo info = midiDevice.first.getInfo();
\r
871 if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
\r
873 BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
\r
874 if (btDevice != null && btDevice.getAddress().equals (address))
\r
882 private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
\r
884 for (Integer deviceID : openTasks.keySet())
\r
886 MidiDeviceOpenTask openTask = openTasks.get (deviceID);
\r
887 if (openTask.getBluetoothAddress().equals (address))
\r
894 public void removePort (MidiPortPath path)
\r
896 openPorts.remove (path);
\r
899 public String getInputPortNameForJuceIndex (int index)
\r
901 MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
\r
902 if (portInfo != null)
\r
903 return getPortName (portInfo);
\r
908 public String getOutputPortNameForJuceIndex (int index)
\r
910 MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
\r
911 if (portInfo != null)
\r
912 return getPortName (portInfo);
\r
917 public void onDeviceAdded (MidiDeviceInfo info)
\r
919 // only add standard midi devices
\r
920 if (info.getType() == info.TYPE_BLUETOOTH)
\r
923 manager.openDevice (info, this, null);
\r
926 public void onDeviceRemoved (MidiDeviceInfo info)
\r
928 synchronized (MidiDeviceManager.class)
\r
930 Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
\r
932 if (devicePair != null)
\r
934 MidiDevice midiDevice = devicePair.first;
\r
935 BluetoothGatt gatt = devicePair.second;
\r
937 // close all ports that use this device
\r
938 boolean removedPort = true;
\r
940 while (removedPort == true)
\r
942 removedPort = false;
\r
943 for (MidiPortPath key : openPorts.keySet())
\r
945 if (key.deviceId == info.getId())
\r
947 openPorts.get(key).get().close();
\r
948 removedPort = true;
\r
960 midiDevices.remove (devicePair);
\r
965 public void onDeviceStatusChanged (MidiDeviceStatus status)
\r
970 public void onDeviceOpened (MidiDevice theDevice)
\r
972 synchronized (MidiDeviceManager.class)
\r
974 MidiDeviceInfo info = theDevice.getInfo();
\r
975 int deviceID = info.getId();
\r
976 BluetoothGatt gatt = null;
\r
977 boolean isBluetooth = false;
\r
979 if (! openTasks.containsKey (deviceID))
\r
981 if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
\r
983 isBluetooth = true;
\r
984 BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
\r
985 if (btDevice != null)
\r
987 String btAddress = btDevice.getAddress();
\r
988 if (btDevicesPairing.containsKey (btAddress))
\r
990 gatt = btDevicesPairing.get (btAddress);
\r
991 btDevicesPairing.remove (btAddress);
\r
995 // unpair was called in the mean time
\r
998 Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
\r
999 if (midiDevicePair != null)
\r
1001 gatt = midiDevicePair.second;
\r
1005 gatt.disconnect();
\r
1010 theDevice.close();
\r
1012 catch (IOException e)
\r
1020 MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
\r
1021 openTasks.put (deviceID, openTask);
\r
1023 new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
\r
1028 public void onDeviceOpenedDelayed (MidiDevice theDevice)
\r
1030 synchronized (MidiDeviceManager.class)
\r
1032 int deviceID = theDevice.getInfo().getId();
\r
1034 if (openTasks.containsKey (deviceID))
\r
1036 if (! midiDevices.contains(theDevice))
\r
1038 BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
\r
1039 openTasks.remove (deviceID);
\r
1040 midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
\r
1045 // unpair was called in the mean time
\r
1046 MidiDeviceInfo info = theDevice.getInfo();
\r
1047 BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
\r
1048 if (btDevice != null)
\r
1050 String btAddress = btDevice.getAddress();
\r
1051 Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
\r
1052 if (midiDevicePair != null)
\r
1054 BluetoothGatt gatt = midiDevicePair.second;
\r
1058 gatt.disconnect();
\r
1066 theDevice.close();
\r
1068 catch (IOException e)
\r
1074 public String getPortName(MidiPortPath path)
\r
1076 int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
\r
1078 synchronized (MidiDeviceManager.class)
\r
1080 for (MidiDeviceInfo info : deviceInfos)
\r
1082 int localIndex = 0;
\r
1083 if (info.getId() == path.deviceId)
\r
1085 for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
\r
1087 int portType = portInfo.getType();
\r
1088 if (portType == portTypeToFind)
\r
1090 int portIndex = portInfo.getPortNumber();
\r
1091 if (portIndex == path.portIndex)
\r
1093 String portName = portInfo.getName();
\r
1094 if (portName.isEmpty())
\r
1095 portName = (String) info.getProperties().get(info.PROPERTY_NAME);
\r
1108 public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
\r
1111 for (MidiDeviceInfo info : deviceInfos)
\r
1113 for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
\r
1115 if (portInfo.getType() == portType)
\r
1117 if (portIdx == juceIndex)
\r
1118 return new MidiPortPath (info.getId(),
\r
1119 (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
\r
1120 portInfo.getPortNumber());
\r
1130 private MidiDeviceInfo[] getDeviceInfos()
\r
1132 synchronized (MidiDeviceManager.class)
\r
1134 MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
\r
1137 for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
\r
1138 infos[idx++] = midiDevice.first.getInfo();
\r
1144 private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
\r
1146 synchronized (MidiDeviceManager.class)
\r
1148 for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
\r
1149 if (midiDevice.first.getInfo().getId() == deviceId)
\r
1150 return midiDevice;
\r
1156 private MidiManager manager;
\r
1157 private HashMap<String, BluetoothGatt> btDevicesPairing;
\r
1158 private HashMap<Integer, MidiDeviceOpenTask> openTasks;
\r
1159 private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
\r
1160 private MidiDeviceInfo[] deviceInfos;
\r
1161 private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
\r
1164 public MidiDeviceManager getAndroidMidiDeviceManager()
\r
1166 if (getSystemService (MIDI_SERVICE) == null)
\r
1169 synchronized (AudioPerformanceTest.class)
\r
1171 if (midiDeviceManager == null)
\r
1172 midiDeviceManager = new MidiDeviceManager();
\r
1175 return midiDeviceManager;
\r
1178 public BluetoothManager getAndroidBluetoothManager()
\r
1180 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
\r
1182 if (adapter == null)
\r
1185 if (adapter.getBluetoothLeScanner() == null)
\r
1188 synchronized (AudioPerformanceTest.class)
\r
1190 if (bluetoothManager == null)
\r
1191 bluetoothManager = new BluetoothManager();
\r
1194 return bluetoothManager;
\r
1197 //==============================================================================
\r
1199 public void onCreate (Bundle savedInstanceState)
\r
1201 super.onCreate (savedInstanceState);
\r
1203 isScreenSaverEnabled = true;
\r
1205 viewHolder = new ViewHolder (this);
\r
1206 setContentView (viewHolder);
\r
1208 setVolumeControlStream (AudioManager.STREAM_MUSIC);
\r
1210 permissionCallbackPtrMap = new HashMap<Integer, Long>();
\r
1211 appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>();
\r
1215 protected void onDestroy()
\r
1218 super.onDestroy();
\r
1224 protected void onPause()
\r
1228 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
1230 for (Long k : keys)
\r
1231 appPausedResumedListeners.get (k).appPaused();
\r
1235 Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down
\r
1236 // openGL glitches when pausing/resuming apps..
\r
1237 } catch (InterruptedException e) {}
\r
1243 protected void onResume()
\r
1248 Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]);
\r
1250 for (Long k : keys)
\r
1251 appPausedResumedListeners.get (k).appResumed();
\r
1255 public void onConfigurationChanged (Configuration cfg)
\r
1257 super.onConfigurationChanged (cfg);
\r
1258 setContentView (viewHolder);
\r
1261 private void callAppLauncher()
\r
1263 launchApp (getApplicationInfo().publicSourceDir,
\r
1264 getApplicationInfo().dataDir);
\r
1267 // Need to override this as the default implementation always finishes the activity.
\r
1269 public void onBackPressed()
\r
1271 ComponentPeerView focusedView = getViewWithFocusOrDefaultView();
\r
1273 if (focusedView == null)
\r
1276 focusedView.backButtonPressed();
\r
1279 private ComponentPeerView getViewWithFocusOrDefaultView()
\r
1281 for (int i = 0; i < viewHolder.getChildCount(); ++i)
\r
1283 if (viewHolder.getChildAt (i).hasFocus())
\r
1284 return (ComponentPeerView) viewHolder.getChildAt (i);
\r
1287 if (viewHolder.getChildCount() > 0)
\r
1288 return (ComponentPeerView) viewHolder.getChildAt (0);
\r
1293 //==============================================================================
\r
1294 private void hideActionBar()
\r
1296 // get "getActionBar" method
\r
1297 java.lang.reflect.Method getActionBarMethod = null;
\r
1300 getActionBarMethod = this.getClass().getMethod ("getActionBar");
\r
1302 catch (SecurityException e) { return; }
\r
1303 catch (NoSuchMethodException e) { return; }
\r
1304 if (getActionBarMethod == null) return;
\r
1306 // invoke "getActionBar" method
\r
1307 Object actionBar = null;
\r
1310 actionBar = getActionBarMethod.invoke (this);
\r
1312 catch (java.lang.IllegalArgumentException e) { return; }
\r
1313 catch (java.lang.IllegalAccessException e) { return; }
\r
1314 catch (java.lang.reflect.InvocationTargetException e) { return; }
\r
1315 if (actionBar == null) return;
\r
1317 // get "hide" method
\r
1318 java.lang.reflect.Method actionBarHideMethod = null;
\r
1321 actionBarHideMethod = actionBar.getClass().getMethod ("hide");
\r
1323 catch (SecurityException e) { return; }
\r
1324 catch (NoSuchMethodException e) { return; }
\r
1325 if (actionBarHideMethod == null) return;
\r
1327 // invoke "hide" method
\r
1330 actionBarHideMethod.invoke (actionBar);
\r
1332 catch (java.lang.IllegalArgumentException e) {}
\r
1333 catch (java.lang.IllegalAccessException e) {}
\r
1334 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1337 void requestPermissionsCompat (String[] permissions, int requestCode)
\r
1339 Method requestPermissionsMethod = null;
\r
1342 requestPermissionsMethod = this.getClass().getMethod ("requestPermissions",
\r
1343 String[].class, int.class);
\r
1345 catch (SecurityException e) { return; }
\r
1346 catch (NoSuchMethodException e) { return; }
\r
1347 if (requestPermissionsMethod == null) return;
\r
1351 requestPermissionsMethod.invoke (this, permissions, requestCode);
\r
1353 catch (java.lang.IllegalArgumentException e) {}
\r
1354 catch (java.lang.IllegalAccessException e) {}
\r
1355 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1358 //==============================================================================
\r
1359 private native void launchApp (String appFile, String appDataDir);
\r
1360 private native void quitApp();
\r
1361 private native void suspendApp();
\r
1362 private native void resumeApp();
\r
1363 private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
\r
1364 private native void appActivityResult (int requestCode, int resultCode, Intent data);
\r
1365 private native void appNewIntent (Intent intent);
\r
1367 //==============================================================================
\r
1368 private ViewHolder viewHolder;
\r
1369 private MidiDeviceManager midiDeviceManager = null;
\r
1370 private BluetoothManager bluetoothManager = null;
\r
1371 private boolean isScreenSaverEnabled;
\r
1372 private java.util.Timer keepAliveTimer;
\r
1374 public final ComponentPeerView createNewView (boolean opaque, long host)
\r
1376 ComponentPeerView v = new ComponentPeerView (this, opaque, host);
\r
1377 viewHolder.addView (v);
\r
1378 addAppPausedResumedListener (v, host);
\r
1382 public final void deleteView (ComponentPeerView view)
\r
1384 removeAppPausedResumedListener (view, view.host);
\r
1388 ViewGroup group = (ViewGroup) (view.getParent());
\r
1390 if (group != null)
\r
1391 group.removeView (view);
\r
1394 public final void deleteNativeSurfaceView (NativeSurfaceView view)
\r
1396 ViewGroup group = (ViewGroup) (view.getParent());
\r
1398 if (group != null)
\r
1399 group.removeView (view);
\r
1402 final class ViewHolder extends ViewGroup
\r
1404 public ViewHolder (Context context)
\r
1407 setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
\r
1408 setFocusable (false);
\r
1411 protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
1413 setScreenSize (getWidth(), getHeight(), getDPI());
\r
1415 if (isFirstResize)
\r
1417 isFirstResize = false;
\r
1418 callAppLauncher();
\r
1422 private final int getDPI()
\r
1424 DisplayMetrics metrics = new DisplayMetrics();
\r
1425 getWindowManager().getDefaultDisplay().getMetrics (metrics);
\r
1426 return metrics.densityDpi;
\r
1429 private boolean isFirstResize = true;
\r
1432 public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
\r
1434 canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
\r
1437 //==============================================================================
\r
1438 public final void setScreenSaver (boolean enabled)
\r
1440 if (isScreenSaverEnabled != enabled)
\r
1442 isScreenSaverEnabled = enabled;
\r
1444 if (keepAliveTimer != null)
\r
1446 keepAliveTimer.cancel();
\r
1447 keepAliveTimer = null;
\r
1452 getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
1456 getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
\r
1458 // If no user input is received after about 3 seconds, the OS will lower the
\r
1459 // task's priority, so this timer forces it to be kept active.
\r
1460 keepAliveTimer = new java.util.Timer();
\r
1462 keepAliveTimer.scheduleAtFixedRate (new TimerTask()
\r
1467 android.app.Instrumentation instrumentation = new android.app.Instrumentation();
\r
1471 instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_UNKNOWN);
\r
1473 catch (Exception e)
\r
1482 public final boolean getScreenSaver()
\r
1484 return isScreenSaverEnabled;
\r
1487 //==============================================================================
\r
1488 public final String getClipboardContent()
\r
1490 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
1491 return clipboard.getText().toString();
\r
1494 public final void setClipboardContent (String newText)
\r
1496 ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
\r
1497 clipboard.setText (newText);
\r
1500 //==============================================================================
\r
1501 public final void showMessageBox (String title, String message, final long callback)
\r
1503 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
1504 builder.setTitle (title)
\r
1505 .setMessage (message)
\r
1506 .setCancelable (true)
\r
1507 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
1509 public void onCancel (DialogInterface dialog)
\r
1511 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1514 .setPositiveButton ("OK", new DialogInterface.OnClickListener()
\r
1516 public void onClick (DialogInterface dialog, int id)
\r
1519 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1523 builder.create().show();
\r
1526 public final void showOkCancelBox (String title, String message, final long callback,
\r
1527 String okButtonText, String cancelButtonText)
\r
1529 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
1530 builder.setTitle (title)
\r
1531 .setMessage (message)
\r
1532 .setCancelable (true)
\r
1533 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
1535 public void onCancel (DialogInterface dialog)
\r
1537 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1540 .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener()
\r
1542 public void onClick (DialogInterface dialog, int id)
\r
1545 AudioPerformanceTest.this.alertDismissed (callback, 1);
\r
1548 .setNegativeButton (cancelButtonText.isEmpty() ? "Cancel" : cancelButtonText, new DialogInterface.OnClickListener()
\r
1550 public void onClick (DialogInterface dialog, int id)
\r
1553 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1557 builder.create().show();
\r
1560 public final void showYesNoCancelBox (String title, String message, final long callback)
\r
1562 AlertDialog.Builder builder = new AlertDialog.Builder (this);
\r
1563 builder.setTitle (title)
\r
1564 .setMessage (message)
\r
1565 .setCancelable (true)
\r
1566 .setOnCancelListener (new DialogInterface.OnCancelListener()
\r
1568 public void onCancel (DialogInterface dialog)
\r
1570 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1573 .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
\r
1575 public void onClick (DialogInterface dialog, int id)
\r
1578 AudioPerformanceTest.this.alertDismissed (callback, 1);
\r
1581 .setNegativeButton ("No", new DialogInterface.OnClickListener()
\r
1583 public void onClick (DialogInterface dialog, int id)
\r
1586 AudioPerformanceTest.this.alertDismissed (callback, 2);
\r
1589 .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
\r
1591 public void onClick (DialogInterface dialog, int id)
\r
1594 AudioPerformanceTest.this.alertDismissed (callback, 0);
\r
1598 builder.create().show();
\r
1601 public native void alertDismissed (long callback, int id);
\r
1603 //==============================================================================
\r
1604 public interface AppPausedResumedListener
\r
1607 void appResumed();
\r
1610 private Map<Long, AppPausedResumedListener> appPausedResumedListeners;
\r
1612 public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
1614 appPausedResumedListeners.put (new Long (listenerHost), l);
\r
1617 public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost)
\r
1619 appPausedResumedListeners.remove (new Long (listenerHost));
\r
1622 //==============================================================================
\r
1623 public final class ComponentPeerView extends ViewGroup
\r
1624 implements View.OnFocusChangeListener, AppPausedResumedListener
\r
1626 public ComponentPeerView (Context context, boolean opaque_, long host)
\r
1630 setWillNotDraw (false);
\r
1633 setFocusable (true);
\r
1634 setFocusableInTouchMode (true);
\r
1635 setOnFocusChangeListener (this);
\r
1637 // swap red and blue colours to match internal opengl texture format
\r
1638 ColorMatrix colorMatrix = new ColorMatrix();
\r
1640 float[] colorTransform = { 0, 0, 1.0f, 0, 0,
\r
1643 0, 0, 0, 1.0f, 0 };
\r
1645 colorMatrix.set (colorTransform);
\r
1646 paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
\r
1648 java.lang.reflect.Method method = null;
\r
1652 method = getClass().getMethod ("setLayerType", int.class, Paint.class);
\r
1654 catch (SecurityException e) {}
\r
1655 catch (NoSuchMethodException e) {}
\r
1657 if (method != null)
\r
1661 int layerTypeNone = 0;
\r
1662 method.invoke (this, layerTypeNone, null);
\r
1664 catch (java.lang.IllegalArgumentException e) {}
\r
1665 catch (java.lang.IllegalAccessException e) {}
\r
1666 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1670 //==============================================================================
\r
1671 private native void handlePaint (long host, Canvas canvas, Paint paint);
\r
1674 public void onDraw (Canvas canvas)
\r
1679 handlePaint (host, canvas, paint);
\r
1683 public boolean isOpaque()
\r
1688 private boolean opaque;
\r
1689 private long host;
\r
1690 private Paint paint = new Paint();
\r
1692 //==============================================================================
\r
1693 private native void handleMouseDown (long host, int index, float x, float y, long time);
\r
1694 private native void handleMouseDrag (long host, int index, float x, float y, long time);
\r
1695 private native void handleMouseUp (long host, int index, float x, float y, long time);
\r
1698 public boolean onTouchEvent (MotionEvent event)
\r
1703 int action = event.getAction();
\r
1704 long time = event.getEventTime();
\r
1706 switch (action & MotionEvent.ACTION_MASK)
\r
1708 case MotionEvent.ACTION_DOWN:
\r
1709 handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
1712 case MotionEvent.ACTION_CANCEL:
\r
1713 case MotionEvent.ACTION_UP:
\r
1714 handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time);
\r
1717 case MotionEvent.ACTION_MOVE:
\r
1719 int n = event.getPointerCount();
\r
1720 for (int i = 0; i < n; ++i)
\r
1721 handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
1726 case MotionEvent.ACTION_POINTER_UP:
\r
1728 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
1729 handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
1733 case MotionEvent.ACTION_POINTER_DOWN:
\r
1735 int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
\r
1736 handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time);
\r
1747 //==============================================================================
\r
1748 private native void handleKeyDown (long host, int keycode, int textchar);
\r
1749 private native void handleKeyUp (long host, int keycode, int textchar);
\r
1750 private native void handleBackButton (long host);
\r
1751 private native void handleKeyboardHidden (long host);
\r
1753 public void showKeyboard (String type)
\r
1755 InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
\r
1759 if (type.length() > 0)
\r
1761 imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
\r
1762 imm.setInputMethod (getWindowToken(), type);
\r
1763 keyboardDismissListener.startListening();
\r
1767 imm.hideSoftInputFromWindow (getWindowToken(), 0);
\r
1768 keyboardDismissListener.stopListening();
\r
1773 public void backButtonPressed()
\r
1778 handleBackButton (host);
\r
1782 public boolean onKeyDown (int keyCode, KeyEvent event)
\r
1789 case KeyEvent.KEYCODE_VOLUME_UP:
\r
1790 case KeyEvent.KEYCODE_VOLUME_DOWN:
\r
1791 return super.onKeyDown (keyCode, event);
\r
1792 case KeyEvent.KEYCODE_BACK:
\r
1794 ((Activity) getContext()).onBackPressed();
\r
1802 handleKeyDown (host, keyCode, event.getUnicodeChar());
\r
1807 public boolean onKeyUp (int keyCode, KeyEvent event)
\r
1812 handleKeyUp (host, keyCode, event.getUnicodeChar());
\r
1817 public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
\r
1822 if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
\r
1823 return super.onKeyMultiple (keyCode, count, event);
\r
1825 if (event.getCharacters() != null)
\r
1827 int utf8Char = event.getCharacters().codePointAt (0);
\r
1828 handleKeyDown (host, utf8Char, utf8Char);
\r
1835 //==============================================================================
\r
1836 private final class KeyboardDismissListener
\r
1838 public KeyboardDismissListener (ComponentPeerView viewToUse)
\r
1843 private void startListening()
\r
1845 view.getViewTreeObserver().addOnGlobalLayoutListener(viewTreeObserver);
\r
1848 private void stopListening()
\r
1850 view.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserver);
\r
1853 private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
\r
1857 keyboardShown = false;
\r
1861 public void onGlobalLayout()
\r
1863 Rect r = new Rect();
\r
1865 ViewGroup parentView = (ViewGroup) getParent();
\r
1867 if (parentView == null)
\r
1870 parentView.getWindowVisibleDisplayFrame (r);
\r
1872 int diff = parentView.getHeight() - (r.bottom - r.top);
\r
1874 // Arbitrary threshold, surely keyboard would take more than 20 pix.
\r
1875 if (diff < 20 && keyboardShown)
\r
1877 keyboardShown = false;
\r
1878 handleKeyboardHidden (view.host);
\r
1881 if (! keyboardShown && diff > 20)
\r
1882 keyboardShown = true;
\r
1885 private boolean keyboardShown;
\r
1888 private ComponentPeerView view;
\r
1889 private TreeObserver viewTreeObserver = new TreeObserver();
\r
1892 private KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener(this);
\r
1894 // this is here to make keyboard entry work on a Galaxy Tab2 10.1
\r
1896 public InputConnection onCreateInputConnection (EditorInfo outAttrs)
\r
1898 outAttrs.actionLabel = "";
\r
1899 outAttrs.hintText = "";
\r
1900 outAttrs.initialCapsMode = 0;
\r
1901 outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
\r
1902 outAttrs.label = "";
\r
1903 outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
\r
1904 outAttrs.inputType = InputType.TYPE_NULL;
\r
1906 return new BaseInputConnection (this, false);
\r
1909 //==============================================================================
\r
1911 protected void onSizeChanged (int w, int h, int oldw, int oldh)
\r
1916 super.onSizeChanged (w, h, oldw, oldh);
\r
1917 viewSizeChanged (host);
\r
1921 protected void onLayout (boolean changed, int left, int top, int right, int bottom)
\r
1923 for (int i = getChildCount(); --i >= 0;)
\r
1924 requestTransparentRegion (getChildAt (i));
\r
1927 private native void viewSizeChanged (long host);
\r
1930 public void onFocusChange (View v, boolean hasFocus)
\r
1936 focusChanged (host, hasFocus);
\r
1939 private native void focusChanged (long host, boolean hasFocus);
\r
1941 public void setViewName (String newName) {}
\r
1943 public void setSystemUiVisibilityCompat (int visibility)
\r
1945 Method systemUIVisibilityMethod = null;
\r
1948 systemUIVisibilityMethod = this.getClass().getMethod ("setSystemUiVisibility", int.class);
\r
1950 catch (SecurityException e) { return; }
\r
1951 catch (NoSuchMethodException e) { return; }
\r
1952 if (systemUIVisibilityMethod == null) return;
\r
1956 systemUIVisibilityMethod.invoke (this, visibility);
\r
1958 catch (java.lang.IllegalArgumentException e) {}
\r
1959 catch (java.lang.IllegalAccessException e) {}
\r
1960 catch (java.lang.reflect.InvocationTargetException e) {}
\r
1963 public boolean isVisible() { return getVisibility() == VISIBLE; }
\r
1964 public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
\r
1966 public boolean containsPoint (int x, int y)
\r
1968 return true; //xxx needs to check overlapping views
\r
1971 //==============================================================================
\r
1972 private native void handleAppPaused (long host);
\r
1973 private native void handleAppResumed (long host);
\r
1976 public void appPaused()
\r
1981 handleAppPaused (host);
\r
1985 public void appResumed()
\r
1990 // Ensure that navigation/status bar visibility is correctly restored.
\r
1991 handleAppResumed (host);
\r
1995 //==============================================================================
\r
1996 public static class NativeSurfaceView extends SurfaceView
\r
1997 implements SurfaceHolder.Callback
\r
1999 private long nativeContext = 0;
\r
2001 NativeSurfaceView (Context context, long nativeContextPtr)
\r
2004 nativeContext = nativeContextPtr;
\r
2007 public Surface getNativeSurface()
\r
2009 Surface retval = null;
\r
2011 SurfaceHolder holder = getHolder();
\r
2012 if (holder != null)
\r
2013 retval = holder.getSurface();
\r
2018 //==============================================================================
\r
2020 public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
\r
2022 surfaceChangedNative (nativeContext, holder, format, width, height);
\r
2026 public void surfaceCreated (SurfaceHolder holder)
\r
2028 surfaceCreatedNative (nativeContext, holder);
\r
2032 public void surfaceDestroyed (SurfaceHolder holder)
\r
2034 surfaceDestroyedNative (nativeContext, holder);
\r
2038 protected void dispatchDraw (Canvas canvas)
\r
2040 super.dispatchDraw (canvas);
\r
2041 dispatchDrawNative (nativeContext, canvas);
\r
2044 //==============================================================================
\r
2046 protected void onAttachedToWindow ()
\r
2048 super.onAttachedToWindow();
\r
2049 getHolder().addCallback (this);
\r
2053 protected void onDetachedFromWindow ()
\r
2055 super.onDetachedFromWindow();
\r
2056 getHolder().removeCallback (this);
\r
2059 //==============================================================================
\r
2060 private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
\r
2061 private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
\r
2062 private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
\r
2063 private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
\r
2064 int format, int width, int height);
\r
2067 public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
\r
2069 return new NativeSurfaceView (this, nativeSurfacePtr);
\r
2072 //==============================================================================
\r
2073 public final int[] renderGlyph (char glyph1, char glyph2, Paint paint, android.graphics.Matrix matrix, Rect bounds)
\r
2075 Path p = new Path();
\r
2077 char[] str = { glyph1, glyph2 };
\r
2078 paint.getTextPath (str, 0, (glyph2 != 0 ? 2 : 1), 0.0f, 0.0f, p);
\r
2080 RectF boundsF = new RectF();
\r
2081 p.computeBounds (boundsF, true);
\r
2082 matrix.mapRect (boundsF);
\r
2084 boundsF.roundOut (bounds);
\r
2088 final int w = bounds.width();
\r
2089 final int h = Math.max (1, bounds.height());
\r
2091 Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
\r
2093 Canvas c = new Canvas (bm);
\r
2094 matrix.postTranslate (-bounds.left, -bounds.top);
\r
2095 c.setMatrix (matrix);
\r
2096 c.drawPath (p, paint);
\r
2098 final int sizeNeeded = w * h;
\r
2099 if (cachedRenderArray.length < sizeNeeded)
\r
2100 cachedRenderArray = new int [sizeNeeded];
\r
2102 bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
\r
2104 return cachedRenderArray;
\r
2107 private int[] cachedRenderArray = new int [256];
\r
2109 //==============================================================================
\r
2110 public static class NativeInvocationHandler implements InvocationHandler
\r
2112 public NativeInvocationHandler (Activity activityToUse, long nativeContextRef)
\r
2114 activity = activityToUse;
\r
2115 nativeContext = nativeContextRef;
\r
2118 public void nativeContextDeleted()
\r
2120 nativeContext = 0;
\r
2124 public void finalize()
\r
2126 activity.runOnUiThread (new Runnable()
\r
2131 if (nativeContext != 0)
\r
2132 dispatchFinalize (nativeContext);
\r
2138 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
\r
2140 return dispatchInvoke (nativeContext, proxy, method, args);
\r
2143 //==============================================================================
\r
2144 Activity activity;
\r
2145 private long nativeContext = 0;
\r
2147 private native void dispatchFinalize (long nativeContextRef);
\r
2148 private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
\r
2151 public InvocationHandler createInvocationHandler (long nativeContextRef)
\r
2153 return new NativeInvocationHandler (this, nativeContextRef);
\r
2156 public void invocationHandlerContextDeleted (InvocationHandler handler)
\r
2158 ((NativeInvocationHandler) handler).nativeContextDeleted();
\r
2161 //==============================================================================
\r
2162 public static class HTTPStream
\r
2164 public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse,
\r
2165 String headersToUse, int timeOutMsToUse,
\r
2166 int[] statusCodeToUse, StringBuffer responseHeadersToUse,
\r
2167 int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
\r
2169 isPost = isPostToUse;
\r
2170 postData = postDataToUse;
\r
2171 headers = headersToUse;
\r
2172 timeOutMs = timeOutMsToUse;
\r
2173 statusCode = statusCodeToUse;
\r
2174 responseHeaders = responseHeadersToUse;
\r
2176 numRedirectsToFollow = numRedirectsToFollowToUse;
\r
2177 httpRequestCmd = httpRequestCmdToUse;
\r
2179 connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd);
\r
2182 private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData,
\r
2183 String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
\r
2185 HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
\r
2189 newConnection.setInstanceFollowRedirects (false);
\r
2190 newConnection.setConnectTimeout (timeOutMs);
\r
2191 newConnection.setReadTimeout (timeOutMs);
\r
2193 // 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
2194 // So convert headers string to an array, with an element for each line
\r
2195 String headerLines[] = headers.split("\\n");
\r
2197 // Set request headers
\r
2198 for (int i = 0; i < headerLines.length; ++i)
\r
2200 int pos = headerLines[i].indexOf (":");
\r
2202 if (pos > 0 && pos < headerLines[i].length())
\r
2204 String field = headerLines[i].substring (0, pos);
\r
2205 String value = headerLines[i].substring (pos + 1);
\r
2207 if (value.length() > 0)
\r
2208 newConnection.setRequestProperty (field, value);
\r
2212 newConnection.setRequestMethod (httpRequestCmd);
\r
2216 newConnection.setDoOutput (true);
\r
2218 if (postData != null)
\r
2220 OutputStream out = newConnection.getOutputStream();
\r
2221 out.write(postData);
\r
2226 return newConnection;
\r
2228 catch (Throwable e)
\r
2230 newConnection.disconnect();
\r
2231 throw new IOException ("Connection error");
\r
2235 private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException
\r
2237 synchronized (createFutureLock)
\r
2239 if (hasBeenCancelled.get())
\r
2242 streamFuture = executor.submit (new Callable<BufferedInputStream>()
\r
2245 public BufferedInputStream call() throws IOException
\r
2247 return new BufferedInputStream (isInput ? connection.getInputStream()
\r
2248 : connection.getErrorStream());
\r
2255 return streamFuture.get();
\r
2257 catch (InterruptedException e)
\r
2261 catch (CancellationException e)
\r
2267 public final boolean connect()
\r
2269 boolean result = false;
\r
2270 int numFollowedRedirects = 0;
\r
2274 result = doConnect();
\r
2279 if (++numFollowedRedirects > numRedirectsToFollow)
\r
2282 int status = statusCode[0];
\r
2284 if (status == 301 || status == 302 || status == 303 || status == 307)
\r
2286 // Assumes only one occurrence of "Location"
\r
2287 int pos1 = responseHeaders.indexOf ("Location:") + 10;
\r
2288 int pos2 = responseHeaders.indexOf ("\n", pos1);
\r
2292 String currentLocation = connection.getURL().toString();
\r
2293 String newLocation = responseHeaders.substring (pos1, pos2);
\r
2297 // Handle newLocation whether it's absolute or relative
\r
2298 URL baseUrl = new URL (currentLocation);
\r
2299 URL newUrl = new URL (baseUrl, newLocation);
\r
2300 String transformedNewLocation = newUrl.toString();
\r
2302 if (transformedNewLocation != currentLocation)
\r
2304 // Clear responseHeaders before next iteration
\r
2305 responseHeaders.delete (0, responseHeaders.length());
\r
2307 synchronized (createStreamLock)
\r
2309 if (hasBeenCancelled.get())
\r
2312 connection.disconnect();
\r
2316 connection = createConnection (transformedNewLocation, isPost,
\r
2317 postData, headers, timeOutMs,
\r
2320 catch (Throwable e)
\r
2331 catch (Throwable e)
\r
2350 private final boolean doConnect()
\r
2352 synchronized (createStreamLock)
\r
2354 if (hasBeenCancelled.get())
\r
2361 inputStream = getCancellableStream (true);
\r
2363 catch (ExecutionException e)
\r
2365 if (connection.getResponseCode() < 400)
\r
2367 statusCode[0] = connection.getResponseCode();
\r
2368 connection.disconnect();
\r
2374 statusCode[0] = connection.getResponseCode();
\r
2379 if (statusCode[0] >= 400)
\r
2380 inputStream = getCancellableStream (false);
\r
2382 inputStream = getCancellableStream (true);
\r
2384 catch (ExecutionException e)
\r
2387 for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
\r
2389 if (entry.getKey() != null && entry.getValue() != null)
\r
2391 responseHeaders.append(entry.getKey() + ": "
\r
2392 + android.text.TextUtils.join(",", entry.getValue()) + "\n");
\r
2394 if (entry.getKey().compareTo ("Content-Length") == 0)
\r
2395 totalLength = Integer.decode (entry.getValue().get (0));
\r
2401 catch (IOException e)
\r
2408 static class DisconnectionRunnable implements Runnable
\r
2410 public DisconnectionRunnable (HttpURLConnection theConnection,
\r
2411 InputStream theInputStream,
\r
2412 ReentrantLock theCreateStreamLock,
\r
2413 Object theCreateFutureLock,
\r
2414 Future<BufferedInputStream> theStreamFuture)
\r
2416 connectionToDisconnect = theConnection;
\r
2417 inputStream = theInputStream;
\r
2418 createStreamLock = theCreateStreamLock;
\r
2419 createFutureLock = theCreateFutureLock;
\r
2420 streamFuture = theStreamFuture;
\r
2427 if (! createStreamLock.tryLock())
\r
2429 synchronized (createFutureLock)
\r
2431 if (streamFuture != null)
\r
2432 streamFuture.cancel (true);
\r
2435 createStreamLock.lock();
\r
2438 if (connectionToDisconnect != null)
\r
2439 connectionToDisconnect.disconnect();
\r
2441 if (inputStream != null)
\r
2442 inputStream.close();
\r
2444 catch (IOException e)
\r
2448 createStreamLock.unlock();
\r
2452 private HttpURLConnection connectionToDisconnect;
\r
2453 private InputStream inputStream;
\r
2454 private ReentrantLock createStreamLock;
\r
2455 private Object createFutureLock;
\r
2456 Future<BufferedInputStream> streamFuture;
\r
2459 public final void release()
\r
2461 DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable (connection,
\r
2467 synchronized (createStreamLock)
\r
2469 hasBeenCancelled.set (true);
\r
2471 connection = null;
\r
2474 Thread disconnectionThread = new Thread(disconnectionRunnable);
\r
2475 disconnectionThread.start();
\r
2478 public final int read (byte[] buffer, int numBytes)
\r
2484 synchronized (createStreamLock)
\r
2486 if (inputStream != null)
\r
2487 num = inputStream.read (buffer, 0, numBytes);
\r
2490 catch (IOException e)
\r
2499 public final long getPosition() { return position; }
\r
2500 public final long getTotalLength() { return totalLength; }
\r
2501 public final boolean isExhausted() { return false; }
\r
2502 public final boolean setPosition (long newPos) { return false; }
\r
2504 private boolean isPost;
\r
2505 private byte[] postData;
\r
2506 private String headers;
\r
2507 private int timeOutMs;
\r
2508 String httpRequestCmd;
\r
2509 private HttpURLConnection connection;
\r
2510 private int[] statusCode;
\r
2511 private StringBuffer responseHeaders;
\r
2512 private int totalLength;
\r
2513 private int numRedirectsToFollow;
\r
2514 private InputStream inputStream;
\r
2515 private long position;
\r
2516 private final ReentrantLock createStreamLock = new ReentrantLock();
\r
2517 private final Object createFutureLock = new Object();
\r
2518 private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
\r
2520 private final ExecutorService executor = Executors.newCachedThreadPool (Executors.defaultThreadFactory());
\r
2521 Future<BufferedInputStream> streamFuture;
\r
2524 public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
\r
2525 String headers, int timeOutMs, int[] statusCode,
\r
2526 StringBuffer responseHeaders, int numRedirectsToFollow,
\r
2527 String httpRequestCmd)
\r
2529 // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
\r
2530 if (timeOutMs < 0)
\r
2532 else if (timeOutMs == 0)
\r
2533 timeOutMs = 30000;
\r
2539 HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers,
\r
2540 timeOutMs, statusCode, responseHeaders,
\r
2541 numRedirectsToFollow, httpRequestCmd);
\r
2543 return httpStream;
\r
2545 catch (Throwable e) {}
\r
2551 public final void launchURL (String url)
\r
2553 startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
\r
2556 private native boolean webViewPageLoadStarted (long host, WebView view, String url);
\r
2557 private native void webViewPageLoadFinished (long host, WebView view, String url);
\r
2558 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error); private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse); private native void webViewReceivedSslError (long host, WebView view, SslErrorHandler handler, SslError error);
\r
2559 private native void webViewCloseWindowRequest (long host, WebView view);
\r
2560 private native void webViewCreateWindowRequest (long host, WebView view);
\r
2562 //==============================================================================
\r
2563 public class JuceWebViewClient extends WebViewClient
\r
2565 public JuceWebViewClient (long hostToUse)
\r
2570 public void hostDeleted()
\r
2572 synchronized (hostLock)
\r
2579 public void onPageFinished (WebView view, String url)
\r
2584 webViewPageLoadFinished (host, view, url);
\r
2588 public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
\r
2593 webViewReceivedSslError (host, view, handler, error);
\r
2597 public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
\r
2602 webViewReceivedError (host, view, request, error);
\r
2606 public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
\r
2611 webViewReceivedHttpError (host, view, request, errorResponse);
\r
2615 public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
\r
2617 synchronized (hostLock)
\r
2621 boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
\r
2628 return new WebResourceResponse ("text/html", null, null);
\r
2631 private long host;
\r
2632 private final Object hostLock = new Object();
\r
2635 public class JuceWebChromeClient extends WebChromeClient
\r
2637 public JuceWebChromeClient (long hostToUse)
\r
2643 public void onCloseWindow (WebView window)
\r
2645 webViewCloseWindowRequest (host, window);
\r
2649 public boolean onCreateWindow (WebView view, boolean isDialog,
\r
2650 boolean isUserGesture, Message resultMsg)
\r
2652 webViewCreateWindowRequest (host, view);
\r
2656 private long host;
\r
2657 private final Object hostLock = new Object();
\r
2661 //==============================================================================
\r
2662 public class CameraDeviceStateCallback extends CameraDevice.StateCallback
\r
2664 private native void cameraDeviceStateClosed (long host, CameraDevice camera);
\r
2665 private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
\r
2666 private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
\r
2667 private native void cameraDeviceStateOpened (long host, CameraDevice camera);
\r
2669 CameraDeviceStateCallback (long hostToUse)
\r
2675 public void onClosed (CameraDevice camera)
\r
2677 cameraDeviceStateClosed (host, camera);
\r
2681 public void onDisconnected (CameraDevice camera)
\r
2683 cameraDeviceStateDisconnected (host, camera);
\r
2687 public void onError (CameraDevice camera, int error)
\r
2689 cameraDeviceStateError (host, camera, error);
\r
2693 public void onOpened (CameraDevice camera)
\r
2695 cameraDeviceStateOpened (host, camera);
\r
2698 private long host;
\r
2701 //==============================================================================
\r
2702 public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
\r
2704 private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
\r
2705 private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
\r
2706 private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
\r
2707 private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
\r
2708 private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
\r
2710 CameraCaptureSessionStateCallback (long hostToUse)
\r
2716 public void onActive (CameraCaptureSession session)
\r
2718 cameraCaptureSessionActive (host, session);
\r
2722 public void onClosed (CameraCaptureSession session)
\r
2724 cameraCaptureSessionClosed (host, session);
\r
2728 public void onConfigureFailed (CameraCaptureSession session)
\r
2730 cameraCaptureSessionConfigureFailed (host, session);
\r
2734 public void onConfigured (CameraCaptureSession session)
\r
2736 cameraCaptureSessionConfigured (host, session);
\r
2740 public void onReady (CameraCaptureSession session)
\r
2742 cameraCaptureSessionReady (host, session);
\r
2745 private long host;
\r
2748 //==============================================================================
\r
2749 public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
\r
2751 private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
\r
2752 private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
\r
2753 private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
\r
2754 private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
\r
2755 private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
\r
2756 private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
\r
2758 CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
\r
2761 preview = shouldBePreview;
\r
2765 public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
\r
2766 TotalCaptureResult result)
\r
2768 cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
\r
2772 public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
\r
2774 cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
\r
2778 public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
\r
2779 CaptureResult partialResult)
\r
2781 cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
\r
2785 public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
\r
2787 cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
\r
2791 public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
\r
2793 cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
\r
2797 public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
\r
2800 cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
\r
2803 private long host;
\r
2804 private boolean preview;
\r
2807 //==============================================================================
\r
2808 public class JuceOrientationEventListener extends OrientationEventListener
\r
2810 private native void deviceOrientationChanged (long host, int orientation);
\r
2812 public JuceOrientationEventListener (long hostToUse, Context context, int rate)
\r
2814 super (context, rate);
\r
2820 public void onOrientationChanged (int orientation)
\r
2822 deviceOrientationChanged (host, orientation);
\r
2825 private long host;
\r
2829 //==============================================================================
\r
2830 public static final String getLocaleValue (boolean isRegion)
\r
2832 java.util.Locale locale = java.util.Locale.getDefault();
\r
2834 return isRegion ? locale.getCountry()
\r
2835 : locale.getLanguage();
\r
2838 private static final String getFileLocation (String type)
\r
2840 return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath();
\r
2843 public static final String getDocumentsFolder()
\r
2845 if (getAndroidSDKVersion() >= 19)
\r
2846 return getFileLocation ("Documents");
\r
2848 return Environment.getDataDirectory().getAbsolutePath();
\r
2851 public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); }
\r
2852 public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); }
\r
2853 public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); }
\r
2854 public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
\r
2856 //==============================================================================
\r
2858 protected void onActivityResult (int requestCode, int resultCode, Intent data)
\r
2860 appActivityResult (requestCode, resultCode, data);
\r
2864 protected void onNewIntent (Intent intent)
\r
2866 super.onNewIntent(intent);
\r
2867 setIntent(intent);
\r
2869 appNewIntent (intent);
\r
2872 //==============================================================================
\r
2873 public final Typeface getTypeFaceFromAsset (String assetName)
\r
2877 return Typeface.createFromAsset (this.getResources().getAssets(), assetName);
\r
2879 catch (Throwable e) {}
\r
2884 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
\r
2886 public static String bytesToHex (byte[] bytes)
\r
2888 char[] hexChars = new char[bytes.length * 2];
\r
2890 for (int j = 0; j < bytes.length; ++j)
\r
2892 int v = bytes[j] & 0xff;
\r
2893 hexChars[j * 2] = hexArray[v >>> 4];
\r
2894 hexChars[j * 2 + 1] = hexArray[v & 0x0f];
\r
2897 return new String (hexChars);
\r
2900 final private java.util.Map dataCache = new java.util.HashMap();
\r
2902 synchronized private final File getDataCacheFile (byte[] data)
\r
2906 java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5");
\r
2907 digest.update (data);
\r
2909 String key = bytesToHex (digest.digest());
\r
2911 if (dataCache.containsKey (key))
\r
2912 return (File) dataCache.get (key);
\r
2914 File f = new File (this.getCacheDir(), "bindata_" + key);
\r
2916 FileOutputStream os = new FileOutputStream (f);
\r
2917 os.write (data, 0, data.length);
\r
2918 dataCache.put (key, f);
\r
2921 catch (Throwable e) {}
\r
2926 private final void clearDataCache()
\r
2928 java.util.Iterator it = dataCache.values().iterator();
\r
2930 while (it.hasNext())
\r
2932 File f = (File) it.next();
\r
2937 public final Typeface getTypeFaceFromByteArray (byte[] data)
\r
2941 File f = getDataCacheFile (data);
\r
2944 return Typeface.createFromFile (f);
\r
2946 catch (Exception e)
\r
2948 Log.e ("JUCE", e.toString());
\r
2954 public static final int getAndroidSDKVersion()
\r
2956 return android.os.Build.VERSION.SDK_INT;
\r
2959 public final String audioManagerGetProperty (String property)
\r
2961 Object obj = getSystemService (AUDIO_SERVICE);
\r
2965 java.lang.reflect.Method method;
\r
2969 method = obj.getClass().getMethod ("getProperty", String.class);
\r
2971 catch (SecurityException e) { return null; }
\r
2972 catch (NoSuchMethodException e) { return null; }
\r
2974 if (method == null)
\r
2979 return (String) method.invoke (obj, property);
\r
2981 catch (java.lang.IllegalArgumentException e) {}
\r
2982 catch (java.lang.IllegalAccessException e) {}
\r
2983 catch (java.lang.reflect.InvocationTargetException e) {}
\r
2988 public final boolean hasSystemFeature (String property)
\r
2990 return getPackageManager().hasSystemFeature (property);
\r