upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -1,169 +0,0 @@
|
||||
$$CameraApi21
|
||||
//==============================================================================
|
||||
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
|
||||
{
|
||||
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
|
||||
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
|
||||
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
|
||||
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
|
||||
|
||||
CameraDeviceStateCallback (long hostToUse)
|
||||
{
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed (CameraDevice camera)
|
||||
{
|
||||
cameraDeviceStateClosed (host, camera);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected (CameraDevice camera)
|
||||
{
|
||||
cameraDeviceStateDisconnected (host, camera);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError (CameraDevice camera, int error)
|
||||
{
|
||||
cameraDeviceStateError (host, camera, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpened (CameraDevice camera)
|
||||
{
|
||||
cameraDeviceStateOpened (host, camera);
|
||||
}
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
|
||||
{
|
||||
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
|
||||
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
|
||||
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
|
||||
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
|
||||
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
|
||||
|
||||
CameraCaptureSessionStateCallback (long hostToUse)
|
||||
{
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActive (CameraCaptureSession session)
|
||||
{
|
||||
cameraCaptureSessionActive (host, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed (CameraCaptureSession session)
|
||||
{
|
||||
cameraCaptureSessionClosed (host, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed (CameraCaptureSession session)
|
||||
{
|
||||
cameraCaptureSessionConfigureFailed (host, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured (CameraCaptureSession session)
|
||||
{
|
||||
cameraCaptureSessionConfigured (host, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady (CameraCaptureSession session)
|
||||
{
|
||||
cameraCaptureSessionReady (host, session);
|
||||
}
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
|
||||
{
|
||||
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
|
||||
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
|
||||
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
|
||||
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
|
||||
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
|
||||
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
|
||||
|
||||
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
|
||||
{
|
||||
host = hostToUse;
|
||||
preview = shouldBePreview;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
|
||||
TotalCaptureResult result)
|
||||
{
|
||||
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
|
||||
{
|
||||
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
|
||||
CaptureResult partialResult)
|
||||
{
|
||||
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
|
||||
{
|
||||
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
|
||||
{
|
||||
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
|
||||
long frameNumber)
|
||||
{
|
||||
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
|
||||
}
|
||||
|
||||
private long host;
|
||||
private boolean preview;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public class JuceOrientationEventListener extends OrientationEventListener
|
||||
{
|
||||
private native void deviceOrientationChanged (long host, int orientation);
|
||||
|
||||
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
|
||||
{
|
||||
super (context, rate);
|
||||
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrientationChanged (int orientation)
|
||||
{
|
||||
deviceOrientationChanged (host, orientation);
|
||||
}
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
CameraApi21$$
|
@ -1,999 +0,0 @@
|
||||
//==============================================================================
|
||||
public class BluetoothManager extends ScanCallback
|
||||
{
|
||||
BluetoothManager()
|
||||
{
|
||||
}
|
||||
|
||||
public String[] getMidiBluetoothAddresses()
|
||||
{
|
||||
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
|
||||
}
|
||||
|
||||
public String getHumanReadableStringForBluetoothAddress (String address)
|
||||
{
|
||||
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
|
||||
return btDevice.getName();
|
||||
}
|
||||
|
||||
public int getBluetoothDeviceStatus (String address)
|
||||
{
|
||||
return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
|
||||
}
|
||||
|
||||
public void startStopScan (boolean shouldStart)
|
||||
{
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (bluetoothAdapter == null)
|
||||
{
|
||||
Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
|
||||
|
||||
if (bluetoothLeScanner == null)
|
||||
{
|
||||
Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldStart)
|
||||
{
|
||||
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
|
||||
scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
|
||||
|
||||
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
|
||||
scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
||||
.setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
|
||||
.setScanMode (ScanSettings.MATCH_MODE_STICKY);
|
||||
|
||||
bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
|
||||
scanSettingsBuilder.build(),
|
||||
this);
|
||||
}
|
||||
else
|
||||
{
|
||||
bluetoothLeScanner.stopScan (this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pairBluetoothMidiDevice(String address)
|
||||
{
|
||||
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
|
||||
|
||||
if (btDevice == null)
|
||||
{
|
||||
Log.d ("JUCE", "failed to create buletooth device from address");
|
||||
return false;
|
||||
}
|
||||
|
||||
return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
|
||||
}
|
||||
|
||||
public void unpairBluetoothMidiDevice (String address)
|
||||
{
|
||||
getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
|
||||
}
|
||||
|
||||
public void onScanFailed (int errorCode)
|
||||
{
|
||||
}
|
||||
|
||||
public void onScanResult (int callbackType, ScanResult result)
|
||||
{
|
||||
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
|
||||
|| callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
|
||||
{
|
||||
BluetoothDevice device = result.getDevice();
|
||||
|
||||
if (device != null)
|
||||
bluetoothMidiDevices.add (device.getAddress());
|
||||
}
|
||||
|
||||
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
|
||||
{
|
||||
Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
|
||||
BluetoothDevice device = result.getDevice();
|
||||
|
||||
if (device != null)
|
||||
{
|
||||
bluetoothMidiDevices.remove (device.getAddress());
|
||||
unpairBluetoothMidiDevice (device.getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBatchScanResults (List<ScanResult> results)
|
||||
{
|
||||
for (ScanResult result : results)
|
||||
onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
|
||||
}
|
||||
|
||||
private BluetoothLeScanner scanner;
|
||||
private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
|
||||
|
||||
private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
|
||||
}
|
||||
|
||||
public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
|
||||
{
|
||||
private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
|
||||
|
||||
public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
|
||||
{
|
||||
owner = mm;
|
||||
androidPort = actualPort;
|
||||
portPath = portPathToUse;
|
||||
juceHost = hostToUse;
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable
|
||||
{
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputPort()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
if (owner != null && androidPort != null && ! isConnected) {
|
||||
androidPort.connect(this);
|
||||
isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if (owner != null && androidPort != null && isConnected) {
|
||||
androidPort.disconnect(this);
|
||||
isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (androidPort != null) {
|
||||
try {
|
||||
androidPort.close();
|
||||
} catch (IOException exception) {
|
||||
Log.d("JUCE", "IO Exception while closing port");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner != null)
|
||||
owner.removePort (portPath);
|
||||
|
||||
owner = null;
|
||||
androidPort = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSend (byte[] msg, int offset, int count, long timestamp)
|
||||
{
|
||||
if (count > 0)
|
||||
handleReceive (juceHost, msg, offset, count, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlush()
|
||||
{}
|
||||
|
||||
@Override
|
||||
public void sendMidi (byte[] msg, int offset, int count)
|
||||
{
|
||||
}
|
||||
|
||||
MidiDeviceManager owner;
|
||||
MidiOutputPort androidPort;
|
||||
MidiPortPath portPath;
|
||||
long juceHost;
|
||||
boolean isConnected;
|
||||
}
|
||||
|
||||
public static class JuceMidiOutputPort implements JuceMidiPort
|
||||
{
|
||||
public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
|
||||
{
|
||||
owner = mm;
|
||||
androidPort = actualPort;
|
||||
portPath = portPathToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable
|
||||
{
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputPort()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMidi (byte[] msg, int offset, int count)
|
||||
{
|
||||
if (androidPort != null)
|
||||
{
|
||||
try {
|
||||
androidPort.send(msg, offset, count);
|
||||
} catch (IOException exception)
|
||||
{
|
||||
Log.d ("JUCE", "send midi had IO exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (androidPort != null) {
|
||||
try {
|
||||
androidPort.close();
|
||||
} catch (IOException exception) {
|
||||
Log.d("JUCE", "IO Exception while closing port");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner != null)
|
||||
owner.removePort (portPath);
|
||||
|
||||
owner = null;
|
||||
androidPort = null;
|
||||
}
|
||||
|
||||
MidiDeviceManager owner;
|
||||
MidiInputPort androidPort;
|
||||
MidiPortPath portPath;
|
||||
}
|
||||
|
||||
private static class MidiPortPath extends Object
|
||||
{
|
||||
public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
|
||||
{
|
||||
deviceId = deviceIdToUse;
|
||||
isInput = direction;
|
||||
portIndex = androidIndex;
|
||||
|
||||
}
|
||||
|
||||
public int deviceId;
|
||||
public int portIndex;
|
||||
public boolean isInput;
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
|
||||
return i.hashCode() * (isInput ? -1 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
|
||||
MidiPortPath other = (MidiPortPath) obj;
|
||||
return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
|
||||
{
|
||||
//==============================================================================
|
||||
private class DummyBluetoothGattCallback extends BluetoothGattCallback
|
||||
{
|
||||
public DummyBluetoothGattCallback (MidiDeviceManager mm)
|
||||
{
|
||||
super();
|
||||
owner = mm;
|
||||
}
|
||||
|
||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
|
||||
{
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED)
|
||||
{
|
||||
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
|
||||
owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
|
||||
}
|
||||
}
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
|
||||
|
||||
private MidiDeviceManager owner;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private class MidiDeviceOpenTask extends java.util.TimerTask
|
||||
{
|
||||
public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
|
||||
{
|
||||
owner = deviceManager;
|
||||
midiDevice = device;
|
||||
btGatt = gattToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel()
|
||||
{
|
||||
synchronized (MidiDeviceOpenTask.class)
|
||||
{
|
||||
owner = null;
|
||||
boolean retval = super.cancel();
|
||||
|
||||
if (btGatt != null)
|
||||
{
|
||||
btGatt.disconnect();
|
||||
btGatt.close();
|
||||
|
||||
btGatt = null;
|
||||
}
|
||||
|
||||
if (midiDevice != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
midiDevice.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
|
||||
midiDevice = null;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
public String getBluetoothAddress()
|
||||
{
|
||||
synchronized (MidiDeviceOpenTask.class)
|
||||
{
|
||||
if (midiDevice != null)
|
||||
{
|
||||
MidiDeviceInfo info = midiDevice.getInfo();
|
||||
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
|
||||
{
|
||||
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
|
||||
if (btDevice != null)
|
||||
return btDevice.getAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public BluetoothGatt getGatt() { return btGatt; }
|
||||
|
||||
public int getID()
|
||||
{
|
||||
return midiDevice.getInfo().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
synchronized (MidiDeviceOpenTask.class)
|
||||
{
|
||||
if (owner != null && midiDevice != null)
|
||||
owner.onDeviceOpenedDelayed (midiDevice);
|
||||
}
|
||||
}
|
||||
|
||||
private MidiDeviceManager owner;
|
||||
private MidiDevice midiDevice;
|
||||
private BluetoothGatt btGatt;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public MidiDeviceManager()
|
||||
{
|
||||
manager = (MidiManager) getSystemService (MIDI_SERVICE);
|
||||
|
||||
if (manager == null)
|
||||
{
|
||||
Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
|
||||
return;
|
||||
}
|
||||
|
||||
openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
|
||||
midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
|
||||
openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
|
||||
btDevicesPairing = new HashMap<String, BluetoothGatt>();
|
||||
|
||||
MidiDeviceInfo[] foundDevices = manager.getDevices();
|
||||
for (MidiDeviceInfo info : foundDevices)
|
||||
onDeviceAdded (info);
|
||||
|
||||
manager.registerDeviceCallback (this, null);
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable
|
||||
{
|
||||
manager.unregisterDeviceCallback (this);
|
||||
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
btDevicesPairing.clear();
|
||||
|
||||
for (Integer deviceID : openTasks.keySet())
|
||||
openTasks.get (deviceID).cancel();
|
||||
|
||||
openTasks = null;
|
||||
}
|
||||
|
||||
for (MidiPortPath key : openPorts.keySet())
|
||||
openPorts.get (key).get().close();
|
||||
|
||||
openPorts = null;
|
||||
|
||||
for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
|
||||
{
|
||||
if (device.second != null)
|
||||
{
|
||||
device.second.disconnect();
|
||||
device.second.close();
|
||||
}
|
||||
|
||||
device.first.close();
|
||||
}
|
||||
|
||||
midiDevices.clear();
|
||||
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public String[] getJuceAndroidMidiInputDevices()
|
||||
{
|
||||
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
|
||||
}
|
||||
|
||||
public String[] getJuceAndroidMidiOutputDevices()
|
||||
{
|
||||
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
|
||||
}
|
||||
|
||||
private String[] getJuceAndroidMidiDevices (int portType)
|
||||
{
|
||||
// only update the list when JUCE asks for a new list
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
deviceInfos = getDeviceInfos();
|
||||
}
|
||||
|
||||
ArrayList<String> portNames = new ArrayList<String>();
|
||||
|
||||
int index = 0;
|
||||
for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
|
||||
portNames.add (getPortName (portInfo));
|
||||
|
||||
String[] names = new String[portNames.size()];
|
||||
return portNames.toArray (names);
|
||||
}
|
||||
|
||||
private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
|
||||
MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
|
||||
|
||||
if (portInfo != null)
|
||||
{
|
||||
// ports must be opened exclusively!
|
||||
if (openPorts.containsKey (portInfo))
|
||||
return null;
|
||||
|
||||
Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
|
||||
|
||||
if (devicePair != null)
|
||||
{
|
||||
MidiDevice device = devicePair.first;
|
||||
if (device != null)
|
||||
{
|
||||
JuceMidiPort juceMidiPort = null;
|
||||
|
||||
if (isInput)
|
||||
{
|
||||
MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
|
||||
|
||||
if (outputPort != null)
|
||||
juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
|
||||
}
|
||||
else
|
||||
{
|
||||
MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
|
||||
|
||||
if (inputPort != null)
|
||||
juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
|
||||
}
|
||||
|
||||
if (juceMidiPort != null)
|
||||
{
|
||||
openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
|
||||
|
||||
return juceMidiPort;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
|
||||
{
|
||||
return openMidiPortWithJuceIndex (index, host, true);
|
||||
}
|
||||
|
||||
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
|
||||
{
|
||||
return openMidiPortWithJuceIndex (index, 0, false);
|
||||
}
|
||||
|
||||
/* 0: unpaired, 1: paired, 2: pairing */
|
||||
public int getBluetoothDeviceStatus (String address)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
if (! address.isEmpty())
|
||||
{
|
||||
if (findMidiDeviceForBluetoothAddress (address) != null)
|
||||
return 1;
|
||||
|
||||
if (btDevicesPairing.containsKey (address))
|
||||
return 2;
|
||||
|
||||
if (findOpenTaskForBluetoothAddress (address) != null)
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean pairBluetoothDevice (BluetoothDevice btDevice)
|
||||
{
|
||||
String btAddress = btDevice.getAddress();
|
||||
if (btAddress.isEmpty())
|
||||
return false;
|
||||
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
if (getBluetoothDeviceStatus (btAddress) != 0)
|
||||
return false;
|
||||
|
||||
|
||||
btDevicesPairing.put (btDevice.getAddress(), null);
|
||||
BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
|
||||
|
||||
if (gatt != null)
|
||||
{
|
||||
btDevicesPairing.put (btDevice.getAddress(), gatt);
|
||||
}
|
||||
else
|
||||
{
|
||||
pairBluetoothDeviceStepTwo (btDevice);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
|
||||
{
|
||||
manager.openBluetoothDevice(btDevice, this, null);
|
||||
}
|
||||
|
||||
public void unpairBluetoothDevice (String address)
|
||||
{
|
||||
if (address.isEmpty())
|
||||
return;
|
||||
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
if (btDevicesPairing.containsKey (address))
|
||||
{
|
||||
BluetoothGatt gatt = btDevicesPairing.get (address);
|
||||
if (gatt != null)
|
||||
{
|
||||
gatt.disconnect();
|
||||
gatt.close();
|
||||
}
|
||||
|
||||
btDevicesPairing.remove (address);
|
||||
}
|
||||
|
||||
MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
|
||||
if (openTask != null)
|
||||
{
|
||||
int deviceID = openTask.getID();
|
||||
openTask.cancel();
|
||||
openTasks.remove (deviceID);
|
||||
}
|
||||
|
||||
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
|
||||
if (midiDevicePair != null)
|
||||
{
|
||||
MidiDevice midiDevice = midiDevicePair.first;
|
||||
onDeviceRemoved (midiDevice.getInfo());
|
||||
|
||||
try {
|
||||
midiDevice.close();
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
Log.d ("JUCE", "IOException while closing midi device");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
|
||||
{
|
||||
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
|
||||
{
|
||||
MidiDeviceInfo info = midiDevice.first.getInfo();
|
||||
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
|
||||
{
|
||||
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
|
||||
if (btDevice != null && btDevice.getAddress().equals (address))
|
||||
return midiDevice;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
|
||||
{
|
||||
for (Integer deviceID : openTasks.keySet())
|
||||
{
|
||||
MidiDeviceOpenTask openTask = openTasks.get (deviceID);
|
||||
if (openTask.getBluetoothAddress().equals (address))
|
||||
return openTask;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removePort (MidiPortPath path)
|
||||
{
|
||||
openPorts.remove (path);
|
||||
}
|
||||
|
||||
public String getInputPortNameForJuceIndex (int index)
|
||||
{
|
||||
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
|
||||
if (portInfo != null)
|
||||
return getPortName (portInfo);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getOutputPortNameForJuceIndex (int index)
|
||||
{
|
||||
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
|
||||
if (portInfo != null)
|
||||
return getPortName (portInfo);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public void onDeviceAdded (MidiDeviceInfo info)
|
||||
{
|
||||
// only add standard midi devices
|
||||
if (info.getType() == info.TYPE_BLUETOOTH)
|
||||
return;
|
||||
|
||||
manager.openDevice (info, this, null);
|
||||
}
|
||||
|
||||
public void onDeviceRemoved (MidiDeviceInfo info)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
|
||||
|
||||
if (devicePair != null)
|
||||
{
|
||||
MidiDevice midiDevice = devicePair.first;
|
||||
BluetoothGatt gatt = devicePair.second;
|
||||
|
||||
// close all ports that use this device
|
||||
boolean removedPort = true;
|
||||
|
||||
while (removedPort == true)
|
||||
{
|
||||
removedPort = false;
|
||||
for (MidiPortPath key : openPorts.keySet())
|
||||
{
|
||||
if (key.deviceId == info.getId())
|
||||
{
|
||||
openPorts.get(key).get().close();
|
||||
removedPort = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gatt != null)
|
||||
{
|
||||
gatt.disconnect();
|
||||
gatt.close();
|
||||
}
|
||||
|
||||
midiDevices.remove (devicePair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDeviceStatusChanged (MidiDeviceStatus status)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceOpened (MidiDevice theDevice)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
MidiDeviceInfo info = theDevice.getInfo();
|
||||
int deviceID = info.getId();
|
||||
BluetoothGatt gatt = null;
|
||||
boolean isBluetooth = false;
|
||||
|
||||
if (! openTasks.containsKey (deviceID))
|
||||
{
|
||||
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
|
||||
{
|
||||
isBluetooth = true;
|
||||
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
|
||||
if (btDevice != null)
|
||||
{
|
||||
String btAddress = btDevice.getAddress();
|
||||
if (btDevicesPairing.containsKey (btAddress))
|
||||
{
|
||||
gatt = btDevicesPairing.get (btAddress);
|
||||
btDevicesPairing.remove (btAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unpair was called in the mean time
|
||||
try
|
||||
{
|
||||
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
|
||||
if (midiDevicePair != null)
|
||||
{
|
||||
gatt = midiDevicePair.second;
|
||||
|
||||
if (gatt != null)
|
||||
{
|
||||
gatt.disconnect();
|
||||
gatt.close();
|
||||
}
|
||||
}
|
||||
|
||||
theDevice.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
|
||||
openTasks.put (deviceID, openTask);
|
||||
|
||||
new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDeviceOpenedDelayed (MidiDevice theDevice)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
int deviceID = theDevice.getInfo().getId();
|
||||
|
||||
if (openTasks.containsKey (deviceID))
|
||||
{
|
||||
if (! midiDevices.contains(theDevice))
|
||||
{
|
||||
BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
|
||||
openTasks.remove (deviceID);
|
||||
midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// unpair was called in the mean time
|
||||
MidiDeviceInfo info = theDevice.getInfo();
|
||||
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
|
||||
if (btDevice != null)
|
||||
{
|
||||
String btAddress = btDevice.getAddress();
|
||||
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
|
||||
if (midiDevicePair != null)
|
||||
{
|
||||
BluetoothGatt gatt = midiDevicePair.second;
|
||||
|
||||
if (gatt != null)
|
||||
{
|
||||
gatt.disconnect();
|
||||
gatt.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
theDevice.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getPortName(MidiPortPath path)
|
||||
{
|
||||
int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
|
||||
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
for (MidiDeviceInfo info : deviceInfos)
|
||||
{
|
||||
int localIndex = 0;
|
||||
if (info.getId() == path.deviceId)
|
||||
{
|
||||
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
|
||||
{
|
||||
int portType = portInfo.getType();
|
||||
if (portType == portTypeToFind)
|
||||
{
|
||||
int portIndex = portInfo.getPortNumber();
|
||||
if (portIndex == path.portIndex)
|
||||
{
|
||||
String portName = portInfo.getName();
|
||||
if (portName.isEmpty())
|
||||
portName = (String) info.getProperties().get(info.PROPERTY_NAME);
|
||||
|
||||
return portName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
|
||||
{
|
||||
int portIdx = 0;
|
||||
for (MidiDeviceInfo info : deviceInfos)
|
||||
{
|
||||
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
|
||||
{
|
||||
if (portInfo.getType() == portType)
|
||||
{
|
||||
if (portIdx == juceIndex)
|
||||
return new MidiPortPath (info.getId(),
|
||||
(portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
|
||||
portInfo.getPortNumber());
|
||||
|
||||
portIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MidiDeviceInfo[] getDeviceInfos()
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
|
||||
|
||||
int idx = 0;
|
||||
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
|
||||
infos[idx++] = midiDevice.first.getInfo();
|
||||
|
||||
return infos;
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
|
||||
{
|
||||
synchronized (MidiDeviceManager.class)
|
||||
{
|
||||
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
|
||||
if (midiDevice.first.getInfo().getId() == deviceId)
|
||||
return midiDevice;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MidiManager manager;
|
||||
private HashMap<String, BluetoothGatt> btDevicesPairing;
|
||||
private HashMap<Integer, MidiDeviceOpenTask> openTasks;
|
||||
private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
|
||||
private MidiDeviceInfo[] deviceInfos;
|
||||
private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
|
||||
}
|
||||
|
||||
public MidiDeviceManager getAndroidMidiDeviceManager()
|
||||
{
|
||||
if (getSystemService (MIDI_SERVICE) == null)
|
||||
return null;
|
||||
|
||||
synchronized (JuceAppActivity.class)
|
||||
{
|
||||
if (midiDeviceManager == null)
|
||||
midiDeviceManager = new MidiDeviceManager();
|
||||
}
|
||||
|
||||
return midiDeviceManager;
|
||||
}
|
||||
|
||||
public BluetoothManager getAndroidBluetoothManager()
|
||||
{
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (adapter == null)
|
||||
return null;
|
||||
|
||||
if (adapter.getBluetoothLeScanner() == null)
|
||||
return null;
|
||||
|
||||
synchronized (JuceAppActivity.class)
|
||||
{
|
||||
if (bluetoothManager == null)
|
||||
bluetoothManager = new BluetoothManager();
|
||||
}
|
||||
|
||||
return bluetoothManager;
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
//==============================================================================
|
||||
public class BluetoothManager
|
||||
{
|
||||
BluetoothManager()
|
||||
{
|
||||
}
|
||||
|
||||
public String[] getMidiBluetoothAddresses()
|
||||
{
|
||||
String[] bluetoothAddresses = new String[0];
|
||||
return bluetoothAddresses;
|
||||
}
|
||||
|
||||
public String getHumanReadableStringForBluetoothAddress (String address)
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
public int getBluetoothDeviceStatus (String address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void startStopScan (boolean shouldStart)
|
||||
{
|
||||
}
|
||||
|
||||
public boolean pairBluetoothMidiDevice(String address)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void unpairBluetoothMidiDevice (String address)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public class MidiDeviceManager
|
||||
{
|
||||
public MidiDeviceManager()
|
||||
{
|
||||
}
|
||||
|
||||
public String[] getJuceAndroidMidiInputDevices()
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
public String[] getJuceAndroidMidiOutputDevices()
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getInputPortNameForJuceIndex (int index)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getOutputPortNameForJuceIndex (int index)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public MidiDeviceManager getAndroidMidiDeviceManager()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public BluetoothManager getAndroidBluetoothManager()
|
||||
{
|
||||
return null;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
@Override
|
||||
public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults)
|
||||
{
|
||||
boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
if (! permissionsGranted)
|
||||
Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID));
|
||||
|
||||
Long ptrToCallback = permissionCallbackPtrMap.get (permissionID);
|
||||
permissionCallbackPtrMap.remove (permissionID);
|
||||
androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback);
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package com.juce;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.FileObserver;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import java.lang.String;
|
||||
|
||||
public final class SharingContentProvider extends ContentProvider
|
||||
{
|
||||
private Object lock = new Object();
|
||||
|
||||
private native void contentSharerFileObserverEvent (long host, int event, String path);
|
||||
|
||||
private native Cursor contentSharerQuery (Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder);
|
||||
|
||||
private native void contentSharerCursorClosed (long host);
|
||||
|
||||
private native AssetFileDescriptor contentSharerOpenFile (Uri uri, String mode);
|
||||
private native String[] contentSharerGetStreamTypes (Uri uri, String mimeTypeFilter);
|
||||
|
||||
public final class ProviderFileObserver extends FileObserver
|
||||
{
|
||||
public ProviderFileObserver (long hostToUse, String path, int mask)
|
||||
{
|
||||
super (path, mask);
|
||||
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
public void onEvent (int event, String path)
|
||||
{
|
||||
contentSharerFileObserverEvent (host, event, path);
|
||||
}
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
public final class ProviderCursor extends MatrixCursor
|
||||
{
|
||||
ProviderCursor (long hostToUse, String[] columnNames)
|
||||
{
|
||||
super (columnNames);
|
||||
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
super.close();
|
||||
|
||||
contentSharerCursorClosed (host);
|
||||
}
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query (Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerQuery (url, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert (Uri uri, ContentValues values)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update (Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete (Uri uri, String selection, String[] selectionArgs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType (Uri uri)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openAssetFile (Uri uri, String mode)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerOpenFile (uri, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile (Uri uri, String mode)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
AssetFileDescriptor result = contentSharerOpenFile (uri, mode);
|
||||
|
||||
if (result != null)
|
||||
return result.getParcelFileDescriptor();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$$ContentProviderApi11
|
||||
@Override
|
||||
public String[] getStreamTypes (Uri uri, String mimeTypeFilter)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerGetStreamTypes (uri, mimeTypeFilter);
|
||||
}
|
||||
}
|
||||
ContentProviderApi11$$
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
$$WebViewNativeApi23 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error);WebViewNativeApi23$$
|
||||
$$WebViewNativeApi21 private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse);WebViewNativeApi21$$
|
||||
|
||||
$$WebViewApi1_10
|
||||
@Override
|
||||
public void onPageStarted (WebView view, String url, Bitmap favicon)
|
||||
{
|
||||
if (host != 0)
|
||||
webViewPageLoadStarted (host, view, url);
|
||||
}
|
||||
WebViewApi1_10$$
|
||||
|
||||
$$WebViewApi11_20
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
|
||||
{
|
||||
synchronized (hostLock)
|
||||
{
|
||||
if (host != 0)
|
||||
{
|
||||
boolean shouldLoad = webViewPageLoadStarted (host, view, url);
|
||||
|
||||
if (shouldLoad)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new WebResourceResponse ("text/html", null, null);
|
||||
}
|
||||
WebViewApi11_20$$
|
||||
|
||||
$$WebViewApi21
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
|
||||
{
|
||||
synchronized (hostLock)
|
||||
{
|
||||
if (host != 0)
|
||||
{
|
||||
boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
|
||||
|
||||
if (shouldLoad)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new WebResourceResponse ("text/html", null, null);
|
||||
}
|
||||
WebViewApi21$$
|
||||
|
||||
$$WebViewApi23
|
||||
@Override
|
||||
public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewReceivedError (host, view, request, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewReceivedHttpError (host, view, request, errorResponse);
|
||||
}
|
||||
WebViewApi23$$
|
@ -1,971 +0,0 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.android.vending.billing;
|
||||
/**
|
||||
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
|
||||
* This service provides the following features:
|
||||
* 1. Provides a new API to get details of in-app items published for the app including
|
||||
* price, type, title and description.
|
||||
* 2. The purchase flow is synchronous and purchase information is available immediately
|
||||
* after it completes.
|
||||
* 3. Purchase information of in-app purchases is maintained within the Google Play system
|
||||
* till the purchase is consumed.
|
||||
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
|
||||
* in-app items are consumable and thereafter can be purchased again.
|
||||
* 5. An API to get current purchases of the user immediately. This will not contain any
|
||||
* consumed purchases.
|
||||
*
|
||||
* All calls will give a response code with the following possible values
|
||||
* RESULT_OK = 0 - success
|
||||
* RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
|
||||
* RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
|
||||
* RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
|
||||
* RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
|
||||
* RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
|
||||
* RESULT_ERROR = 6 - Fatal error during the API action
|
||||
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
|
||||
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
|
||||
*/
|
||||
public interface IInAppBillingService extends android.os.IInterface
|
||||
{
|
||||
/** Local-side IPC implementation stub class. */
|
||||
public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IInAppBillingService
|
||||
{
|
||||
private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IInAppBillingService";
|
||||
/** Construct the stub at attach it to the interface. */
|
||||
public Stub()
|
||||
{
|
||||
this.attachInterface(this, DESCRIPTOR);
|
||||
}
|
||||
/**
|
||||
* Cast an IBinder object into an com.android.vending.billing.IInAppBillingService interface,
|
||||
* generating a proxy if needed.
|
||||
*/
|
||||
public static com.android.vending.billing.IInAppBillingService asInterface(android.os.IBinder obj)
|
||||
{
|
||||
if ((obj==null)) {
|
||||
return null;
|
||||
}
|
||||
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
|
||||
if (((iin!=null)&&(iin instanceof com.android.vending.billing.IInAppBillingService))) {
|
||||
return ((com.android.vending.billing.IInAppBillingService)iin);
|
||||
}
|
||||
return new com.android.vending.billing.IInAppBillingService.Stub.Proxy(obj);
|
||||
}
|
||||
@Override public android.os.IBinder asBinder()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case INTERFACE_TRANSACTION:
|
||||
{
|
||||
reply.writeString(DESCRIPTOR);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_isBillingSupported:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
int _result = this.isBillingSupported(_arg0, _arg1, _arg2);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(_result);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getSkuDetails:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
android.os.Bundle _arg3;
|
||||
if ((0!=data.readInt())) {
|
||||
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
|
||||
}
|
||||
else {
|
||||
_arg3 = null;
|
||||
}
|
||||
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getBuyIntent:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
java.lang.String _arg3;
|
||||
_arg3 = data.readString();
|
||||
java.lang.String _arg4;
|
||||
_arg4 = data.readString();
|
||||
android.os.Bundle _result = this.getBuyIntent(_arg0, _arg1, _arg2, _arg3, _arg4);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getPurchases:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
java.lang.String _arg3;
|
||||
_arg3 = data.readString();
|
||||
android.os.Bundle _result = this.getPurchases(_arg0, _arg1, _arg2, _arg3);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_consumePurchase:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
int _result = this.consumePurchase(_arg0, _arg1, _arg2);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(_result);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_stub:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
int _result = this.stub(_arg0, _arg1, _arg2);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(_result);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getBuyIntentToReplaceSkus:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.util.List<java.lang.String> _arg2;
|
||||
_arg2 = data.createStringArrayList();
|
||||
java.lang.String _arg3;
|
||||
_arg3 = data.readString();
|
||||
java.lang.String _arg4;
|
||||
_arg4 = data.readString();
|
||||
java.lang.String _arg5;
|
||||
_arg5 = data.readString();
|
||||
android.os.Bundle _result = this.getBuyIntentToReplaceSkus(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getBuyIntentExtraParams:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
java.lang.String _arg3;
|
||||
_arg3 = data.readString();
|
||||
java.lang.String _arg4;
|
||||
_arg4 = data.readString();
|
||||
android.os.Bundle _arg5;
|
||||
if ((0!=data.readInt())) {
|
||||
_arg5 = android.os.Bundle.CREATOR.createFromParcel(data);
|
||||
}
|
||||
else {
|
||||
_arg5 = null;
|
||||
}
|
||||
android.os.Bundle _result = this.getBuyIntentExtraParams(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getPurchaseHistory:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
java.lang.String _arg3;
|
||||
_arg3 = data.readString();
|
||||
android.os.Bundle _arg4;
|
||||
if ((0!=data.readInt())) {
|
||||
_arg4 = android.os.Bundle.CREATOR.createFromParcel(data);
|
||||
}
|
||||
else {
|
||||
_arg4 = null;
|
||||
}
|
||||
android.os.Bundle _result = this.getPurchaseHistory(_arg0, _arg1, _arg2, _arg3, _arg4);
|
||||
reply.writeNoException();
|
||||
if ((_result!=null)) {
|
||||
reply.writeInt(1);
|
||||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
}
|
||||
else {
|
||||
reply.writeInt(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_isBillingSupportedExtraParams:
|
||||
{
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
java.lang.String _arg1;
|
||||
_arg1 = data.readString();
|
||||
java.lang.String _arg2;
|
||||
_arg2 = data.readString();
|
||||
android.os.Bundle _arg3;
|
||||
if ((0!=data.readInt())) {
|
||||
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
|
||||
}
|
||||
else {
|
||||
_arg3 = null;
|
||||
}
|
||||
int _result = this.isBillingSupportedExtraParams(_arg0, _arg1, _arg2, _arg3);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(_result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
private static class Proxy implements com.android.vending.billing.IInAppBillingService
|
||||
{
|
||||
private android.os.IBinder mRemote;
|
||||
Proxy(android.os.IBinder remote)
|
||||
{
|
||||
mRemote = remote;
|
||||
}
|
||||
@Override public android.os.IBinder asBinder()
|
||||
{
|
||||
return mRemote;
|
||||
}
|
||||
public java.lang.String getInterfaceDescriptor()
|
||||
{
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
@Override public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
mRemote.transact(Stub.TRANSACTION_isBillingSupported, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Provides details of a list of SKUs
|
||||
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
|
||||
* with a list JSON strings containing the productId, price, title and description.
|
||||
* This API can be called with a maximum of 20 SKUs.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param type of the in-app items ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "DETAILS_LIST" with a StringArrayList containing purchase information
|
||||
* in JSON format similar to:
|
||||
* '{ "productId" : "exampleSku",
|
||||
* "type" : "inapp",
|
||||
* "price" : "$5.00",
|
||||
* "price_currency": "USD",
|
||||
* "price_amount_micros": 5000000,
|
||||
* "title : "Example Title",
|
||||
* "description" : "This is an example description" }'
|
||||
*/
|
||||
@Override public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
if ((skusBundle!=null)) {
|
||||
_data.writeInt(1);
|
||||
skusBundle.writeToParcel(_data, 0);
|
||||
}
|
||||
else {
|
||||
_data.writeInt(0);
|
||||
}
|
||||
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
|
||||
* the type, a unique purchase token and an optional developer payload.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type of the in-app item being purchased ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
|
||||
* codes on failures.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
*/
|
||||
@Override public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(sku);
|
||||
_data.writeString(type);
|
||||
_data.writeString(developerPayload);
|
||||
mRemote.transact(Stub.TRANSACTION_getBuyIntent, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Returns the current SKUs owned by the user of the type and package name specified along with
|
||||
* purchase information and a signature of the data to be validated.
|
||||
* This will return all SKUs that have been purchased in V3 and managed items purchased using
|
||||
* V1 and V2 that have not been consumed.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param type of the in-app items being requested ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus are too many, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
on failures.
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
@Override public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
_data.writeString(continuationToken);
|
||||
mRemote.transact(Stub.TRANSACTION_getPurchases, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
@Override public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(purchaseToken);
|
||||
mRemote.transact(Stub.TRANSACTION_consumePurchase, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
@Override public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
mRemote.transact(Stub.TRANSACTION_stub, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
|
||||
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
|
||||
* the user is upgrading or downgrading to.
|
||||
* @param apiVersion billing API version that the app is using, must be 5 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
|
||||
* if null or empty this method will behave like {@link #getBuyIntent}
|
||||
* @param newSku the SKU that the user is upgrading or downgrading to
|
||||
* @param type of the item being purchased, currently must be "subs"
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
|
||||
* codes on failures.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
*/
|
||||
@Override public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeStringList(oldSkus);
|
||||
_data.writeString(newSku);
|
||||
_data.writeString(type);
|
||||
_data.writeString(developerPayload);
|
||||
mRemote.transact(Stub.TRANSACTION_getBuyIntentToReplaceSkus, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
|
||||
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
|
||||
* parameter. This parameter is a Bundle of optional keys and values that affect the
|
||||
* operation of the method.
|
||||
* @param apiVersion billing API version that the app is using, must be 6 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type of the in-app item being purchased ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @extraParams a Bundle with the following optional keys:
|
||||
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
|
||||
* upgrading or downgrading from.
|
||||
* Pass this field if the purchase is upgrading or downgrading
|
||||
* existing subscriptions.
|
||||
* The specified SKUs are replaced with the SKUs that the user is
|
||||
* purchasing. Google Play replaces the specified SKUs at the start of
|
||||
* the next billing cycle.
|
||||
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
|
||||
* subscription time on the SKUs they are upgrading or downgrading.
|
||||
* If you set this field to true, Google Play swaps out the old SKUs
|
||||
* and credits the user with the unused value of their subscription
|
||||
* time on a pro-rated basis.
|
||||
* Google Play applies this credit to the new subscription, and does
|
||||
* not begin billing the user for the new subscription until after
|
||||
* the credit is used up.
|
||||
* If you set this field to false, the user does not receive credit for
|
||||
* any unused subscription time and the recurrence date does not
|
||||
* change.
|
||||
* Default value is true. Ignored if you do not pass skusToReplace.
|
||||
* "accountId" - String - an optional obfuscated string that is uniquely
|
||||
* associated with the user's account in your app.
|
||||
* If you pass this value, Google Play can use it to detect irregular
|
||||
* activity, such as many devices making purchases on the same
|
||||
* account in a short period of time.
|
||||
* Do not use the developer ID or the user's Google ID for this field.
|
||||
* In addition, this field should not contain the user's ID in
|
||||
* cleartext.
|
||||
* We recommend that you use a one-way hash to generate a string from
|
||||
* the user's ID, and store the hashed string in this field.
|
||||
* "vr" - Boolean - an optional flag indicating whether the returned intent
|
||||
* should start a VR purchase flow. The apiVersion must also be 7 or
|
||||
* later to use this flag.
|
||||
*/
|
||||
@Override public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(sku);
|
||||
_data.writeString(type);
|
||||
_data.writeString(developerPayload);
|
||||
if ((extraParams!=null)) {
|
||||
_data.writeInt(1);
|
||||
extraParams.writeToParcel(_data, 0);
|
||||
}
|
||||
else {
|
||||
_data.writeInt(0);
|
||||
}
|
||||
mRemote.transact(Stub.TRANSACTION_getBuyIntentExtraParams, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
/**
|
||||
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
|
||||
* expired, canceled, or consumed.
|
||||
* @param apiVersion billing API version that the app is using, must be 6 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param type of the in-app items being requested ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus is too large, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @param extraParams a Bundle with extra params that would be appended into http request
|
||||
* query string. Not used at this moment. Reserved for future functionality.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
|
||||
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
|
||||
*
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
@Override public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
android.os.Bundle _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
_data.writeString(continuationToken);
|
||||
if ((extraParams!=null)) {
|
||||
_data.writeInt(1);
|
||||
extraParams.writeToParcel(_data, 0);
|
||||
}
|
||||
else {
|
||||
_data.writeInt(0);
|
||||
}
|
||||
mRemote.transact(Stub.TRANSACTION_getPurchaseHistory, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
if ((0!=_reply.readInt())) {
|
||||
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
|
||||
}
|
||||
else {
|
||||
_result = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
@Override public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException
|
||||
{
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(apiVersion);
|
||||
_data.writeString(packageName);
|
||||
_data.writeString(type);
|
||||
if ((extraParams!=null)) {
|
||||
_data.writeInt(1);
|
||||
extraParams.writeToParcel(_data, 0);
|
||||
}
|
||||
else {
|
||||
_data.writeInt(0);
|
||||
}
|
||||
mRemote.transact(Stub.TRANSACTION_isBillingSupportedExtraParams, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
}
|
||||
finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
static final int TRANSACTION_isBillingSupported = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
|
||||
static final int TRANSACTION_getSkuDetails = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
|
||||
static final int TRANSACTION_getBuyIntent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
|
||||
static final int TRANSACTION_getPurchases = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
|
||||
static final int TRANSACTION_consumePurchase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
|
||||
static final int TRANSACTION_stub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
|
||||
static final int TRANSACTION_getBuyIntentToReplaceSkus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
|
||||
static final int TRANSACTION_getBuyIntentExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
|
||||
static final int TRANSACTION_getPurchaseHistory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
|
||||
static final int TRANSACTION_isBillingSupportedExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
|
||||
}
|
||||
public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
|
||||
/**
|
||||
* Provides details of a list of SKUs
|
||||
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
|
||||
* with a list JSON strings containing the productId, price, title and description.
|
||||
* This API can be called with a maximum of 20 SKUs.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName the package name of the calling app
|
||||
* @param type of the in-app items ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "DETAILS_LIST" with a StringArrayList containing purchase information
|
||||
* in JSON format similar to:
|
||||
* '{ "productId" : "exampleSku",
|
||||
* "type" : "inapp",
|
||||
* "price" : "$5.00",
|
||||
* "price_currency": "USD",
|
||||
* "price_amount_micros": 5000000,
|
||||
* "title : "Example Title",
|
||||
* "description" : "This is an example description" }'
|
||||
*/
|
||||
public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException;
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
|
||||
* the type, a unique purchase token and an optional developer payload.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type of the in-app item being purchased ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
|
||||
* codes on failures.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
*/
|
||||
public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
|
||||
/**
|
||||
* Returns the current SKUs owned by the user of the type and package name specified along with
|
||||
* purchase information and a signature of the data to be validated.
|
||||
* This will return all SKUs that have been purchased in V3 and managed items purchased using
|
||||
* V1 and V2 that have not been consumed.
|
||||
* @param apiVersion billing API version that the app is using
|
||||
* @param packageName package name of the calling app
|
||||
* @param type of the in-app items being requested ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus are too many, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
on failures.
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException;
|
||||
public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException;
|
||||
public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
|
||||
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
|
||||
* the user is upgrading or downgrading to.
|
||||
* @param apiVersion billing API version that the app is using, must be 5 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
|
||||
* if null or empty this method will behave like {@link #getBuyIntent}
|
||||
* @param newSku the SKU that the user is upgrading or downgrading to
|
||||
* @param type of the item being purchased, currently must be "subs"
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
|
||||
* on failures.
|
||||
* "BUY_INTENT" - PendingIntent to start the purchase flow
|
||||
*
|
||||
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
|
||||
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
|
||||
* If the purchase is successful, the result data will contain the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
|
||||
* codes on failures.
|
||||
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
|
||||
* '{"orderId":"12999763169054705758.1371079406387615",
|
||||
* "packageName":"com.example.app",
|
||||
* "productId":"exampleSku",
|
||||
* "purchaseTime":1345678900000,
|
||||
* "purchaseToken" : "122333444455555",
|
||||
* "developerPayload":"example developer payload" }'
|
||||
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
|
||||
* was signed with the private key of the developer
|
||||
*/
|
||||
public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
|
||||
/**
|
||||
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
|
||||
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
|
||||
* parameter. This parameter is a Bundle of optional keys and values that affect the
|
||||
* operation of the method.
|
||||
* @param apiVersion billing API version that the app is using, must be 6 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param sku the SKU of the in-app item as published in the developer console
|
||||
* @param type of the in-app item being purchased ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param developerPayload optional argument to be sent back with the purchase information
|
||||
* @extraParams a Bundle with the following optional keys:
|
||||
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
|
||||
* upgrading or downgrading from.
|
||||
* Pass this field if the purchase is upgrading or downgrading
|
||||
* existing subscriptions.
|
||||
* The specified SKUs are replaced with the SKUs that the user is
|
||||
* purchasing. Google Play replaces the specified SKUs at the start of
|
||||
* the next billing cycle.
|
||||
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
|
||||
* subscription time on the SKUs they are upgrading or downgrading.
|
||||
* If you set this field to true, Google Play swaps out the old SKUs
|
||||
* and credits the user with the unused value of their subscription
|
||||
* time on a pro-rated basis.
|
||||
* Google Play applies this credit to the new subscription, and does
|
||||
* not begin billing the user for the new subscription until after
|
||||
* the credit is used up.
|
||||
* If you set this field to false, the user does not receive credit for
|
||||
* any unused subscription time and the recurrence date does not
|
||||
* change.
|
||||
* Default value is true. Ignored if you do not pass skusToReplace.
|
||||
* "accountId" - String - an optional obfuscated string that is uniquely
|
||||
* associated with the user's account in your app.
|
||||
* If you pass this value, Google Play can use it to detect irregular
|
||||
* activity, such as many devices making purchases on the same
|
||||
* account in a short period of time.
|
||||
* Do not use the developer ID or the user's Google ID for this field.
|
||||
* In addition, this field should not contain the user's ID in
|
||||
* cleartext.
|
||||
* We recommend that you use a one-way hash to generate a string from
|
||||
* the user's ID, and store the hashed string in this field.
|
||||
* "vr" - Boolean - an optional flag indicating whether the returned intent
|
||||
* should start a VR purchase flow. The apiVersion must also be 7 or
|
||||
* later to use this flag.
|
||||
*/
|
||||
public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException;
|
||||
/**
|
||||
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
|
||||
* expired, canceled, or consumed.
|
||||
* @param apiVersion billing API version that the app is using, must be 6 or later
|
||||
* @param packageName package name of the calling app
|
||||
* @param type of the in-app items being requested ("inapp" for one-time purchases
|
||||
* and "subs" for subscriptions)
|
||||
* @param continuationToken to be set as null for the first call, if the number of owned
|
||||
* skus is too large, a continuationToken is returned in the response bundle.
|
||||
* This method can be called again with the continuation token to get the next set of
|
||||
* owned skus.
|
||||
* @param extraParams a Bundle with extra params that would be appended into http request
|
||||
* query string. Not used at this moment. Reserved for future functionality.
|
||||
* @return Bundle containing the following key-value pairs
|
||||
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
|
||||
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
|
||||
*
|
||||
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
|
||||
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
|
||||
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
|
||||
* of the purchase information
|
||||
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
|
||||
* next set of in-app purchases. Only set if the
|
||||
* user has more owned skus than the current list.
|
||||
*/
|
||||
public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException;
|
||||
public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
||||
package com.juce;
|
||||
|
||||
import com.google.firebase.iid.*;
|
||||
|
||||
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService
|
||||
{
|
||||
private native void firebaseInstanceIdTokenRefreshed (String token);
|
||||
|
||||
@Override
|
||||
public void onTokenRefresh()
|
||||
{
|
||||
String token = FirebaseInstanceId.getInstance().getToken();
|
||||
|
||||
firebaseInstanceIdTokenRefreshed (token);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package com.juce;
|
||||
|
||||
import com.google.firebase.messaging.*;
|
||||
|
||||
public final class JuceFirebaseMessagingService extends FirebaseMessagingService
|
||||
{
|
||||
private native void firebaseRemoteMessageReceived (RemoteMessage message);
|
||||
private native void firebaseRemoteMessagesDeleted();
|
||||
private native void firebaseRemoteMessageSent (String messageId);
|
||||
private native void firebaseRemoteMessageSendError (String messageId, String error);
|
||||
|
||||
@Override
|
||||
public void onMessageReceived (RemoteMessage message)
|
||||
{
|
||||
firebaseRemoteMessageReceived (message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeletedMessages()
|
||||
{
|
||||
firebaseRemoteMessagesDeleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageSent (String messageId)
|
||||
{
|
||||
firebaseRemoteMessageSent (messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendError (String messageId, Exception e)
|
||||
{
|
||||
firebaseRemoteMessageSendError (messageId, e.toString());
|
||||
}
|
||||
}
|
42
modules/juce_core/native/java/README.txt
Normal file
42
modules/juce_core/native/java/README.txt
Normal file
@ -0,0 +1,42 @@
|
||||
The Java code in the module's native/java subfolders have been used to generate
|
||||
dex byte-code in various places in the JUCE framework. These are the steps
|
||||
required to re-generate the dex byte-code from any Java source code inside the
|
||||
native/java subfolders:
|
||||
|
||||
1. Create a new JUCE android project with the minimal sdk version which is
|
||||
required for the Java source code you wish to compile.
|
||||
|
||||
2. If you are creating byte-code for new .java files, move the new files into
|
||||
the native/javacore/app folder of the module, or create one if it doesn't
|
||||
exist. Remember that .java files need to be in nested sub-folders which
|
||||
resemble their package, i.e. a Java class com.roli.juce.HelloWorld.java
|
||||
should be in the module's native/javacore/app/com/roli/juce folder.
|
||||
If you wish to modify existing .java files in the JUCE modules then just rename
|
||||
native/java to native/javacore.
|
||||
|
||||
3. Build your project with AS and run. The app will now use the source code in
|
||||
the folder created in step 2 so you can debug your Java code this way.
|
||||
|
||||
4. Once everything is working rebuild your app in release mode.
|
||||
|
||||
5. Go to your app's Builds/Android folder. Inside there you will find
|
||||
build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes.
|
||||
Inside of that folder, you will find all your Java byte-code compiled classes.
|
||||
Remove any classes that you are not interested in (typically you'll find
|
||||
Java.class, JuceApp.class and JuceSharingContentProvider.class which you will
|
||||
probably want to remove).
|
||||
|
||||
6. Inside of app/build/intermediates/classes/release_/release execute the
|
||||
following dx command:
|
||||
|
||||
<path-to-your-android-sdk>/build-tools/<latest-build-tool-version>/dx --dex --verbose --min-sdk-version=<your-min-sdk-of-your-classes> --output /tmp/JavaDexByteCode.dex .
|
||||
|
||||
(Replace <your-min-sdk-of-your-classes> with the minimal sdk version you used in step 1.)
|
||||
|
||||
7. gzip the output:
|
||||
|
||||
gzip /tmp/JavaDexByteCode.dex
|
||||
|
||||
8. The output /tmp/JavaDexByteCode.dex.gz is now the byte code that can be
|
||||
included into JUCE. You can use the Projucer's BinaryData generator
|
||||
functionality to get this into a convenient char array like form.
|
@ -0,0 +1,58 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class FragmentOverlay extends DialogFragment
|
||||
{
|
||||
@Override
|
||||
public void onCreate (Bundle state)
|
||||
{
|
||||
super.onCreate (state);
|
||||
cppThis = getArguments ().getLong ("cppThis");
|
||||
|
||||
if (cppThis != 0)
|
||||
onCreateNative (cppThis, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart ()
|
||||
{
|
||||
super.onStart ();
|
||||
|
||||
if (cppThis != 0)
|
||||
onStartNative (cppThis);
|
||||
}
|
||||
|
||||
public void onRequestPermissionsResult (int requestCode,
|
||||
String[] permissions,
|
||||
int[] grantResults)
|
||||
{
|
||||
if (cppThis != 0)
|
||||
onRequestPermissionsResultNative (cppThis, requestCode,
|
||||
permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult (int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (cppThis != 0)
|
||||
onActivityResultNative (cppThis, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
public void close ()
|
||||
{
|
||||
cppThis = 0;
|
||||
dismiss ();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private long cppThis = 0;
|
||||
|
||||
private native void onActivityResultNative (long myself, int requestCode, int resultCode, Intent data);
|
||||
private native void onCreateNative (long myself, Bundle state);
|
||||
private native void onStartNative (long myself);
|
||||
private native void onRequestPermissionsResultNative (long myself, int requestCode,
|
||||
String[] permissions, int[] grantResults);
|
||||
}
|
@ -0,0 +1,407 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
public class JuceHTTPStream
|
||||
{
|
||||
public JuceHTTPStream(String address, boolean isPostToUse, byte[] postDataToUse,
|
||||
String headersToUse, int timeOutMsToUse,
|
||||
int[] statusCodeToUse, StringBuffer responseHeadersToUse,
|
||||
int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
|
||||
{
|
||||
isPost = isPostToUse;
|
||||
postData = postDataToUse;
|
||||
headers = headersToUse;
|
||||
timeOutMs = timeOutMsToUse;
|
||||
statusCode = statusCodeToUse;
|
||||
responseHeaders = responseHeadersToUse;
|
||||
totalLength = -1;
|
||||
numRedirectsToFollow = numRedirectsToFollowToUse;
|
||||
httpRequestCmd = httpRequestCmdToUse;
|
||||
|
||||
connection = createConnection(address, isPost, postData, headers, timeOutMs, httpRequestCmd);
|
||||
}
|
||||
|
||||
public static final JuceHTTPStream createHTTPStream(String address, boolean isPost, byte[] postData,
|
||||
String headers, int timeOutMs, int[] statusCode,
|
||||
StringBuffer responseHeaders, int numRedirectsToFollow,
|
||||
String httpRequestCmd)
|
||||
{
|
||||
// timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
|
||||
if (timeOutMs < 0)
|
||||
timeOutMs = 0;
|
||||
else if (timeOutMs == 0)
|
||||
timeOutMs = 30000;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
try
|
||||
{
|
||||
JuceHTTPStream httpStream = new JuceHTTPStream(address, isPost, postData, headers,
|
||||
timeOutMs, statusCode, responseHeaders,
|
||||
numRedirectsToFollow, httpRequestCmd);
|
||||
|
||||
return httpStream;
|
||||
} catch (Throwable e)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final HttpURLConnection createConnection(String address, boolean isPost, byte[] postData,
|
||||
String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
|
||||
{
|
||||
HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
|
||||
|
||||
try
|
||||
{
|
||||
newConnection.setInstanceFollowRedirects(false);
|
||||
newConnection.setConnectTimeout(timeOutMs);
|
||||
newConnection.setReadTimeout(timeOutMs);
|
||||
|
||||
// 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.
|
||||
// So convert headers string to an array, with an element for each line
|
||||
String headerLines[] = headers.split("\\n");
|
||||
|
||||
// Set request headers
|
||||
for (int i = 0; i < headerLines.length; ++i)
|
||||
{
|
||||
int pos = headerLines[i].indexOf(":");
|
||||
|
||||
if (pos > 0 && pos < headerLines[i].length())
|
||||
{
|
||||
String field = headerLines[i].substring(0, pos);
|
||||
String value = headerLines[i].substring(pos + 1);
|
||||
|
||||
if (value.length() > 0)
|
||||
newConnection.setRequestProperty(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
newConnection.setRequestMethod(httpRequestCmd);
|
||||
|
||||
if (isPost)
|
||||
{
|
||||
newConnection.setDoOutput(true);
|
||||
|
||||
if (postData != null)
|
||||
{
|
||||
OutputStream out = newConnection.getOutputStream();
|
||||
out.write(postData);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
return newConnection;
|
||||
} catch (Throwable e)
|
||||
{
|
||||
newConnection.disconnect();
|
||||
throw new IOException("Connection error");
|
||||
}
|
||||
}
|
||||
|
||||
private final InputStream getCancellableStream(final boolean isInput) throws ExecutionException
|
||||
{
|
||||
synchronized (createFutureLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return null;
|
||||
|
||||
streamFuture = executor.submit(new Callable<BufferedInputStream>()
|
||||
{
|
||||
@Override
|
||||
public BufferedInputStream call() throws IOException
|
||||
{
|
||||
return new BufferedInputStream(isInput ? connection.getInputStream()
|
||||
: connection.getErrorStream());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return streamFuture.get();
|
||||
} catch (InterruptedException e)
|
||||
{
|
||||
return null;
|
||||
} catch (CancellationException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean connect()
|
||||
{
|
||||
boolean result = false;
|
||||
int numFollowedRedirects = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
result = doConnect();
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
if (++numFollowedRedirects > numRedirectsToFollow)
|
||||
break;
|
||||
|
||||
int status = statusCode[0];
|
||||
|
||||
if (status == 301 || status == 302 || status == 303 || status == 307)
|
||||
{
|
||||
// Assumes only one occurrence of "Location"
|
||||
int pos1 = responseHeaders.indexOf("Location:") + 10;
|
||||
int pos2 = responseHeaders.indexOf("\n", pos1);
|
||||
|
||||
if (pos2 > pos1)
|
||||
{
|
||||
String currentLocation = connection.getURL().toString();
|
||||
String newLocation = responseHeaders.substring(pos1, pos2);
|
||||
|
||||
try
|
||||
{
|
||||
// Handle newLocation whether it's absolute or relative
|
||||
URL baseUrl = new URL(currentLocation);
|
||||
URL newUrl = new URL(baseUrl, newLocation);
|
||||
String transformedNewLocation = newUrl.toString();
|
||||
|
||||
if (transformedNewLocation != currentLocation)
|
||||
{
|
||||
// Clear responseHeaders before next iteration
|
||||
responseHeaders.delete(0, responseHeaders.length());
|
||||
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return false;
|
||||
|
||||
connection.disconnect();
|
||||
|
||||
try
|
||||
{
|
||||
connection = createConnection(transformedNewLocation, isPost,
|
||||
postData, headers, timeOutMs,
|
||||
httpRequestCmd);
|
||||
} catch (Throwable e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} catch (Throwable e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private final boolean doConnect()
|
||||
{
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
inputStream = getCancellableStream(true);
|
||||
} catch (ExecutionException e)
|
||||
{
|
||||
if (connection.getResponseCode() < 400)
|
||||
{
|
||||
statusCode[0] = connection.getResponseCode();
|
||||
connection.disconnect();
|
||||
return false;
|
||||
}
|
||||
} finally
|
||||
{
|
||||
statusCode[0] = connection.getResponseCode();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (statusCode[0] >= 400)
|
||||
inputStream = getCancellableStream(false);
|
||||
else
|
||||
inputStream = getCancellableStream(true);
|
||||
} catch (ExecutionException e)
|
||||
{
|
||||
}
|
||||
|
||||
for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
|
||||
{
|
||||
if (entry.getKey() != null && entry.getValue() != null)
|
||||
{
|
||||
responseHeaders.append(entry.getKey() + ": "
|
||||
+ android.text.TextUtils.join(",", entry.getValue()) + "\n");
|
||||
|
||||
if (entry.getKey().compareTo("Content-Length") == 0)
|
||||
totalLength = Integer.decode(entry.getValue().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DisconnectionRunnable implements Runnable
|
||||
{
|
||||
public DisconnectionRunnable(HttpURLConnection theConnection,
|
||||
InputStream theInputStream,
|
||||
ReentrantLock theCreateStreamLock,
|
||||
Object theCreateFutureLock,
|
||||
Future<BufferedInputStream> theStreamFuture)
|
||||
{
|
||||
connectionToDisconnect = theConnection;
|
||||
inputStream = theInputStream;
|
||||
createStreamLock = theCreateStreamLock;
|
||||
createFutureLock = theCreateFutureLock;
|
||||
streamFuture = theStreamFuture;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!createStreamLock.tryLock())
|
||||
{
|
||||
synchronized (createFutureLock)
|
||||
{
|
||||
if (streamFuture != null)
|
||||
streamFuture.cancel(true);
|
||||
}
|
||||
|
||||
createStreamLock.lock();
|
||||
}
|
||||
|
||||
if (connectionToDisconnect != null)
|
||||
connectionToDisconnect.disconnect();
|
||||
|
||||
if (inputStream != null)
|
||||
inputStream.close();
|
||||
} catch (IOException e)
|
||||
{
|
||||
} finally
|
||||
{
|
||||
createStreamLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private HttpURLConnection connectionToDisconnect;
|
||||
private InputStream inputStream;
|
||||
private ReentrantLock createStreamLock;
|
||||
private Object createFutureLock;
|
||||
Future<BufferedInputStream> streamFuture;
|
||||
}
|
||||
|
||||
public final void release()
|
||||
{
|
||||
DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable(connection,
|
||||
inputStream,
|
||||
createStreamLock,
|
||||
createFutureLock,
|
||||
streamFuture);
|
||||
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
hasBeenCancelled.set(true);
|
||||
|
||||
connection = null;
|
||||
}
|
||||
|
||||
Thread disconnectionThread = new Thread(disconnectionRunnable);
|
||||
disconnectionThread.start();
|
||||
}
|
||||
|
||||
public final int read(byte[] buffer, int numBytes)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
try
|
||||
{
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (inputStream != null)
|
||||
num = inputStream.read(buffer, 0, numBytes);
|
||||
}
|
||||
} catch (IOException e)
|
||||
{
|
||||
}
|
||||
|
||||
if (num > 0)
|
||||
position += num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
public final long getPosition()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public final long getTotalLength()
|
||||
{
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
public final boolean isExhausted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean setPosition(long newPos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPost;
|
||||
private byte[] postData;
|
||||
private String headers;
|
||||
private int timeOutMs;
|
||||
String httpRequestCmd;
|
||||
private HttpURLConnection connection;
|
||||
private int[] statusCode;
|
||||
private StringBuffer responseHeaders;
|
||||
private int totalLength;
|
||||
private int numRedirectsToFollow;
|
||||
private InputStream inputStream;
|
||||
private long position;
|
||||
private final ReentrantLock createStreamLock = new ReentrantLock();
|
||||
private final Object createFutureLock = new Object();
|
||||
private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
|
||||
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
|
||||
Future<BufferedInputStream> streamFuture;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import com.roli.juce.Java;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class JuceApp extends Application
|
||||
{
|
||||
@Override
|
||||
public void onCreate ()
|
||||
{
|
||||
super.onCreate ();
|
||||
Java.initialiseJUCE (this);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class Java
|
||||
{
|
||||
static
|
||||
{
|
||||
System.loadLibrary ("juce_jni");
|
||||
}
|
||||
|
||||
public native static void initialiseJUCE (Context appContext);
|
||||
}
|
@ -23,47 +23,97 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Landroid/content/Context;Landroid/media/MediaScannerConnection$MediaScannerConnectionClient;)V") \
|
||||
METHOD (connect, "connect", "()V") \
|
||||
METHOD (disconnect, "disconnect", "()V") \
|
||||
METHOD (scanFile, "scanFile", "(Ljava/lang/String;Ljava/lang/String;)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection");
|
||||
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
|
||||
METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \
|
||||
METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;")
|
||||
|
||||
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver");
|
||||
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (moveToFirst, "moveToFirst", "()Z") \
|
||||
METHOD (getColumnIndex, "getColumnIndex", "(Ljava/lang/String;)I") \
|
||||
METHOD (getString, "getString", "(I)Ljava/lang/String;") \
|
||||
METHOD (close, "close", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor");
|
||||
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getExternalStorageDirectory, "getExternalStorageDirectory", "()Ljava/io/File;") \
|
||||
STATICMETHOD (getExternalStoragePublicDirectory, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;") \
|
||||
STATICMETHOD (getDataDirectory, "getDataDirectory", "()Ljava/io/File;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment");
|
||||
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (close, "close", "()V") \
|
||||
METHOD (flush, "flush", "()V") \
|
||||
METHOD (write, "write", "([BII)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream");
|
||||
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
FIELD (publicSourceDir, "publicSourceDir", "Ljava/lang/String;") \
|
||||
FIELD (dataDir, "dataDir", "Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidApplicationInfo, "android/content/pm/ApplicationInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
static File juceFile (LocalRef<jobject> obj)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
|
||||
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
|
||||
JavaFile.getAbsolutePath))));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static File getWellKnownFolder (const char* folderId)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto fieldId = env->GetStaticFieldID (AndroidEnvironment, folderId, "Ljava/lang/String;");
|
||||
|
||||
if (fieldId == 0)
|
||||
{
|
||||
// unknown field in environment
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
LocalRef<jobject> fieldValue (env->GetStaticObjectField (AndroidEnvironment, fieldId));
|
||||
|
||||
if (fieldValue == nullptr)
|
||||
return {};
|
||||
|
||||
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
|
||||
AndroidEnvironment.getExternalStoragePublicDirectory,
|
||||
fieldValue.get()));
|
||||
|
||||
return (downloadFolder ? juceFile (downloadFolder) : File());
|
||||
}
|
||||
|
||||
static LocalRef<jobject> urlToUri (const URL& url)
|
||||
{
|
||||
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
|
||||
javaString (url.toString (true)).get()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AndroidContentUriResolver
|
||||
{
|
||||
@ -74,7 +124,7 @@ public:
|
||||
jassert (url.getScheme() == "content");
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver)
|
||||
return LocalRef<jobject> ((env->CallObjectMethod (contentResolver.get(),
|
||||
@ -116,7 +166,7 @@ public:
|
||||
else if (type.equalsIgnoreCase ("downloads"))
|
||||
{
|
||||
auto subDownloadPath = url.getSubPath().fromFirstOccurrenceOf ("tree/downloads", false, false);
|
||||
return File (getWellKnownFolder ("Download").getFullPathName() + "/" + subDownloadPath);
|
||||
return File (getWellKnownFolder ("DIRECTORY_DOWNLOADS").getFullPathName() + "/" + subDownloadPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -142,7 +192,7 @@ public:
|
||||
{
|
||||
auto uri = urlToUri (url);
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver == 0)
|
||||
return {};
|
||||
@ -166,7 +216,7 @@ private:
|
||||
{
|
||||
auto uri = urlToUri (url);
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver)
|
||||
{
|
||||
@ -188,6 +238,13 @@ private:
|
||||
uri.get(), projection.get(), jSelection.get(),
|
||||
args.get(), nullptr));
|
||||
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
|
||||
@ -210,17 +267,6 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static File getWellKnownFolder (const String& folderId)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
|
||||
AndroidEnvironment.getExternalStoragePublicDirectory,
|
||||
javaString (folderId).get()));
|
||||
|
||||
return (downloadFolder ? juceFile (downloadFolder) : File());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static File getStorageDevicePath (const String& storageId)
|
||||
{
|
||||
@ -247,15 +293,15 @@ private:
|
||||
{
|
||||
Array<File> results;
|
||||
|
||||
if (getSDKVersion() >= 19)
|
||||
if (getAndroidSDKVersion() >= 19)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
static jmethodID m = (env->GetMethodID (JuceAppActivity, "getExternalFilesDirs",
|
||||
static jmethodID m = (env->GetMethodID (AndroidContext, "getExternalFilesDirs",
|
||||
"(Ljava/lang/String;)[Ljava/io/File;"));
|
||||
if (m == 0)
|
||||
return {};
|
||||
|
||||
auto paths = convertFileArray (LocalRef<jobject> (android.activity.callObjectMethod (m, nullptr)));
|
||||
auto paths = convertFileArray (LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), m, nullptr)));
|
||||
|
||||
for (auto path : paths)
|
||||
results.add (getMountPointForFile (path));
|
||||
@ -341,33 +387,6 @@ private:
|
||||
return files;
|
||||
}
|
||||
|
||||
static File juceFile (LocalRef<jobject> obj)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
|
||||
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
|
||||
JavaFile.getAbsolutePath))));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int getSDKVersion()
|
||||
{
|
||||
static int sdkVersion
|
||||
= getEnv()->CallStaticIntMethod (JuceAppActivity,
|
||||
JuceAppActivity.getAndroidSDKVersion);
|
||||
|
||||
return sdkVersion;
|
||||
}
|
||||
|
||||
static LocalRef<jobject> urlToUri (const URL& url)
|
||||
{
|
||||
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
|
||||
javaString (url.toString (true)).get()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String getStringUsingDataColumn (const String& columnNameToUse, JNIEnv* env,
|
||||
const LocalRef<jobject>& uri,
|
||||
@ -380,6 +399,13 @@ private:
|
||||
uri.get(), projection.get(), nullptr,
|
||||
nullptr, nullptr));
|
||||
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (cursor == 0)
|
||||
return {};
|
||||
|
||||
@ -458,7 +484,7 @@ OutputStream* juce_CreateContentURIOutputStream (const URL& url)
|
||||
{
|
||||
auto stream = AndroidContentUriResolver::getStreamForContentUri (url, false);
|
||||
|
||||
return (stream.get() != 0 ? new AndroidContentUriOutputStream (static_cast<LocalRef<jobject>&&> (stream)) : nullptr);
|
||||
return (stream.get() != 0 ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -511,9 +537,24 @@ String File::getVersion() const
|
||||
return {};
|
||||
}
|
||||
|
||||
static File getSpecialFile (jmethodID type)
|
||||
static File getDocumentsDirectory()
|
||||
{
|
||||
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type))));
|
||||
auto* env = getEnv();
|
||||
|
||||
if (getAndroidSDKVersion() >= 19)
|
||||
return getWellKnownFolder ("DIRECTORY_DOCUMENTS");
|
||||
|
||||
return juceFile (LocalRef<jobject> (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getDataDirectory)));
|
||||
}
|
||||
|
||||
static File getAppDataDir (bool dataDir)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> applicationInfo (env->CallObjectMethod (getAppContext().get(), AndroidContext.getApplicationInfo));
|
||||
LocalRef<jobject> jString (env->GetObjectField (applicationInfo.get(), dataDir ? AndroidApplicationInfo.dataDir : AndroidApplicationInfo.publicSourceDir));
|
||||
|
||||
return {juceString ((jstring) jString.get())};
|
||||
}
|
||||
|
||||
File File::getSpecialLocation (const SpecialLocationType type)
|
||||
@ -524,20 +565,41 @@ File File::getSpecialLocation (const SpecialLocationType type)
|
||||
case userApplicationDataDirectory:
|
||||
case userDesktopDirectory:
|
||||
case commonApplicationDataDirectory:
|
||||
return File (android.appDataDir);
|
||||
{
|
||||
static File appDataDir = getAppDataDir (true);
|
||||
return appDataDir;
|
||||
}
|
||||
|
||||
case userDocumentsDirectory:
|
||||
case commonDocumentsDirectory: return getSpecialFile (JuceAppActivity.getDocumentsFolder);
|
||||
case userPicturesDirectory: return getSpecialFile (JuceAppActivity.getPicturesFolder);
|
||||
case userMusicDirectory: return getSpecialFile (JuceAppActivity.getMusicFolder);
|
||||
case userMoviesDirectory: return getSpecialFile (JuceAppActivity.getMoviesFolder);
|
||||
case commonDocumentsDirectory:
|
||||
{
|
||||
static auto docsDir = getDocumentsDirectory();
|
||||
return docsDir;
|
||||
}
|
||||
|
||||
case userPicturesDirectory:
|
||||
{
|
||||
static auto picturesDir = getWellKnownFolder ("DIRECTORY_PICTURES");
|
||||
return picturesDir;
|
||||
}
|
||||
|
||||
case userMusicDirectory:
|
||||
{
|
||||
static auto musicDir = getWellKnownFolder ("DIRECTORY_MUSIC");
|
||||
return musicDir;
|
||||
}
|
||||
case userMoviesDirectory:
|
||||
{
|
||||
static auto moviesDir = getWellKnownFolder ("DIRECTORY_MOVIES");
|
||||
return moviesDir;
|
||||
}
|
||||
|
||||
case globalApplicationsDirectory:
|
||||
return File ("/system/app");
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
File tmp = File (android.appDataDir).getChildFile (".temp");
|
||||
File tmp = getSpecialLocation (commonApplicationDataDirectory).getChildFile (".temp");
|
||||
tmp.createDirectory();
|
||||
return File (tmp.getFullPathName());
|
||||
}
|
||||
@ -546,7 +608,7 @@ File File::getSpecialLocation (const SpecialLocationType type)
|
||||
case currentExecutableFile:
|
||||
case currentApplicationFile:
|
||||
case hostApplicationPath:
|
||||
return juce_getExecutableFile();
|
||||
return getAppDataDir (false);
|
||||
|
||||
default:
|
||||
jassertfalse; // unknown type?
|
||||
@ -567,8 +629,13 @@ bool File::moveToTrash() const
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&)
|
||||
{
|
||||
const LocalRef<jstring> t (javaString (fileName));
|
||||
android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get());
|
||||
URL targetURL (fileName);
|
||||
auto* env = getEnv();
|
||||
|
||||
const LocalRef<jstring> action (javaString ("android.intent.action.VIEW"));
|
||||
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithUri, action.get(), urlToUri (targetURL).get()));
|
||||
|
||||
env->CallVoidMethod (getCurrentActivity(), AndroidContext.startActivity, intent.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -581,10 +648,10 @@ class SingleMediaScanner : public MediaScannerConnectionClient
|
||||
{
|
||||
public:
|
||||
SingleMediaScanner (const String& filename)
|
||||
: msc (getEnv()->NewObject (MediaScannerConnection,
|
||||
MediaScannerConnection.constructor,
|
||||
android.activity.get(),
|
||||
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get())),
|
||||
: msc (LocalRef<jobject> (getEnv()->NewObject (MediaScannerConnection,
|
||||
MediaScannerConnection.constructor,
|
||||
getAppContext().get(),
|
||||
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get()))),
|
||||
file (filename)
|
||||
{
|
||||
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect);
|
||||
|
687
modules/juce_core/native/juce_android_JNIHelpers.cpp
Normal file
687
modules/juce_core/native/juce_android_JNIHelpers.cpp
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
static const uint8 invocationHandleByteCode[] = {
|
||||
31,139,8,8,170,94,229,91,0,3,105,110,118,111,99,97,116,105,111,110,95,104,97,110,100,108,101,114,46,100,101,120,0,109,
|
||||
148,75,107,19,81,20,199,207,189,119,38,169,181,109,98,218,138,72,23,81,20,68,193,169,218,130,144,42,130,162,53,140,15,
|
||||
104,200,66,187,232,52,185,181,147,78,103,98,58,141,33,184,168,197,47,224,66,168,187,186,241,83,168,184,16,247,162,31,193,
|
||||
69,151,174,220,40,234,255,62,154,4,237,192,239,62,206,57,115,30,115,239,153,186,236,12,79,95,154,165,159,187,91,95,227,
|
||||
217,197,247,87,42,31,231,191,156,157,58,241,253,199,220,201,79,75,175,118,58,14,81,147,136,58,213,153,2,217,231,131,32,
|
||||
154,32,35,63,164,246,0,102,244,13,48,48,129,33,139,121,138,153,125,5,131,131,119,82,204,203,156,168,1,214,65,10,186,224,
|
||||
53,120,7,62,131,61,144,131,237,57,112,30,92,4,183,192,93,176,4,154,160,11,182,193,11,97,252,171,216,46,200,144,137,59,
|
||||
100,243,26,6,35,0,46,9,166,116,131,155,89,113,159,27,125,214,214,116,216,174,23,185,241,89,208,181,8,173,99,240,48,106,
|
||||
247,99,182,198,156,149,231,245,204,232,136,246,203,173,189,65,189,61,7,209,31,60,167,48,239,26,119,90,183,35,84,190,66,
|
||||
175,201,230,218,204,43,201,81,172,30,76,131,25,66,180,190,133,169,81,107,139,164,243,112,123,25,154,253,194,53,232,17,
|
||||
231,2,74,190,140,106,212,190,57,205,201,97,99,168,207,209,223,71,61,147,202,182,57,104,59,74,11,45,212,255,56,251,60,251,
|
||||
50,251,166,157,81,81,71,80,83,129,206,252,238,133,239,101,162,242,72,237,217,186,246,251,171,106,51,248,242,162,183,218,
|
||||
183,207,204,133,113,152,94,37,86,38,215,47,251,190,79,142,175,198,211,126,45,89,247,90,73,20,122,141,205,154,244,202,24,
|
||||
110,199,237,164,22,164,97,18,207,7,113,61,146,173,18,29,247,235,65,212,14,215,188,32,142,147,84,235,188,202,106,43,121,
|
||||
178,81,162,130,223,8,218,129,23,5,241,35,239,222,114,67,214,210,18,77,14,200,180,93,176,28,201,18,162,245,197,45,185,18,
|
||||
193,214,59,48,218,255,102,119,100,186,154,212,75,196,170,196,171,101,26,127,120,64,84,183,22,201,160,69,249,122,184,209,
|
||||
12,210,218,234,205,48,14,162,176,43,105,108,95,162,130,173,73,26,90,217,215,100,66,35,25,141,145,66,91,94,79,226,84,118,
|
||||
82,114,219,65,180,41,137,115,54,62,197,142,57,196,132,186,86,207,182,156,95,156,111,115,98,10,182,43,4,123,43,24,219,19,
|
||||
185,127,206,70,247,159,237,77,62,208,159,98,160,71,157,129,62,117,169,223,171,25,234,247,171,200,155,181,62,231,162,121,
|
||||
231,169,178,41,26,185,186,207,44,111,228,234,142,243,162,137,171,250,219,41,246,239,56,217,181,190,251,214,167,250,127,
|
||||
252,5,76,239,47,69,120,4,0,0};
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
|
||||
|
||||
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(J)V") \
|
||||
METHOD (clear, "clear", "()V") \
|
||||
CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \
|
||||
CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/roli/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (findClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") \
|
||||
STATICMETHOD (getSystemClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
struct SystemJavaClassComparator
|
||||
{
|
||||
static int compareElements (JNIClassBase* first, JNIClassBase* second)
|
||||
{
|
||||
auto isSysClassA = isSystemClass (first);
|
||||
auto isSysClassB = isSystemClass (second);
|
||||
|
||||
if ((! isSysClassA) && (! isSysClassB))
|
||||
{
|
||||
return DefaultElementComparator<bool>::compareElements (first != nullptr ? first->byteCode != nullptr : false,
|
||||
second != nullptr ? second->byteCode != nullptr : false);
|
||||
}
|
||||
|
||||
return DefaultElementComparator<bool>::compareElements (isSystemClass (first),
|
||||
isSystemClass (second));
|
||||
}
|
||||
|
||||
static bool isSystemClass (JNIClassBase* cls)
|
||||
{
|
||||
if (cls == nullptr)
|
||||
return false;
|
||||
|
||||
String path (cls->getClassPath());
|
||||
|
||||
return path.startsWith ("java/")
|
||||
|| path.startsWith ("android/")
|
||||
|| path.startsWith ("dalvik/");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n)
|
||||
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (0)
|
||||
{
|
||||
SystemJavaClassComparator comparator;
|
||||
|
||||
getClasses().addSorted (comparator, this);
|
||||
}
|
||||
|
||||
JNIClassBase::~JNIClassBase()
|
||||
{
|
||||
getClasses().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
Array<JNIClassBase*>& JNIClassBase::getClasses()
|
||||
{
|
||||
static Array<JNIClassBase*> classes;
|
||||
return classes;
|
||||
}
|
||||
|
||||
// Get code cache directory without yet having a context object
|
||||
static File getCodeCacheDirectory()
|
||||
{
|
||||
int pid = getpid();
|
||||
File cmdline("/proc/" + String(pid) + "/cmdline");
|
||||
|
||||
auto bundleId = cmdline.loadFileAsString().trimStart().trimEnd();
|
||||
|
||||
if (bundleId.isEmpty())
|
||||
return {};
|
||||
|
||||
return File("/data/data/" + bundleId + "/code_cache");
|
||||
}
|
||||
|
||||
void JNIClassBase::initialise (JNIEnv* env)
|
||||
{
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
if (sdkVersion >= minSDK)
|
||||
{
|
||||
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
|
||||
static Array<GlobalRef> byteCodeLoaders;
|
||||
|
||||
if (! SystemJavaClassComparator::isSystemClass(this))
|
||||
{
|
||||
LocalRef<jobject> defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader));
|
||||
tryLoadingClassWithClassLoader (env, defaultClassLoader.get());
|
||||
|
||||
if (classRef == 0)
|
||||
{
|
||||
for (auto& byteCodeLoader : byteCodeLoaders)
|
||||
{
|
||||
tryLoadingClassWithClassLoader (env, byteCodeLoader.get());
|
||||
|
||||
if (classRef != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// fallback by trying to load the class from bytecode
|
||||
if (byteCode != nullptr)
|
||||
{
|
||||
LocalRef<jobject> byteCodeClassLoader;
|
||||
|
||||
MemoryOutputStream uncompressedByteCode;
|
||||
|
||||
{
|
||||
MemoryInputStream rawGZipData (byteCode, byteCodeSize, false);
|
||||
GZIPDecompressorInputStream gzipStream (&rawGZipData, false, GZIPDecompressorInputStream::gzipFormat);
|
||||
uncompressedByteCode.writeFromInputStream (gzipStream, -1);
|
||||
}
|
||||
|
||||
if (sdkVersion >= 26)
|
||||
{
|
||||
LocalRef<jbyteArray> byteArray (env->NewByteArray ((jsize) uncompressedByteCode.getDataSize()));
|
||||
jboolean isCopy;
|
||||
auto* dst = env->GetByteArrayElements (byteArray.get(), &isCopy);
|
||||
memcpy (dst, uncompressedByteCode.getData(), uncompressedByteCode.getDataSize());
|
||||
env->ReleaseByteArrayElements (byteArray.get(), dst, 0);
|
||||
|
||||
LocalRef<jobject> byteBuffer (env->CallStaticObjectMethod (JavaByteBuffer, JavaByteBuffer.wrap, byteArray.get()));
|
||||
|
||||
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidInMemoryDexClassLoader,
|
||||
AndroidInMemoryDexClassLoader.constructor,
|
||||
byteBuffer.get(), defaultClassLoader.get()));
|
||||
}
|
||||
else if (uncompressedByteCode.getDataSize() >= 32)
|
||||
{
|
||||
auto codeCacheDir = getCodeCacheDirectory();
|
||||
|
||||
// The dex file has an embedded 20-byte long SHA-1 signature at offset 12
|
||||
auto fileName = String::toHexString ((char*)uncompressedByteCode.getData() + 12, 20, 0) + ".dex";
|
||||
auto dexFile = codeCacheDir.getChildFile (fileName);
|
||||
auto optimizedDirectory = codeCacheDir.getChildFile ("optimized_cache");
|
||||
optimizedDirectory.createDirectory();
|
||||
|
||||
if (dexFile.replaceWithData (uncompressedByteCode.getData(), uncompressedByteCode.getDataSize()))
|
||||
{
|
||||
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidDexClassLoader,
|
||||
AndroidDexClassLoader.constructor,
|
||||
javaString (dexFile.getFullPathName()).get(),
|
||||
javaString (optimizedDirectory.getFullPathName()).get(),
|
||||
nullptr,
|
||||
defaultClassLoader.get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't write to cache folder
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if (byteCodeClassLoader != nullptr)
|
||||
{
|
||||
tryLoadingClassWithClassLoader (env, byteCodeClassLoader.get());
|
||||
byteCodeLoaders.add (GlobalRef(byteCodeClassLoader));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (classRef == 0)
|
||||
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
|
||||
|
||||
jassert (classRef != 0);
|
||||
initialiseFields (env);
|
||||
}
|
||||
}
|
||||
|
||||
void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader)
|
||||
{
|
||||
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
|
||||
|
||||
// Android SDK <= 19 has a bug where the class loader might throw an exception but still return
|
||||
// a non-nullptr. So don't assign the result of this call to a jobject just yet...
|
||||
auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.findClass, classNameAndPackage.get());
|
||||
|
||||
if (jthrowable exception = env->ExceptionOccurred ())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
classObj = 0;
|
||||
}
|
||||
|
||||
// later versions of Android don't throw at all, so re-check the object
|
||||
if (classObj != nullptr)
|
||||
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj));
|
||||
}
|
||||
|
||||
void JNIClassBase::release (JNIEnv* env)
|
||||
{
|
||||
if (classRef != 0)
|
||||
env->DeleteGlobalRef (classRef);
|
||||
}
|
||||
|
||||
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->initialise (env);
|
||||
}
|
||||
|
||||
void JNIClassBase::releaseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->release (env);
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetMethodID (classRef, methodName, params);
|
||||
jassert (m != 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
|
||||
jassert (m != 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
|
||||
jassert (f != 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
|
||||
jassert (f != 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
void JNIClassBase::resolveCallbacks (JNIEnv* env, const Array<JNINativeMethod>& nativeCallbacks)
|
||||
{
|
||||
if (nativeCallbacks.size() > 0)
|
||||
env->RegisterNatives (classRef, nativeCallbacks.begin(), (jint) nativeCallbacks.size());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames,
|
||||
LocalRef<jobject> subclass)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
implementer->javaSubClass = GlobalRef (subclass);
|
||||
|
||||
// you need to override at least one interface
|
||||
jassert (interfaceNames.size() > 0);
|
||||
|
||||
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
|
||||
LocalRef<jobject> classLoader;
|
||||
|
||||
for (auto i = 0; i < interfaceNames.size(); ++i)
|
||||
{
|
||||
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
|
||||
|
||||
if (aClass != nullptr)
|
||||
{
|
||||
if (i == 0)
|
||||
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
|
||||
|
||||
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// interface class not found
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
auto invocationHandler = LocalRef<jobject> (env->NewObject (JuceInvocationHandler, JuceInvocationHandler.constructor,
|
||||
reinterpret_cast<jlong> (implementer)));
|
||||
|
||||
// CreateJavaInterface() is expected to be called just once for a given implementer
|
||||
jassert (implementer->invocationHandler == nullptr);
|
||||
|
||||
implementer->invocationHandler = GlobalRef (invocationHandler);
|
||||
|
||||
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
|
||||
classLoader.get(), classArray.get(),
|
||||
invocationHandler.get()));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames)
|
||||
{
|
||||
return CreateJavaInterface (implementer, interfaceNames,
|
||||
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
|
||||
JavaObject.constructor)));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const String& interfaceName)
|
||||
{
|
||||
return CreateJavaInterface (implementer, StringArray (interfaceName));
|
||||
}
|
||||
|
||||
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void AndroidInterfaceImplementer::clear()
|
||||
{
|
||||
if (invocationHandler != nullptr)
|
||||
getEnv()->CallVoidMethod (invocationHandler,
|
||||
JuceInvocationHandler.clear);
|
||||
}
|
||||
|
||||
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
|
||||
}
|
||||
|
||||
jobject juce_invokeImplementer (JNIEnv*, jobject /*object*/, jlong host, jobject proxy,
|
||||
jobject method, jobjectArray args)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
|
||||
return myself->invoke (proxy, method, args);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void juce_dispatchDelete (JNIEnv*, jobject /*object*/, jlong host)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
|
||||
delete myself;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobjectArray args)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||
|
||||
auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr;
|
||||
auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr;
|
||||
|
||||
if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; }
|
||||
else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; }
|
||||
else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; }
|
||||
else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; }
|
||||
|
||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getAndroidSDKVersion()
|
||||
{
|
||||
// this is used so often that we need to cache this
|
||||
static int sdkVersion = []
|
||||
{
|
||||
// don't use any jni helpers as they might not have been initialised yet
|
||||
// when this method is used
|
||||
auto* env = getEnv();
|
||||
|
||||
auto buildVersion = env->FindClass ("android/os/Build$VERSION");
|
||||
jassert (buildVersion != 0);
|
||||
|
||||
auto sdkVersionField = env->GetStaticFieldID (buildVersion, "SDK_INT", "I");
|
||||
jassert (sdkVersionField != 0);
|
||||
|
||||
return env->GetStaticIntField (buildVersion, sdkVersionField);
|
||||
}();
|
||||
|
||||
return sdkVersion;
|
||||
}
|
||||
|
||||
bool isPermissionDeclaredInManifest (const String& requestedPermission)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> pkgManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
|
||||
LocalRef<jobject> pkgName (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName));
|
||||
LocalRef<jobject> pkgInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.getPackageInfo,
|
||||
pkgName.get(), 0x00001000 /* PERMISSIONS */));
|
||||
|
||||
LocalRef<jobjectArray> permissions ((jobjectArray) env->GetObjectField (pkgInfo.get(), AndroidPackageInfo.requestedPermissions));
|
||||
int n = env->GetArrayLength (permissions);
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
LocalRef<jstring> jstr ((jstring) env->GetObjectArrayElement (permissions, i));
|
||||
String permissionId (juceString (jstr));
|
||||
|
||||
if (permissionId == requestedPermission)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/java/com/roli/juce/FragmentOverlay.java with min sdk version 16
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaFragmentOverlay[] =
|
||||
{31,139,8,8,197,105,229,91,0,3,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149,93,136,
|
||||
28,69,16,199,171,231,243,62,39,235,93,60,206,243,244,214,136,73,4,201,156,104,32,186,107,184,36,34,236,58,248,145,11,251,
|
||||
112,241,101,216,29,215,137,123,51,155,153,217,35,1,65,61,2,201,131,8,10,126,97,2,17,84,16,204,91,30,228,240,73,130,95,40,
|
||||
104,124,9,137,47,9,248,230,23,8,65,52,130,255,234,238,201,133,35,226,178,191,169,234,234,238,170,234,154,158,238,78,116,
|
||||
100,100,254,129,157,116,170,242,197,47,171,219,126,253,250,218,79,107,167,30,234,189,251,229,123,127,239,158,219,187,86,
|
||||
124,127,193,33,234,19,209,145,214,131,19,164,127,187,96,187,151,148,125,4,108,22,74,214,33,241,167,179,120,84,32,63,213,
|
||||
237,186,65,244,130,69,244,12,228,121,147,232,34,248,29,252,1,174,130,191,192,63,224,118,140,217,9,154,224,121,240,34,88,
|
||||
5,39,192,171,224,117,240,14,56,13,62,0,31,129,51,224,51,240,21,56,15,46,128,203,224,55,240,39,112,108,162,105,48,15,30,6,
|
||||
77,240,44,56,1,94,3,167,193,25,176,6,62,7,223,2,164,73,72,135,176,76,114,193,16,24,214,107,29,5,147,188,102,0,247,114,
|
||||
125,199,48,216,214,109,210,99,92,173,143,105,253,21,140,25,215,250,219,208,61,173,191,15,125,147,214,63,54,85,221,88,255,
|
||||
4,250,45,90,63,7,125,66,235,223,200,88,130,166,136,243,52,100,12,3,217,221,169,219,91,116,30,51,196,227,84,63,203,91,181,
|
||||
156,38,53,255,54,41,77,154,149,210,161,59,164,84,126,108,172,120,78,74,139,170,82,186,116,151,158,191,69,74,155,238,38,
|
||||
181,102,65,164,163,40,157,127,67,142,146,38,44,108,251,193,86,53,236,87,120,44,103,190,84,229,10,148,253,151,116,127,217,
|
||||
147,84,28,140,243,80,71,75,190,131,43,182,90,255,34,54,220,20,130,221,15,55,187,208,187,152,161,38,135,197,49,241,134,
|
||||
251,225,138,51,12,95,30,241,76,94,255,207,152,195,107,74,171,130,14,192,163,11,235,24,205,136,41,74,170,38,170,60,74,75,
|
||||
11,240,184,112,163,71,87,182,251,11,136,251,180,39,223,163,138,127,245,127,226,187,50,254,184,140,207,181,229,189,195,19,
|
||||
249,253,165,21,206,231,166,113,230,55,145,37,60,93,55,71,239,57,210,82,233,195,178,46,66,83,238,55,238,45,117,30,97,72,
|
||||
221,210,99,156,122,156,196,197,110,218,252,88,22,118,151,163,164,120,114,37,202,122,225,209,29,135,194,149,144,68,131,68,
|
||||
147,140,102,64,34,160,217,32,76,58,89,26,119,252,176,223,247,31,141,195,94,218,45,103,213,104,250,122,111,59,77,10,152,
|
||||
252,134,20,53,154,188,222,147,230,254,222,65,210,233,69,53,154,11,218,233,178,159,165,189,216,63,52,104,71,254,134,240,
|
||||
53,154,8,56,3,191,23,38,93,127,177,200,226,164,91,35,209,34,171,213,104,4,252,12,2,50,90,77,178,91,77,54,176,128,197,108,
|
||||
53,217,12,14,54,104,242,224,77,92,216,237,94,154,71,228,182,251,253,3,207,197,57,89,157,176,8,201,237,196,249,114,156,
|
||||
231,52,214,141,138,61,89,119,192,169,228,228,162,21,164,73,23,230,44,76,138,253,81,62,232,193,92,73,147,61,237,34,94,137,
|
||||
139,163,202,68,83,27,45,79,132,104,69,52,148,38,251,178,40,44,34,242,74,77,247,204,164,201,254,232,240,32,202,139,167,
|
||||
162,140,67,199,105,146,107,111,213,255,238,211,179,221,52,89,44,194,172,160,113,173,104,251,104,127,125,2,141,102,202,
|
||||
201,190,180,19,209,72,38,231,75,221,206,11,78,201,42,184,0,46,185,158,113,95,141,118,64,62,94,167,237,230,214,109,211,
|
||||
174,119,252,77,26,19,219,93,175,126,238,248,18,85,205,173,247,204,194,246,22,190,57,215,123,4,22,18,54,62,111,235,229,
|
||||
151,172,31,45,123,21,39,201,13,216,226,154,101,138,147,182,33,190,3,39,29,72,103,124,195,55,207,178,188,19,120,63,150,
|
||||
247,130,73,235,119,67,185,103,249,126,224,179,163,188,35,28,90,191,39,68,85,181,249,174,16,21,117,46,240,249,106,84,149,
|
||||
127,190,63,76,61,134,207,21,62,160,68,121,230,84,148,206,247,211,191,48,134,254,198,216,6,0,0};
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (construct, "<init>", "()V") \
|
||||
METHOD (close, "close", "()V") \
|
||||
CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \
|
||||
CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \
|
||||
CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \
|
||||
CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/roli/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (show, "show", "(Landroid/app/FragmentManager;Ljava/lang/String;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidDialogFragment, "android/app/DialogFragment")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
FragmentOverlay::FragmentOverlay()
|
||||
: native (LocalRef<jobject> (getEnv()->NewObject (JuceFragmentOverlay, JuceFragmentOverlay.construct)))
|
||||
{}
|
||||
|
||||
FragmentOverlay::~FragmentOverlay()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (native.get(), JuceFragmentOverlay.close);
|
||||
}
|
||||
|
||||
void FragmentOverlay::open()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> bundle (env->NewObject (AndroidBundle, AndroidBundle.constructor));
|
||||
env->CallVoidMethod (bundle.get(), AndroidBundle.putLong, javaString ("cppThis").get(), (jlong) this);
|
||||
env->CallVoidMethod (native.get(), AndroidFragment.setArguments, bundle.get());
|
||||
|
||||
LocalRef<jobject> fm (env->CallObjectMethod (getCurrentActivity().get(), AndroidActivity.getFragmentManager));
|
||||
env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get());
|
||||
}
|
||||
|
||||
void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host,
|
||||
jint requestCode, jint resultCode, jobject data)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
|
||||
}
|
||||
|
||||
void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle)));
|
||||
}
|
||||
|
||||
void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onStart();
|
||||
}
|
||||
|
||||
void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode,
|
||||
jobjectArray jPermissions, jintArray jGrantResults)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
{
|
||||
Array<int> grantResults;
|
||||
int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
auto* data = env->GetIntArrayElements (jGrantResults, 0);
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
grantResults.add (data[i]);
|
||||
|
||||
env->ReleaseIntArrayElements (jGrantResults, data, 0);
|
||||
}
|
||||
|
||||
myself->onRequestPermissionsResult (requestCode,
|
||||
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
|
||||
grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
jobject FragmentOverlay::getNativeHandle()
|
||||
{
|
||||
return native.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ActivityLauncher : public FragmentOverlay
|
||||
{
|
||||
public:
|
||||
ActivityLauncher (const LocalRef<jobject>& intentToUse,
|
||||
int requestCodeToUse,
|
||||
std::function<void (int, int, LocalRef<jobject>)> && callbackToUse)
|
||||
: intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse))
|
||||
{}
|
||||
|
||||
void onStart() override
|
||||
{
|
||||
getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult,
|
||||
intent.get(), requestCode);
|
||||
}
|
||||
|
||||
void onActivityResult (int activityRequestCode, int resultCode, LocalRef<jobject> data) override
|
||||
{
|
||||
if (callback)
|
||||
callback (activityRequestCode, resultCode, std::move (data));
|
||||
|
||||
getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
GlobalRef intent;
|
||||
int requestCode;
|
||||
std::function<void (int, int, LocalRef<jobject>)> callback;
|
||||
};
|
||||
|
||||
void startAndroidActivityForResult (const LocalRef<jobject>& intent, int requestCode,
|
||||
std::function<void (int, int, LocalRef<jobject>)> && callback)
|
||||
{
|
||||
auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback));
|
||||
activityLauncher->open();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool androidHasSystemFeature (const String& property)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> packageManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
|
||||
|
||||
if (packageManager != nullptr)
|
||||
return env->CallBooleanMethod (packageManager.get(),
|
||||
AndroidPackageManager.hasSystemFeature,
|
||||
javaString (property).get()) != 0;
|
||||
}
|
||||
|
||||
// unable to get app's context
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
String audioManagerGetProperty (const String& property)
|
||||
{
|
||||
if (getAndroidSDKVersion() >= 17)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> audioManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService,
|
||||
javaString ("audio").get()));
|
||||
|
||||
if (audioManager != nullptr)
|
||||
{
|
||||
LocalRef<jstring> jProperty (javaString (property));
|
||||
|
||||
auto methodID = env->GetMethodID (AndroidAudioManager, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
if (methodID != nullptr)
|
||||
return juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (audioManager.get(),
|
||||
methodID,
|
||||
javaString (property).get())));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -23,15 +23,177 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/java/com/roli/juce/JuceHTTPStream.java with min sdk version 16
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaJuceHttpStream[] =
|
||||
{31,139,8,8,96,105,229,91,0,3,74,117,99,101,72,84,84,80,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124,84,213,157,
|
||||
199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,32,40,146,128,200,75,32,65,17,6,20,146,181,29,
|
||||
50,23,50,48,220,9,51,119,32,88,84,64,171,212,218,74,173,86,250,82,139,88,105,87,91,171,237,214,86,187,109,197,118,219,
|
||||
218,90,109,245,179,174,187,125,91,119,237,167,187,186,218,118,221,246,227,102,127,231,49,153,137,77,33,124,239,255,127,
|
||||
254,231,127,206,61,143,255,121,204,36,41,123,34,208,119,193,18,58,123,73,224,169,145,163,171,22,29,152,124,110,71,248,
|
||||
214,145,193,207,157,62,245,139,197,215,175,242,156,233,36,26,39,162,137,237,139,35,164,254,253,114,14,209,11,36,237,139,
|
||||
192,183,116,162,56,228,9,15,81,61,228,147,38,209,149,144,71,188,68,200,34,79,128,104,101,19,81,10,242,205,90,162,63,130,
|
||||
183,193,95,192,255,1,173,142,200,0,33,80,5,162,160,27,92,12,54,131,173,224,42,48,2,118,1,27,92,11,174,3,199,192,205,224,
|
||||
86,112,59,56,5,206,128,179,224,41,240,10,104,140,18,45,3,215,128,27,193,253,224,187,224,183,128,161,193,49,112,17,184,28,
|
||||
236,6,71,193,221,224,33,240,44,120,5,188,13,194,13,68,237,96,0,92,5,246,129,227,224,62,240,8,248,1,248,37,120,19,248,27,
|
||||
137,174,6,105,112,45,248,4,120,2,188,6,106,102,161,14,112,13,56,2,62,9,254,5,76,130,30,140,211,213,96,20,236,6,123,129,3,
|
||||
242,224,125,224,40,184,13,156,4,31,5,119,131,79,130,123,193,253,224,113,240,36,248,30,120,17,252,28,188,10,94,7,255,3,
|
||||
222,1,70,51,81,16,68,65,12,244,128,53,96,59,24,3,215,130,219,192,167,193,3,224,9,112,14,252,24,188,10,254,2,204,22,244,
|
||||
17,88,160,17,116,128,249,96,17,184,8,108,2,127,7,28,112,61,184,25,220,13,206,128,175,128,239,128,31,129,127,6,175,129,55,
|
||||
193,36,240,181,18,213,129,78,208,15,46,3,27,193,86,240,94,176,23,28,2,71,193,9,112,23,184,23,60,0,30,6,143,129,175,129,
|
||||
103,193,207,192,239,192,155,224,207,128,98,232,59,168,4,115,64,63,216,14,118,131,125,96,28,28,4,71,192,205,224,36,184,23,
|
||||
60,0,190,0,190,9,94,4,63,3,191,6,255,14,254,12,188,109,68,179,65,31,88,9,182,128,221,96,63,112,193,17,112,2,124,28,60,8,
|
||||
190,8,190,14,190,15,126,14,222,2,122,59,226,2,52,130,118,208,11,150,131,203,64,2,100,64,1,92,7,110,5,31,1,167,64,16,221,
|
||||
178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229,75,88,214,52,23,116,129,110,48,15,
|
||||
204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126,48,0,86,128,75,192,74,112,
|
||||
41,88,5,214,128,117,224,50,176,30,108,4,91,192,54,176,29,92,69,178,31,197,127,33,37,151,98,111,8,43,125,101,153,190,30,
|
||||
122,165,210,55,215,202,254,51,149,230,251,143,1,70,96,143,148,213,203,245,98,249,6,165,187,202,167,88,87,173,242,91,170,
|
||||
236,181,202,94,173,244,35,202,30,45,179,71,149,189,70,233,55,65,175,83,250,109,202,94,175,236,181,74,95,170,244,134,50,
|
||||
157,207,219,157,181,178,28,215,63,165,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,67,170,126,
|
||||
62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,114,133,210,191,12,125,72,233,79,150,233,125,101,58,111,255,
|
||||
38,165,63,13,125,179,210,249,248,95,174,244,31,150,249,252,91,173,60,27,122,213,248,23,235,121,165,86,198,196,2,213,158,
|
||||
173,74,255,61,236,9,165,187,170,47,11,213,123,117,204,244,247,136,203,5,244,28,164,7,35,103,11,89,75,123,132,236,161,180,
|
||||
144,97,250,176,144,221,244,77,226,241,209,68,41,33,165,159,161,252,12,140,88,65,200,249,116,147,144,81,186,69,201,15,8,
|
||||
41,235,49,240,190,59,72,198,217,105,33,25,125,78,200,56,125,94,200,122,250,130,144,125,244,13,33,139,239,197,154,87,242,
|
||||
135,64,163,42,186,75,180,191,141,76,33,189,244,30,33,131,66,122,208,30,83,200,86,250,146,40,215,41,210,188,221,59,84,59,
|
||||
71,133,172,163,221,66,154,52,166,236,7,132,244,211,97,33,13,186,65,201,99,42,255,164,144,58,61,36,100,43,61,172,198,225,
|
||||
31,136,175,153,118,241,158,16,118,11,46,195,200,79,10,233,17,254,149,72,31,18,114,30,253,68,196,93,5,237,83,241,119,187,
|
||||
136,189,86,81,46,138,113,217,165,228,135,72,198,246,199,132,92,64,95,23,178,146,190,45,164,37,100,61,86,212,136,144,17,
|
||||
218,47,164,44,87,143,145,146,82,150,175,87,254,13,234,61,13,88,101,35,66,134,233,25,226,123,225,108,97,111,68,254,19,196,
|
||||
215,83,43,57,66,250,232,90,33,27,233,125,42,125,68,200,0,93,79,114,221,29,21,50,74,199,133,140,209,99,66,118,211,151,149,
|
||||
252,138,178,127,85,200,14,122,92,200,57,244,53,226,107,85,142,87,51,118,85,41,235,233,140,144,178,93,173,24,247,247,11,
|
||||
25,164,123,136,239,205,33,154,32,190,63,7,233,58,37,111,36,190,158,103,145,75,124,45,55,208,157,196,215,113,51,125,139,
|
||||
248,94,221,66,25,33,155,232,227,196,215,116,47,189,87,200,160,168,103,190,26,143,249,248,145,233,110,122,148,248,158,46,
|
||||
237,92,222,45,228,124,122,86,165,127,76,242,142,70,36,215,22,223,35,170,32,31,199,198,117,229,28,105,247,148,229,247,169,
|
||||
252,151,144,191,87,229,243,120,102,84,218,47,121,254,219,200,63,174,242,121,253,63,197,193,243,50,248,77,135,244,253,131,
|
||||
146,239,116,240,117,128,251,4,124,67,115,164,45,170,100,167,146,151,40,185,126,14,175,75,23,250,167,176,233,249,32,135,
|
||||
145,24,209,24,141,91,179,68,132,242,92,94,223,153,118,185,71,182,179,32,109,139,49,156,83,22,37,98,68,7,44,175,136,112,
|
||||
199,234,19,114,60,86,141,18,85,108,122,222,2,33,187,222,228,109,99,226,125,15,183,203,126,58,22,183,4,69,31,77,252,240,
|
||||
188,71,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,71,237,242,60,76,
|
||||
244,121,80,23,223,105,3,56,63,107,41,177,80,167,102,150,232,51,209,82,31,113,25,137,228,98,235,17,35,45,76,250,94,46,74,
|
||||
105,200,247,10,233,88,189,194,98,40,139,1,75,237,84,205,155,197,104,132,80,122,46,158,37,91,215,159,144,90,40,83,76,156,
|
||||
16,132,213,171,3,46,153,58,187,116,113,206,243,158,47,17,82,71,219,47,184,33,116,225,114,172,204,0,241,81,122,165,93,238,
|
||||
221,188,207,62,140,114,15,180,77,240,28,62,230,71,170,86,216,121,138,231,205,71,106,163,72,85,96,95,226,55,129,32,59,96,
|
||||
93,198,199,145,13,31,11,208,200,173,149,52,252,193,8,13,223,22,164,29,31,170,163,225,15,215,208,240,237,213,20,249,239,
|
||||
29,199,162,120,179,69,59,142,90,120,75,21,13,31,13,209,182,27,43,41,113,83,132,18,239,15,210,22,92,253,19,183,132,200,
|
||||
123,204,123,199,65,175,95,213,232,69,207,53,117,35,168,154,45,227,47,129,185,173,18,177,233,21,49,82,15,123,158,199,135,
|
||||
175,25,49,176,12,235,216,177,6,240,142,160,175,213,215,68,222,155,90,61,77,20,241,57,177,11,113,18,56,177,197,244,7,60,
|
||||
47,194,179,209,119,53,180,86,156,3,65,45,162,183,117,45,185,49,78,235,124,154,222,232,215,97,111,166,83,20,48,151,155,
|
||||
237,194,230,224,114,236,163,128,111,201,205,141,34,29,241,59,184,34,158,50,131,140,167,94,242,155,204,137,197,80,34,232,
|
||||
229,158,94,120,46,247,97,22,251,46,166,26,239,75,186,206,186,158,79,156,13,161,214,165,104,195,210,96,132,34,245,78,108,
|
||||
9,215,67,60,70,23,34,166,130,134,131,15,47,47,34,21,69,108,85,121,66,34,42,60,232,85,136,26,253,126,244,172,6,245,207,
|
||||
245,201,59,206,85,124,70,217,172,61,213,56,23,2,208,15,205,150,247,139,254,112,45,69,194,237,168,193,251,25,246,168,247,
|
||||
156,247,5,246,91,239,159,124,94,139,52,95,21,249,252,17,58,24,48,197,216,93,28,254,175,201,234,112,95,161,235,119,33,22,
|
||||
161,174,183,229,216,86,201,122,27,43,68,140,135,233,19,179,249,12,227,126,228,59,201,34,53,17,61,241,128,159,154,141,196,
|
||||
233,10,180,110,16,30,1,109,41,214,71,139,193,22,85,106,145,200,120,44,136,115,36,168,13,159,14,83,226,116,13,69,204,196,
|
||||
131,94,120,46,226,35,226,29,244,106,166,180,174,209,52,115,241,241,159,82,68,155,94,130,251,198,81,107,80,203,89,171,149,
|
||||
92,195,165,153,179,86,96,133,242,183,61,77,57,235,18,232,65,214,202,218,160,95,202,35,5,246,55,38,115,214,42,165,255,110,
|
||||
50,241,96,53,70,174,1,209,218,133,121,43,122,181,106,81,204,65,55,118,221,160,214,232,243,8,253,56,226,164,209,175,81,
|
||||
177,116,171,86,67,7,98,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185,209,27,128,87,19,229,208,
|
||||
206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,88,13,178,14,171,81,140,225,142,211,81,234,186,61,36,198,177,
|
||||
155,70,38,67,106,44,22,137,220,37,199,219,228,104,122,18,159,149,86,140,132,57,104,106,30,233,213,11,175,153,243,101,125,
|
||||
139,39,103,158,139,110,90,54,25,210,90,140,185,90,72,75,156,249,107,15,143,57,247,93,115,218,77,113,148,232,166,158,73,
|
||||
212,188,170,155,188,147,124,255,240,99,87,170,22,123,168,87,220,131,121,236,116,136,61,171,90,124,214,184,1,233,199,196,
|
||||
222,19,18,103,44,63,243,159,83,126,255,170,236,191,80,233,255,84,233,55,196,174,205,232,29,81,111,53,249,152,180,27,55,
|
||||
132,30,101,191,103,55,116,126,149,189,206,232,247,140,94,103,215,119,190,95,19,70,47,198,129,159,41,175,206,150,159,119,
|
||||
34,90,98,171,143,154,245,196,149,50,90,25,5,216,82,134,104,213,189,109,56,37,174,12,80,59,246,244,241,62,70,9,51,23,91,
|
||||
139,207,56,216,113,174,172,69,126,2,207,156,181,65,236,65,173,44,76,93,175,133,88,139,62,151,97,119,106,235,250,141,120,
|
||||
254,82,158,165,13,240,225,235,206,135,247,206,85,251,172,70,157,90,117,247,156,158,26,232,94,62,23,24,12,126,103,168,198,
|
||||
135,155,72,232,32,171,64,127,2,214,197,150,33,44,149,214,75,161,16,243,193,182,133,89,116,97,243,255,78,114,61,193,48,31,
|
||||
250,154,42,166,87,179,94,198,111,151,139,172,26,170,102,11,148,142,177,101,113,165,195,159,93,32,244,11,172,151,85,233,
|
||||
106,106,212,55,32,26,59,112,195,14,176,151,194,44,80,178,35,150,251,230,208,194,112,160,242,162,215,159,82,254,124,222,
|
||||
47,16,253,117,172,229,92,250,139,254,78,223,92,250,85,101,176,162,184,187,158,162,246,32,180,190,126,186,173,130,251,6,
|
||||
17,27,139,238,184,103,234,189,17,93,104,30,148,180,58,49,70,1,143,211,55,155,22,122,132,21,177,226,195,213,178,57,34,189,
|
||||
203,231,165,82,140,70,75,164,155,38,38,49,218,17,140,118,128,91,186,105,188,172,141,189,240,230,169,109,122,37,201,247,
|
||||
68,164,52,130,66,110,49,235,100,218,27,66,204,84,83,241,36,225,182,97,212,192,235,95,57,41,107,230,250,170,73,18,247,243,
|
||||
185,104,233,205,106,255,187,91,204,163,135,62,13,233,133,198,239,125,124,247,127,74,165,117,186,11,91,228,41,118,195,172,
|
||||
239,176,187,24,169,91,12,209,137,14,185,135,110,141,85,137,207,177,69,251,135,59,100,172,108,137,69,113,103,183,166,206,
|
||||
179,59,213,125,41,130,29,221,15,111,15,126,62,209,33,63,27,226,76,220,133,157,86,75,36,195,180,148,249,137,203,136,238,
|
||||
172,172,163,3,55,6,168,69,91,142,246,108,221,85,69,199,60,79,172,218,9,89,73,24,51,13,99,198,186,222,98,36,239,86,30,181,
|
||||
134,26,85,92,206,154,58,255,27,88,168,137,199,44,67,116,106,244,88,135,252,238,160,29,37,18,7,112,255,200,97,47,206,227,
|
||||
157,46,110,47,133,90,58,104,242,154,18,57,209,158,3,21,20,169,118,98,67,136,171,8,27,134,119,139,214,110,54,34,50,230,
|
||||
163,46,199,234,33,126,67,146,109,137,136,27,17,19,223,79,48,241,51,7,111,144,55,212,103,202,250,206,191,75,228,255,52,42,
|
||||
73,55,32,199,242,8,228,77,1,121,103,44,126,47,192,207,175,147,1,57,166,167,32,239,83,249,229,119,95,158,111,170,122,252,
|
||||
74,242,239,15,206,42,223,14,85,95,157,146,81,245,222,162,140,171,250,226,170,78,63,201,207,58,113,225,209,39,62,191,68,
|
||||
85,217,5,101,109,43,239,131,69,53,194,110,168,54,151,202,71,133,125,158,242,227,159,65,25,201,123,167,108,67,84,148,105,
|
||||
130,165,7,189,233,86,246,102,85,46,78,197,241,96,24,222,46,194,133,141,45,35,109,89,140,216,0,153,3,105,39,237,174,32,
|
||||
109,69,63,121,86,244,119,111,39,107,117,214,113,236,81,55,157,117,98,118,46,151,205,81,24,22,215,118,220,222,33,219,217,
|
||||
227,142,81,237,154,116,126,116,202,105,75,193,113,146,187,50,54,177,13,164,109,24,34,125,195,208,6,242,224,129,253,112,
|
||||
35,85,111,44,140,218,235,19,137,205,91,221,156,157,220,191,96,111,242,96,146,216,16,105,112,210,185,143,54,132,34,67,120,
|
||||
120,135,134,118,14,13,161,130,128,82,184,174,13,237,164,250,161,164,147,202,101,211,169,184,107,79,184,241,4,30,219,220,
|
||||
116,38,223,79,177,161,209,236,254,120,46,155,73,199,247,226,53,241,233,239,234,88,216,79,139,207,239,49,99,71,250,169,
|
||||
229,188,165,250,169,125,40,149,204,28,76,239,139,39,29,39,235,38,121,225,248,90,103,52,147,205,167,157,61,171,51,201,60,
|
||||
218,54,251,124,62,155,108,119,44,155,226,47,250,107,167,13,104,79,78,85,210,54,67,254,38,123,255,46,229,96,195,165,121,6,
|
||||
151,173,233,61,78,210,45,228,208,149,198,25,178,19,99,185,236,33,81,148,207,70,60,157,141,175,42,236,222,109,231,236,212,
|
||||
6,103,188,224,22,123,89,59,149,189,225,138,181,19,163,246,56,47,60,205,92,238,93,55,101,190,162,224,150,217,235,165,61,
|
||||
147,116,246,196,87,143,37,115,91,237,3,5,219,25,181,167,42,18,57,101,245,87,151,153,55,32,238,246,216,57,62,211,211,141,
|
||||
185,92,97,220,181,83,101,197,106,202,61,224,32,103,50,82,102,189,98,215,94,76,244,116,207,210,156,151,123,162,237,152,
|
||||
164,233,109,151,54,57,80,253,212,48,67,78,58,147,226,89,229,21,97,164,237,100,106,122,87,197,232,203,119,54,73,179,99,
|
||||
187,241,245,174,59,190,109,203,80,105,237,245,83,184,148,139,156,169,214,168,116,185,167,234,81,1,171,66,246,221,205,162,
|
||||
33,86,153,117,40,157,119,167,154,33,44,155,146,227,29,107,29,55,119,184,159,54,205,100,30,248,235,241,120,87,125,51,120,
|
||||
172,192,15,85,78,175,110,186,97,171,237,242,176,47,25,176,254,70,11,185,28,182,151,248,234,100,38,35,118,146,214,243,231,
|
||||
247,83,207,223,114,64,96,193,135,143,74,89,104,116,207,236,189,118,194,30,45,188,203,181,243,124,174,89,4,111,238,96,154,
|
||||
199,110,236,252,126,249,169,185,125,183,199,186,2,95,151,197,165,55,115,110,63,13,156,47,123,224,188,203,22,19,208,49,
|
||||
115,105,25,140,235,146,163,104,32,102,125,254,204,94,136,157,253,233,209,248,165,66,172,202,102,51,118,18,227,50,111,102,
|
||||
231,76,118,116,95,62,190,197,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,118,210,182,99,143,223,142,61,126,59,
|
||||
246,120,19,15,190,215,35,177,147,2,219,203,246,249,237,59,137,237,36,109,231,70,0,57,188,10,108,160,234,225,25,86,165,54,
|
||||
226,80,32,57,58,106,231,243,29,125,125,125,84,33,245,117,153,228,158,60,121,147,169,84,14,41,50,147,227,227,182,147,34,
|
||||
239,174,100,222,222,150,203,144,185,75,140,22,121,70,17,70,100,142,138,88,33,131,239,200,54,249,177,225,143,39,115,118,
|
||||
34,75,94,117,34,80,160,116,52,80,93,73,79,100,75,167,6,89,163,24,83,215,46,45,198,162,69,78,21,31,144,162,165,116,128,20,
|
||||
45,50,37,124,42,213,136,22,135,142,204,148,61,154,77,217,84,147,178,119,39,11,25,119,218,228,241,220,140,237,218,20,72,
|
||||
149,154,82,155,154,241,84,174,158,102,150,213,144,63,149,85,77,38,102,147,193,167,239,48,249,132,192,2,133,166,226,152,
|
||||
140,221,105,59,147,130,200,20,242,99,164,239,65,102,13,30,197,133,134,23,168,46,133,97,93,203,111,11,42,93,137,244,122,
|
||||
188,202,206,173,227,53,228,133,67,89,156,146,137,244,160,125,88,56,150,159,24,84,1,195,102,156,146,98,24,120,238,22,59,
|
||||
63,158,117,242,24,100,140,7,175,38,129,131,44,163,238,34,188,26,236,134,228,131,220,158,204,20,108,178,198,146,249,85,
|
||||
136,72,213,70,27,33,0,203,229,184,53,80,197,152,104,209,80,218,177,17,41,50,145,167,160,82,18,217,109,136,132,240,24,118,
|
||||
226,45,252,144,202,187,171,247,167,168,122,122,90,58,5,184,81,181,151,165,201,155,118,82,246,196,21,187,169,34,93,214,67,
|
||||
95,218,81,77,170,72,231,215,78,140,37,11,121,151,183,38,157,23,227,64,102,58,143,126,186,60,151,75,89,179,47,173,246,111,
|
||||
242,236,205,166,17,7,25,217,79,15,95,106,228,113,146,251,49,165,142,125,104,117,114,116,204,78,201,233,220,140,85,74,33,
|
||||
110,44,133,97,5,146,165,96,66,2,175,16,146,47,5,143,195,71,67,119,10,104,36,30,171,14,187,24,143,26,104,235,178,153,76,
|
||||
246,144,157,218,98,167,210,57,212,36,173,83,169,68,86,58,80,195,76,86,53,126,89,44,187,178,134,232,89,116,85,31,199,219,
|
||||
61,120,44,20,207,69,228,27,47,206,48,215,220,53,73,55,73,161,162,38,43,242,136,72,245,230,16,233,88,193,84,153,83,97,176,
|
||||
94,77,91,205,187,12,178,148,9,43,150,11,233,185,2,222,157,71,188,86,225,161,218,147,72,239,183,121,115,42,96,90,147,149,
|
||||
81,71,141,121,30,152,121,151,135,139,236,72,169,247,220,113,42,22,195,121,30,139,201,84,177,22,75,164,69,96,200,251,28,
|
||||
69,74,150,205,57,140,67,206,61,76,70,126,60,147,118,33,220,100,14,115,14,225,22,242,20,144,82,132,116,101,73,151,125,8,
|
||||
230,69,0,169,163,194,204,23,118,237,71,13,126,200,188,216,2,41,228,142,149,111,57,213,60,249,238,93,167,100,44,219,102,
|
||||
194,48,150,47,194,74,164,183,150,191,204,227,142,165,17,39,252,217,209,71,126,23,93,197,48,109,194,242,157,82,85,156,186,
|
||||
89,185,31,83,133,91,182,28,235,248,57,144,223,157,205,237,183,83,151,151,69,160,23,59,139,104,129,89,112,68,36,87,28,76,
|
||||
102,58,138,43,193,56,40,150,137,113,40,135,232,39,109,162,143,142,104,223,103,228,13,211,81,254,156,92,54,64,71,120,234,
|
||||
30,205,56,199,190,203,190,195,158,198,231,109,227,159,216,188,117,222,240,64,47,255,71,159,215,224,112,98,249,96,186,117,
|
||||
100,112,120,240,154,97,109,188,121,109,219,57,170,243,143,220,201,30,98,31,96,207,176,111,178,71,216,25,246,65,230,13,
|
||||
107,127,209,250,39,38,14,107,199,175,59,35,10,247,14,12,14,177,72,37,89,236,5,228,210,106,115,228,163,236,239,217,9,246,
|
||||
3,225,253,138,238,249,36,107,30,28,184,100,159,174,221,194,22,49,166,235,31,98,108,249,9,221,124,144,177,207,78,232,236,
|
||||
80,253,9,221,251,19,86,159,214,14,246,27,204,240,106,225,126,195,28,94,48,180,96,80,55,62,203,172,129,75,12,163,197,208,
|
||||
12,189,85,167,124,211,0,205,243,79,85,255,143,236,139,236,126,118,43,94,210,59,135,85,69,209,230,94,173,162,191,141,126,
|
||||
141,94,111,92,126,139,214,219,163,237,104,214,42,14,61,62,113,61,107,180,60,204,109,239,127,154,53,84,178,250,208,138,95,
|
||||
177,250,106,131,241,42,195,56,52,13,226,218,220,19,95,190,230,71,125,90,203,243,6,105,187,250,207,117,124,76,179,250,233,
|
||||
32,187,131,121,155,81,89,109,143,182,191,249,229,182,143,172,68,15,40,95,211,218,98,144,208,58,91,233,78,62,202,189,186,
|
||||
245,37,22,111,211,67,167,25,27,234,213,190,77,61,134,165,93,218,108,90,31,56,172,87,125,131,49,86,95,117,78,15,159,69,
|
||||
159,245,202,207,49,182,224,156,238,223,87,255,65,189,226,83,172,126,167,238,187,166,101,167,30,188,135,181,12,233,218,
|
||||
179,172,62,206,44,235,240,3,90,104,135,97,173,68,215,125,70,176,85,15,228,155,52,103,163,17,48,53,211,103,6,77,235,125,
|
||||
172,165,246,162,86,51,64,111,241,169,163,183,197,243,29,241,252,185,166,189,231,51,24,149,54,157,238,195,80,15,78,180,
|
||||
247,238,213,38,154,7,232,25,158,253,71,157,198,141,229,123,7,90,119,234,230,129,89,3,13,134,73,199,117,246,105,62,117,
|
||||
154,151,61,207,26,131,154,79,219,233,161,83,172,69,11,32,221,100,68,123,163,203,163,70,180,35,234,213,42,184,1,3,88,84,
|
||||
170,52,19,74,189,33,138,248,78,177,104,133,44,227,137,174,86,37,68,210,31,101,209,197,209,206,232,122,84,37,51,124,154,
|
||||
95,214,224,41,86,213,73,26,211,232,100,140,225,255,209,163,158,71,106,53,246,92,45,251,118,236,190,58,50,24,211,144,41,
|
||||
126,200,115,236,168,231,201,58,131,253,71,29,85,153,94,175,198,34,226,71,228,49,109,218,143,72,155,240,127,164,158,157,
|
||||
136,189,212,160,69,222,104,96,13,127,152,197,34,63,108,98,145,163,237,190,170,147,179,141,170,183,59,89,213,201,185,172,
|
||||
234,44,120,18,60,210,197,170,206,116,139,239,148,168,236,187,6,46,139,127,187,197,191,31,40,254,253,86,241,123,11,254,55,
|
||||
92,252,59,146,226,223,113,241,239,20,138,127,203,101,82,233,239,185,116,75,254,254,141,127,15,195,98,242,119,191,143,240,
|
||||
239,79,98,210,135,255,254,144,89,165,223,41,106,49,249,94,254,247,95,186,242,231,191,195,243,196,72,252,238,137,255,126,
|
||||
144,84,89,241,123,71,75,182,149,255,173,217,255,3,11,139,181,139,164,38,0,0};
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;") \
|
||||
|
||||
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer");
|
||||
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/roli/juce/JuceHTTPStream;") \
|
||||
METHOD (connect, "connect", "()Z") \
|
||||
METHOD (release, "release", "()V") \
|
||||
METHOD (read, "read", "([BI)I") \
|
||||
@ -40,15 +202,15 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer");
|
||||
METHOD (isExhausted, "isExhausted", "()Z") \
|
||||
METHOD (setPosition, "setPosition", "(J)Z") \
|
||||
|
||||
DECLARE_JNI_CLASS (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream");
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/roli/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (close, "close", "()V") \
|
||||
METHOD (read, "read", "([BII)I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream");
|
||||
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
@ -192,8 +354,8 @@ public:
|
||||
const ScopedLock lock (createStreamLock);
|
||||
|
||||
if (! hasBeenCancelled)
|
||||
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (JuceAppActivity,
|
||||
JuceAppActivity.createHTTPStream,
|
||||
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (HTTPStream,
|
||||
HTTPStream.createHTTPStream,
|
||||
javaString (address).get(),
|
||||
(jboolean) isPost,
|
||||
postDataArray,
|
||||
@ -342,15 +504,22 @@ URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extra
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result)
|
||||
{
|
||||
in_addr_t addr = addr_in->sin_addr.s_addr;
|
||||
#if __ANDROID_API__ < 24 // Android support for getifadds was added in Android 7.0 (API 24) so the posix implementation does not apply
|
||||
|
||||
if (addr != INADDR_NONE)
|
||||
result.addIfNotAlreadyThere (IPAddress (ntohl (addr)));
|
||||
static IPAddress makeAddress (const sockaddr_in *addr_in)
|
||||
{
|
||||
if (addr_in->sin_addr.s_addr == INADDR_NONE)
|
||||
return {};
|
||||
|
||||
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
static void findIPAddresses (int sock, Array<IPAddress>& result)
|
||||
struct InterfaceInfo
|
||||
{
|
||||
IPAddress interfaceAddress, broadcastAddress;
|
||||
};
|
||||
|
||||
static Array<InterfaceInfo> findIPAddresses (int dummySocket)
|
||||
{
|
||||
ifconf cfg;
|
||||
HeapBlock<char> buffer;
|
||||
@ -364,40 +533,66 @@ static void findIPAddresses (int sock, Array<IPAddress>& result)
|
||||
cfg.ifc_len = bufferSize;
|
||||
cfg.ifc_buf = buffer;
|
||||
|
||||
if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
|
||||
return;
|
||||
if (ioctl (dummySocket, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
|
||||
return {};
|
||||
|
||||
} while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6)));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in)))
|
||||
{
|
||||
if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses
|
||||
addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result);
|
||||
Array<InterfaceInfo> result;
|
||||
|
||||
cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
}
|
||||
#else
|
||||
for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i)
|
||||
{
|
||||
const ifreq& item = cfg.ifc_req[i];
|
||||
auto& item = cfg.ifc_req[i];
|
||||
|
||||
if (item.ifr_addr.sa_family == AF_INET)
|
||||
addAddress ((const sockaddr_in*) &item.ifr_addr, result);
|
||||
{
|
||||
InterfaceInfo info;
|
||||
info.interfaceAddress = makeAddress ((const sockaddr_in*) &item.ifr_addr);
|
||||
|
||||
if (! info.interfaceAddress.isNull())
|
||||
{
|
||||
if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0)
|
||||
info.broadcastAddress = makeAddress ((const sockaddr_in*) &item.ifr_broadaddr);
|
||||
|
||||
result.add (info);
|
||||
}
|
||||
}
|
||||
else if (item.ifr_addr.sa_family == AF_INET6)
|
||||
{
|
||||
// TODO: IPv6
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Array<InterfaceInfo> findIPAddresses()
|
||||
{
|
||||
auto dummySocket = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
|
||||
|
||||
if (dummySocket < 0)
|
||||
return {};
|
||||
|
||||
auto result = findIPAddresses (dummySocket);
|
||||
::close (dummySocket);
|
||||
return result;
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool /*includeIPv6*/)
|
||||
{
|
||||
const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
|
||||
|
||||
if (sock >= 0)
|
||||
{
|
||||
findIPAddresses (sock, result);
|
||||
::close (sock);
|
||||
}
|
||||
for (auto& a : findIPAddresses())
|
||||
result.add (a.interfaceAddress);
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& address)
|
||||
{
|
||||
for (auto& a : findIPAddresses())
|
||||
if (a.interfaceAddress == address)
|
||||
return a.broadcastAddress;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -23,33 +23,185 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
static void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr)
|
||||
//==============================================================================
|
||||
static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission)
|
||||
{
|
||||
if (callbackPtr == nullptr)
|
||||
switch (permission)
|
||||
{
|
||||
// got a nullptr passed in from java! this should never happen...
|
||||
jassertfalse;
|
||||
return;
|
||||
case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO";
|
||||
case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_COARSE_LOCATION";
|
||||
case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::camera: return "android.permission.CAMERA";
|
||||
}
|
||||
|
||||
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr);
|
||||
|
||||
if (RuntimePermissions::Callback callbackObj = *uptr)
|
||||
callbackObj (permissionWasGranted);
|
||||
// invalid permission
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME,
|
||||
androidRuntimePermissionsCallback,
|
||||
void, (JNIEnv* env, jobject, jboolean permissionsGranted, jlong callbackPtr))
|
||||
static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission)
|
||||
{
|
||||
setEnv (env);
|
||||
handleAndroidCallback (permissionsGranted != 0,
|
||||
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr));
|
||||
if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio;
|
||||
else if (permission == "android.permission.ACCESS_COARSE_LOCATION") return RuntimePermissions::bluetoothMidi;
|
||||
else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage;
|
||||
else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage;
|
||||
else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera;
|
||||
|
||||
return static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct PermissionsRequest
|
||||
{
|
||||
PermissionsRequest() {}
|
||||
|
||||
// using "= default" on the following method triggers an internal compiler error
|
||||
// in Android NDK 17
|
||||
PermissionsRequest (const PermissionsRequest& o)
|
||||
: callback (o.callback), permission (o.permission)
|
||||
{}
|
||||
|
||||
PermissionsRequest (PermissionsRequest&& o)
|
||||
: callback (std::move (o.callback)), permission (o.permission)
|
||||
{
|
||||
o.permission = static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
PermissionsRequest (RuntimePermissions::Callback && callbackToUse,
|
||||
RuntimePermissions::PermissionID permissionToRequest)
|
||||
: callback (std::move (callbackToUse)), permission (permissionToRequest)
|
||||
{}
|
||||
|
||||
PermissionsRequest& operator= (const PermissionsRequest & o)
|
||||
{
|
||||
callback = o.callback;
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PermissionsRequest& operator= (PermissionsRequest && o)
|
||||
{
|
||||
callback = std::move (o.callback);
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RuntimePermissions::Callback callback;
|
||||
RuntimePermissions::PermissionID permission;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct PermissionsOverlay : FragmentOverlay
|
||||
{
|
||||
PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {}
|
||||
~PermissionsOverlay() {}
|
||||
|
||||
struct PermissionResult
|
||||
{
|
||||
PermissionsRequest request;
|
||||
bool granted;
|
||||
};
|
||||
|
||||
void onStart() override { onRequestPermissionsResult (0, {}, {}); }
|
||||
|
||||
void onRequestPermissionsResult (int /*requestCode*/,
|
||||
const StringArray& permissions,
|
||||
const Array<int>& grantResults) override
|
||||
{
|
||||
std::vector<PermissionResult> results;
|
||||
|
||||
{
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
for (auto it = requests.begin(); it != requests.end();)
|
||||
{
|
||||
auto& request = *it;
|
||||
|
||||
if (RuntimePermissions::isGranted (request.permission))
|
||||
{
|
||||
results.push_back ({std::move (request), true});
|
||||
it = requests.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
auto n = permissions.size();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto permission = androidPermissionToJucePermission (permissions[i]);
|
||||
auto granted = (grantResults.getReference (i) == 0);
|
||||
|
||||
for (auto it = requests.begin(); it != requests.end();)
|
||||
{
|
||||
auto& request = *it;
|
||||
|
||||
if (request.permission == permission)
|
||||
{
|
||||
results.push_back ({std::move (request), granted});
|
||||
it = requests.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& result : results)
|
||||
if (result.request.callback)
|
||||
result.request.callback (result.granted);
|
||||
|
||||
{
|
||||
auto* env = getEnv();
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
if (requests.size() > 0)
|
||||
{
|
||||
auto &request = requests.front();
|
||||
|
||||
StringArray permissionsArray{
|
||||
jucePermissionToAndroidPermission (request.permission)};
|
||||
auto jPermissionsArray = juceStringArrayToJava (permissionsArray);
|
||||
|
||||
|
||||
auto requestPermissionsMethodID
|
||||
= env->GetMethodID(AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V");
|
||||
|
||||
// this code should only be reached for SDKs >= 23, so this method should be
|
||||
// be available
|
||||
jassert(requestPermissionsMethodID != 0);
|
||||
|
||||
env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
getSingleton() = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<PermissionsOverlay>& getSingleton()
|
||||
{
|
||||
static std::unique_ptr<PermissionsOverlay> instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
CriticalSection& overlayGuard;
|
||||
std::vector<PermissionsRequest> requests;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void RuntimePermissions::request (PermissionID permission, Callback callback)
|
||||
{
|
||||
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission))
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
|
||||
if (! isPermissionDeclaredInManifest (requestedPermission))
|
||||
{
|
||||
// Error! If you want to be able to request this runtime permission, you
|
||||
// also need to declare it in your app's manifest. You can do so via
|
||||
@ -60,29 +212,50 @@ void RuntimePermissions::request (PermissionID permission, Callback callback)
|
||||
return;
|
||||
}
|
||||
|
||||
if (JUCE_ANDROID_API_VERSION < 23)
|
||||
auto alreadyGranted = isGranted (permission);
|
||||
|
||||
if (alreadyGranted || getAndroidSDKVersion() < 23)
|
||||
{
|
||||
// There is no runtime permission system on API level below 23. As long as the
|
||||
// permission is in the manifest (seems to be the case), we can simply ask Android
|
||||
// if the app has the permission, and then directly call through to the callback.
|
||||
callback (isGranted (permission));
|
||||
callback (alreadyGranted);
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to move the callback object to the heap so Java can keep track of the pointer
|
||||
// and asynchronously pass it back to us (to be called and then deleted)
|
||||
Callback* callbackPtr = new Callback (std::move (callback));
|
||||
android.activity.callVoidMethod (JuceAppActivity.requestRuntimePermission, permission, (jlong) callbackPtr);
|
||||
PermissionsRequest request (std::move (callback), permission);
|
||||
|
||||
static CriticalSection overlayGuard;
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
std::unique_ptr<PermissionsOverlay>& overlay = PermissionsOverlay::getSingleton();
|
||||
|
||||
bool alreadyOpen = true;
|
||||
|
||||
if (overlay == nullptr)
|
||||
{
|
||||
overlay.reset (new PermissionsOverlay (overlayGuard));
|
||||
alreadyOpen = false;
|
||||
}
|
||||
|
||||
overlay->requests.push_back (std::move (request));
|
||||
|
||||
if (! alreadyOpen)
|
||||
overlay->open();
|
||||
}
|
||||
|
||||
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
|
||||
{
|
||||
return JUCE_ANDROID_API_VERSION >= 23;
|
||||
return getAndroidSDKVersion() >= 23;
|
||||
}
|
||||
|
||||
bool RuntimePermissions::isGranted (PermissionID permission)
|
||||
{
|
||||
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission);
|
||||
auto* env = getEnv();
|
||||
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission,
|
||||
javaString (requestedPermission).get());
|
||||
|
||||
|
||||
return result == 0 /* PERMISSION_GRANTED */;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -23,319 +23,21 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
|
||||
|
||||
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0)
|
||||
{
|
||||
getClasses().add (this);
|
||||
}
|
||||
|
||||
JNIClassBase::~JNIClassBase()
|
||||
{
|
||||
getClasses().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
Array<JNIClassBase*>& JNIClassBase::getClasses()
|
||||
{
|
||||
static Array<JNIClassBase*> classes;
|
||||
return classes;
|
||||
}
|
||||
|
||||
void JNIClassBase::initialise (JNIEnv* env)
|
||||
{
|
||||
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
|
||||
jassert (classRef != 0);
|
||||
|
||||
initialiseFields (env);
|
||||
}
|
||||
|
||||
void JNIClassBase::release (JNIEnv* env)
|
||||
{
|
||||
env->DeleteGlobalRef (classRef);
|
||||
}
|
||||
|
||||
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->initialise (env);
|
||||
}
|
||||
|
||||
void JNIClassBase::releaseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->release (env);
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetMethodID (classRef, methodName, params);
|
||||
jassert (m != 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
|
||||
jassert (m != 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
|
||||
jassert (f != 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
|
||||
jassert (f != 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames,
|
||||
LocalRef<jobject> subclass)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
implementer->javaSubClass = GlobalRef (subclass);
|
||||
|
||||
// you need to override at least one interface
|
||||
jassert (interfaceNames.size() > 0);
|
||||
|
||||
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
|
||||
LocalRef<jobject> classLoader;
|
||||
|
||||
for (auto i = 0; i < interfaceNames.size(); ++i)
|
||||
{
|
||||
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
|
||||
|
||||
if (aClass != nullptr)
|
||||
{
|
||||
if (i == 0)
|
||||
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
|
||||
|
||||
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// interface class not found
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
auto invocationHandler = LocalRef<jobject> (env->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.createInvocationHandler,
|
||||
reinterpret_cast<jlong> (implementer)));
|
||||
|
||||
// CreateJavaInterface() is expected to be called just once for a given implementer
|
||||
jassert (implementer->invocationHandler == nullptr);
|
||||
|
||||
implementer->invocationHandler = GlobalRef (invocationHandler);
|
||||
|
||||
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
|
||||
classLoader.get(), classArray.get(),
|
||||
invocationHandler.get()));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames)
|
||||
{
|
||||
return CreateJavaInterface (implementer, interfaceNames,
|
||||
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
|
||||
JavaObject.constructor)));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const String& interfaceName)
|
||||
{
|
||||
return CreateJavaInterface (implementer, StringArray (interfaceName));
|
||||
}
|
||||
|
||||
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
|
||||
|
||||
{
|
||||
if (invocationHandler != nullptr)
|
||||
getEnv()->CallVoidMethod (android.activity,
|
||||
JuceAppActivity.invocationHandlerContextDeleted,
|
||||
invocationHandler.get());
|
||||
}
|
||||
|
||||
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
|
||||
}
|
||||
|
||||
jobject juce_invokeImplementer (JNIEnv* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args)
|
||||
{
|
||||
setEnv (env);
|
||||
return reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr)->invoke (proxy, method, args);
|
||||
}
|
||||
|
||||
void juce_dispatchDelete (JNIEnv* env, jlong thisPtr)
|
||||
{
|
||||
setEnv (env);
|
||||
delete reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr);
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchInvoke,
|
||||
jobject, (JNIEnv* env, jobject /*object*/, jlong thisPtr, jobject proxy, jobject method, jobjectArray args))
|
||||
{
|
||||
return juce_invokeImplementer (env, thisPtr, proxy, method, args);
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchFinalize,
|
||||
void, (JNIEnv* env, jobject /*object*/, jlong thisPtr))
|
||||
{
|
||||
juce_dispatchDelete (env, thisPtr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JavaVM* androidJNIJavaVM = nullptr;
|
||||
|
||||
class JniEnvThreadHolder
|
||||
{
|
||||
public:
|
||||
static JniEnvThreadHolder& getInstance() noexcept
|
||||
{
|
||||
// You cann only use JNI functions AFTER JNI_OnLoad was called
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
try
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new JniEnvThreadHolder;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
jassertfalse;
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
static JNIEnv* getEnv() { return reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey)); }
|
||||
|
||||
static void setEnv (JNIEnv* env)
|
||||
{
|
||||
// env must not be a nullptr
|
||||
jassert (env != nullptr);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
JNIEnv* oldenv = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
|
||||
|
||||
// This thread is already attached to the JavaVM and you trying to attach
|
||||
// it to a different instance of the VM.
|
||||
jassert (oldenv == nullptr || oldenv == env);
|
||||
#endif
|
||||
|
||||
pthread_setspecific (getInstance().threadKey, env);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_key_t threadKey;
|
||||
|
||||
static void threadDetach (void* p)
|
||||
{
|
||||
if (JNIEnv* env = reinterpret_cast<JNIEnv*> (p))
|
||||
{
|
||||
ignoreUnused (env);
|
||||
|
||||
androidJNIJavaVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
JniEnvThreadHolder()
|
||||
{
|
||||
pthread_key_create (&threadKey, threadDetach);
|
||||
}
|
||||
|
||||
static JniEnvThreadHolder* instance;
|
||||
};
|
||||
|
||||
JniEnvThreadHolder* JniEnvThreadHolder::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JNIEnv* attachAndroidJNI() noexcept
|
||||
{
|
||||
auto* env = JniEnvThreadHolder::getEnv();
|
||||
|
||||
if (env == nullptr)
|
||||
{
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
setEnv (env);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
JNIEnv* getEnv() noexcept
|
||||
{
|
||||
auto* env = JniEnvThreadHolder::getEnv();
|
||||
|
||||
// You are trying to use a JUCE function on a thread that was not created by JUCE.
|
||||
// You need to first call setEnv on this thread before using JUCE
|
||||
jassert (env != nullptr);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
void setEnv (JNIEnv* env) noexcept { JniEnvThreadHolder::setEnv (env); }
|
||||
|
||||
extern "C" jint JNI_OnLoad (JavaVM* vm, void*)
|
||||
{
|
||||
// Huh? JNI_OnLoad was called two times!
|
||||
jassert (androidJNIJavaVM == nullptr);
|
||||
|
||||
androidJNIJavaVM = vm;
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160)
|
||||
{
|
||||
}
|
||||
|
||||
void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir)
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
screenWidth = screenHeight = 0;
|
||||
dpi = 160;
|
||||
JNIClassBase::initialiseAllClasses (env);
|
||||
|
||||
activity = GlobalRef (act);
|
||||
appFile = juceString (env, file);
|
||||
appDataDir = juceString (env, dataDir);
|
||||
}
|
||||
|
||||
void AndroidSystem::shutdown (JNIEnv* env)
|
||||
{
|
||||
activity.clear();
|
||||
|
||||
JNIClassBase::releaseAllClasses (env);
|
||||
}
|
||||
|
||||
AndroidSystem android;
|
||||
|
||||
//==============================================================================
|
||||
namespace AndroidStatsHelpers
|
||||
{
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (SystemClass, "java/lang/System");
|
||||
DECLARE_JNI_CLASS (SystemClass, "java/lang/System")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getDefault, "getDefault", "()Ljava/util/Locale;") \
|
||||
METHOD (getCountry, "getCountry", "()Ljava/lang/String;") \
|
||||
METHOD (getLanguage, "getLanguage", "()Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaLocale, "java/util/Locale")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static inline String getSystemProperty (const String& name)
|
||||
@ -347,19 +49,19 @@ namespace AndroidStatsHelpers
|
||||
|
||||
static inline String getLocaleValue (bool isRegion)
|
||||
{
|
||||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity,
|
||||
JuceAppActivity.getLocaleValue,
|
||||
isRegion)));
|
||||
}
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> locale (env->CallStaticObjectMethod (JavaLocale, JavaLocale.getDefault));
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD)
|
||||
DECLARE_JNI_CLASS (BuildClass, "android/os/Build");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
auto stringResult = isRegion ? env->CallObjectMethod (locale.get(), JavaLocale.getCountry)
|
||||
: env->CallObjectMethod (locale.get(), JavaLocale.getLanguage);
|
||||
|
||||
return juceString (LocalRef<jstring> ((jstring) stringResult));
|
||||
}
|
||||
|
||||
static inline String getAndroidOsBuildValue (const char* fieldName)
|
||||
{
|
||||
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField (
|
||||
BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;"))));
|
||||
AndroidBuild, getEnv()->GetStaticFieldID (AndroidBuild, fieldName, "Ljava/lang/String;"))));
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +106,7 @@ String SystemStats::getCpuModel()
|
||||
return readPosixConfigFileValue ("/proc/cpuinfo", "Hardware");
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegaherz()
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
int maxFreqKHz = 0;
|
||||
|
||||
|
@ -28,6 +28,322 @@ namespace juce
|
||||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
FIELD (activityInfo, "activityInfo", "Landroid/content/pm/ActivityInfo;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidResolveInfo, "android/content/pm/ResolveInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
JavaVM* androidJNIJavaVM = nullptr;
|
||||
jobject androidApkContext = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JNIEnv* getEnv() noexcept
|
||||
{
|
||||
if (androidJNIJavaVM != nullptr)
|
||||
{
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
|
||||
// before using any JUCE APIs. The Projucer will automatically generate java code
|
||||
// which will invoke Thread::initialiseJUCE for you.
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context)
|
||||
{
|
||||
Thread::initialiseJUCE (env, context);
|
||||
}
|
||||
|
||||
extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*)
|
||||
{
|
||||
// Huh? JNI_OnLoad was called two times!
|
||||
jassert (androidJNIJavaVM == nullptr);
|
||||
|
||||
androidJNIJavaVM = vm;
|
||||
|
||||
auto* env = getEnv();
|
||||
|
||||
// register the initialisation function
|
||||
auto juceJavaClass = env->FindClass("com/roli/juce/Java");
|
||||
|
||||
if (juceJavaClass != nullptr)
|
||||
{
|
||||
JNINativeMethod method {"initialiseJUCE", "(Landroid/content/Context;)V",
|
||||
reinterpret_cast<void*> (juce_JavainitialiseJUCE)};
|
||||
|
||||
auto status = env->RegisterNatives (juceJavaClass, &method, 1);
|
||||
jassert (status == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// com.roli.juce.Java class not found. Apparently this project is a library
|
||||
// or was not generated by the Projucer. That's ok, the user will have to
|
||||
// call Thread::initialiseJUCE manually
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
JNIClassBase::initialiseAllClasses (env);
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceActivityWatcher : public ActivityLifecycleCallbacks
|
||||
{
|
||||
public:
|
||||
JuceActivityWatcher()
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
|
||||
}
|
||||
|
||||
checkActivityIsMain (androidApkContext);
|
||||
}
|
||||
|
||||
~JuceActivityWatcher()
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr && myself != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
|
||||
clear();
|
||||
myself.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityStarted (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
checkActivityIsMain (activity);
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (currentActivity != nullptr)
|
||||
{
|
||||
// see Clarification June 2001 in JNI reference for why this is
|
||||
// necessary
|
||||
LocalRef<jobject> localStorage (env->NewLocalRef (currentActivity));
|
||||
|
||||
if (env->IsSameObject (localStorage.get(), activity) != 0)
|
||||
return;
|
||||
|
||||
env->DeleteWeakGlobalRef (currentActivity);
|
||||
currentActivity = nullptr;
|
||||
}
|
||||
|
||||
if (activity != nullptr)
|
||||
currentActivity = env->NewWeakGlobalRef (activity);
|
||||
}
|
||||
|
||||
void onActivityStopped (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (currentActivity != nullptr)
|
||||
{
|
||||
// important that the comparison happens in this order
|
||||
// to avoid race condition where the weak reference becomes null
|
||||
// just after the first check
|
||||
if (env->IsSameObject (currentActivity, activity) != 0
|
||||
|| env->IsSameObject (currentActivity, nullptr) != 0)
|
||||
{
|
||||
env->DeleteWeakGlobalRef (currentActivity);
|
||||
currentActivity = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalRef<jobject> getCurrent()
|
||||
{
|
||||
ScopedLock lock (currentActivityLock);
|
||||
return LocalRef<jobject> (getEnv()->NewLocalRef (currentActivity));
|
||||
}
|
||||
|
||||
LocalRef<jobject> getMain()
|
||||
{
|
||||
ScopedLock lock (currentActivityLock);
|
||||
return LocalRef<jobject> (getEnv()->NewLocalRef (mainActivity));
|
||||
}
|
||||
|
||||
static JuceActivityWatcher& getInstance()
|
||||
{
|
||||
static JuceActivityWatcher activityWatcher;
|
||||
return activityWatcher;
|
||||
}
|
||||
|
||||
private:
|
||||
void checkActivityIsMain (jobject context)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (mainActivity != nullptr)
|
||||
{
|
||||
if (env->IsSameObject (mainActivity, nullptr) != 0)
|
||||
{
|
||||
env->DeleteWeakGlobalRef (mainActivity);
|
||||
mainActivity = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainActivity == nullptr)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
auto mainActivityPath = getMainActivityClassPath();
|
||||
|
||||
if (mainActivityPath.isNotEmpty())
|
||||
{
|
||||
auto clasz = env->GetObjectClass (context);
|
||||
auto activityPath = juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (clasz, JavaClass.getName)));
|
||||
|
||||
// This may be problematic for apps which use several activities with the same type. We just
|
||||
// assume that the very first activity of this type is the main one
|
||||
if (activityPath == mainActivityPath)
|
||||
mainActivity = env->NewWeakGlobalRef (context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getMainActivityClassPath()
|
||||
{
|
||||
static String mainActivityClassPath;
|
||||
|
||||
if (mainActivityClassPath.isEmpty())
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> pkgManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
|
||||
LocalRef<jstring> pkgName ((jstring) env->CallObjectMethod (appContext.get(), AndroidContext.getPackageName));
|
||||
|
||||
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
|
||||
javaString ("android.intent.action.MAIN").get()));
|
||||
|
||||
intent = LocalRef<jobject> (env->CallObjectMethod (intent.get(),
|
||||
AndroidIntent.setPackage,
|
||||
pkgName.get()));
|
||||
|
||||
LocalRef<jobject> resolveInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.resolveActivity, intent.get(), 0));
|
||||
|
||||
if (resolveInfo != nullptr)
|
||||
{
|
||||
LocalRef<jobject> activityInfo (env->GetObjectField (resolveInfo.get(), AndroidResolveInfo.activityInfo));
|
||||
LocalRef<jstring> jName ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.name));
|
||||
LocalRef<jstring> jPackage ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.packageName));
|
||||
|
||||
mainActivityClassPath = juceString (jName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainActivityClassPath;
|
||||
}
|
||||
|
||||
GlobalRef myself;
|
||||
CriticalSection currentActivityLock;
|
||||
jweak currentActivity = nullptr;
|
||||
jweak mainActivity = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
|
||||
void juce_juceEventsAndroidStartApp();
|
||||
#endif
|
||||
|
||||
void Thread::initialiseJUCE (void* jniEnv, void* context)
|
||||
{
|
||||
static CriticalSection cs;
|
||||
ScopedLock lock (cs);
|
||||
|
||||
// jniEnv and context should not be null!
|
||||
jassert (jniEnv != nullptr && context != nullptr);
|
||||
|
||||
auto* env = static_cast<JNIEnv*> (jniEnv);
|
||||
|
||||
if (androidJNIJavaVM == nullptr)
|
||||
{
|
||||
JavaVM* javaVM = nullptr;
|
||||
|
||||
auto status = env->GetJavaVM (&javaVM);
|
||||
jassert (status == 0 && javaVM != nullptr);
|
||||
|
||||
androidJNIJavaVM = javaVM;
|
||||
}
|
||||
|
||||
static bool firstCall = true;
|
||||
|
||||
if (firstCall)
|
||||
{
|
||||
firstCall = false;
|
||||
|
||||
// if we ever support unloading then this should probably be a weak reference
|
||||
androidApkContext = env->NewGlobalRef (static_cast<jobject> (context));
|
||||
JuceActivityWatcher::getInstance();
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
|
||||
juce_juceEventsAndroidStartApp();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LocalRef<jobject> getAppContext() noexcept
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto context = androidApkContext;
|
||||
|
||||
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
|
||||
// before using any JUCE APIs. The Projucer will automatically generate java code
|
||||
// which will invoke Thread::initialiseJUCE for you.
|
||||
jassert (env != nullptr && context != nullptr);
|
||||
|
||||
if (context == nullptr)
|
||||
return LocalRef<jobject>();
|
||||
|
||||
if (env->IsInstanceOf (context, AndroidApplication) != 0)
|
||||
return LocalRef<jobject> (env->NewLocalRef (context));
|
||||
|
||||
LocalRef<jobject> applicationContext (env->CallObjectMethod (context, AndroidContext.getApplicationContext));
|
||||
|
||||
if (applicationContext == nullptr)
|
||||
return LocalRef<jobject> (env->NewLocalRef (context));
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
LocalRef<jobject> getCurrentActivity() noexcept
|
||||
{
|
||||
return JuceActivityWatcher::getInstance().getCurrent();
|
||||
}
|
||||
|
||||
LocalRef<jobject> getMainActivity() noexcept
|
||||
{
|
||||
return JuceActivityWatcher::getInstance().getMain();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
||||
@ -74,4 +390,6 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
|
||||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
|
||||
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
@ -23,6 +23,90 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct CURLSymbols
|
||||
{
|
||||
CURL* (*curl_easy_init) (void);
|
||||
CURLcode (*curl_easy_setopt) (CURL *curl, CURLoption option, ...);
|
||||
void (*curl_easy_cleanup) (CURL *curl);
|
||||
CURLcode (*curl_easy_getinfo) (CURL *curl, CURLINFO info, ...);
|
||||
CURLMcode (*curl_multi_add_handle) (CURLM *multi_handle, CURL *curl_handle);
|
||||
CURLMcode (*curl_multi_cleanup) (CURLM *multi_handle);
|
||||
CURLMcode (*curl_multi_fdset) (CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd);
|
||||
CURLMsg* (*curl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
|
||||
CURLM* (*curl_multi_init) (void);
|
||||
CURLMcode (*curl_multi_perform) (CURLM *multi_handle, int *running_handles);
|
||||
CURLMcode (*curl_multi_remove_handle) (CURLM *multi_handle, CURL *curl_handle);
|
||||
CURLMcode (*curl_multi_timeout) (CURLM *multi_handle, long *milliseconds);
|
||||
struct curl_slist* (*curl_slist_append) (struct curl_slist *, const char *);
|
||||
void (*curl_slist_free_all) (struct curl_slist *);
|
||||
curl_version_info_data* (*curl_version_info) (CURLversion);
|
||||
|
||||
static std::unique_ptr<CURLSymbols> create()
|
||||
{
|
||||
std::unique_ptr<CURLSymbols> symbols (new CURLSymbols);
|
||||
|
||||
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
|
||||
const ScopedLock sl (getLibcurlLock());
|
||||
#define JUCE_INIT_CURL_SYMBOL(name) if (! symbols->loadSymbol (symbols->name, #name)) return nullptr;
|
||||
#else
|
||||
#define JUCE_INIT_CURL_SYMBOL(name) symbols->name = ::name;
|
||||
#endif
|
||||
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_init)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_setopt)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_cleanup)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_getinfo)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_add_handle)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_cleanup)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_fdset)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_info_read)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_init)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_perform)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_remove_handle)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_timeout)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_slist_append)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_slist_free_all)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_version_info)
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// liburl's curl_multi_init calls curl_global_init which is not thread safe
|
||||
// so we need to get a lock during calls to curl_multi_init and curl_multi_cleanup
|
||||
static CriticalSection& getLibcurlLock() noexcept
|
||||
{
|
||||
static CriticalSection cs;
|
||||
return cs;
|
||||
}
|
||||
|
||||
private:
|
||||
CURLSymbols() = default;
|
||||
|
||||
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
|
||||
static DynamicLibrary& getLibcurl()
|
||||
{
|
||||
const ScopedLock sl (getLibcurlLock());
|
||||
static DynamicLibrary libcurl;
|
||||
|
||||
if (libcurl.getNativeHandle() == nullptr)
|
||||
for (auto libName : { "libcurl.so", "libcurl.so.4", "libcurl.so.3" })
|
||||
if (libcurl.open (libName))
|
||||
break;
|
||||
|
||||
return libcurl;
|
||||
}
|
||||
|
||||
template <typename FuncPtr>
|
||||
bool loadSymbol (FuncPtr& dst, const char* name)
|
||||
{
|
||||
dst = reinterpret_cast<FuncPtr> (getLibcurl().getFunction (name));
|
||||
return (dst != nullptr);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream::Pimpl
|
||||
{
|
||||
public:
|
||||
@ -30,14 +114,19 @@ public:
|
||||
: owner (ownerStream), url (urlToCopy), isPost (shouldUsePost),
|
||||
httpRequest (isPost ? "POST" : "GET")
|
||||
{
|
||||
multi = curl_multi_init();
|
||||
jassert (symbols); // Unable to load libcurl!
|
||||
|
||||
{
|
||||
const ScopedLock sl (CURLSymbols::getLibcurlLock());
|
||||
multi = symbols->curl_multi_init();
|
||||
}
|
||||
|
||||
if (multi != nullptr)
|
||||
{
|
||||
curl = curl_easy_init();
|
||||
curl = symbols->curl_easy_init();
|
||||
|
||||
if (curl != nullptr)
|
||||
if (curl_multi_add_handle (multi, curl) == CURLM_OK)
|
||||
if (symbols->curl_multi_add_handle (multi, curl) == CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -100,24 +189,25 @@ public:
|
||||
void cleanup()
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
const ScopedLock sl (CURLSymbols::getLibcurlLock());
|
||||
|
||||
if (curl != nullptr)
|
||||
{
|
||||
curl_multi_remove_handle (multi, curl);
|
||||
symbols->curl_multi_remove_handle (multi, curl);
|
||||
|
||||
if (headerList != nullptr)
|
||||
{
|
||||
curl_slist_free_all (headerList);
|
||||
symbols->curl_slist_free_all (headerList);
|
||||
headerList = nullptr;
|
||||
}
|
||||
|
||||
curl_easy_cleanup (curl);
|
||||
symbols->curl_easy_cleanup (curl);
|
||||
curl = nullptr;
|
||||
}
|
||||
|
||||
if (multi != nullptr)
|
||||
{
|
||||
curl_multi_cleanup (multi);
|
||||
symbols->curl_multi_cleanup (multi);
|
||||
multi = nullptr;
|
||||
}
|
||||
}
|
||||
@ -132,7 +222,7 @@ public:
|
||||
{
|
||||
auto address = url.toString (! isPost);
|
||||
|
||||
curl_version_info_data* data = curl_version_info (CURLVERSION_NOW);
|
||||
curl_version_info_data* data = symbols->curl_version_info (CURLVERSION_NOW);
|
||||
jassert (data != nullptr);
|
||||
|
||||
if (! requestHeaders.endsWithChar ('\n'))
|
||||
@ -146,22 +236,22 @@ public:
|
||||
|
||||
auto userAgent = String ("curl/") + data->version;
|
||||
|
||||
if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
|
||||
&& curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
|
||||
{
|
||||
if (isPost)
|
||||
{
|
||||
if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|
||||
|| curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|
||||
|| curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -169,20 +259,20 @@ public:
|
||||
bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET");
|
||||
|
||||
if (hasSpecialRequestCmd)
|
||||
if (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|
||||
|| curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (timeOutMs > 0)
|
||||
{
|
||||
auto timeOutSecs = ((long) timeOutMs + 999) / 1000;
|
||||
|
||||
if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|
||||
|| curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|
||||
|| curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -212,10 +302,10 @@ public:
|
||||
|
||||
// fromLines will always return at least one line if the string is not empty
|
||||
jassert (headerLines.size() > 0);
|
||||
headerList = curl_slist_append (headerList, headerLines [0].toRawUTF8());
|
||||
headerList = symbols->curl_slist_append (headerList, headerLines [0].toRawUTF8());
|
||||
|
||||
for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i)
|
||||
headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8());
|
||||
headerList = symbols->curl_slist_append (headerList, headerLines [i].toRawUTF8());
|
||||
|
||||
if (headerList == nullptr)
|
||||
{
|
||||
@ -223,7 +313,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
|
||||
{
|
||||
cleanup();
|
||||
return false;
|
||||
@ -272,12 +362,12 @@ public:
|
||||
return false;
|
||||
|
||||
long responseCode;
|
||||
if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
|
||||
if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
|
||||
statusCode = static_cast<int> (responseCode);
|
||||
|
||||
// get content length size
|
||||
double curlLength;
|
||||
if (curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK)
|
||||
if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK)
|
||||
contentLength = static_cast<int64> (curlLength);
|
||||
}
|
||||
|
||||
@ -295,7 +385,7 @@ public:
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
if (CURLMsg* msg = curl_multi_info_read (multi, &cnt))
|
||||
if (CURLMsg* msg = symbols->curl_multi_info_read (multi, &cnt))
|
||||
{
|
||||
if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl)
|
||||
{
|
||||
@ -328,7 +418,7 @@ public:
|
||||
if (multi == nullptr)
|
||||
return;
|
||||
|
||||
if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK)
|
||||
if ((lastError = (int) symbols->curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -350,7 +440,7 @@ public:
|
||||
if (multi == nullptr)
|
||||
return;
|
||||
|
||||
if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK)
|
||||
if ((lastError = (int) symbols->curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -374,7 +464,7 @@ public:
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM)
|
||||
while ((curlRet = (int) symbols->curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM)
|
||||
{}
|
||||
}
|
||||
|
||||
@ -510,6 +600,7 @@ public:
|
||||
//==============================================================================
|
||||
WebInputStream& owner;
|
||||
const URL url;
|
||||
std::unique_ptr<CURLSymbols> symbols { CURLSymbols::create() };
|
||||
|
||||
//==============================================================================
|
||||
// curl stuff
|
||||
|
@ -189,29 +189,27 @@ static bool isFileExecutable (const String& filename)
|
||||
|
||||
bool Process::openDocument (const String& fileName, const String& parameters)
|
||||
{
|
||||
String cmdString (fileName.replace (" ", "\\ ",false));
|
||||
auto cmdString = fileName.replace (" ", "\\ ", false);
|
||||
cmdString << " " << parameters;
|
||||
|
||||
if (/*URL::isProbablyAWebsiteURL (fileName)
|
||||
||*/ cmdString.startsWithIgnoreCase ("file:")
|
||||
/*|| URL::isProbablyAnEmailAddress (fileName)*/
|
||||
if (cmdString.startsWithIgnoreCase ("file:")
|
||||
|| File::createFileWithoutCheckingPath (fileName).isDirectory()
|
||||
|| ! isFileExecutable (fileName))
|
||||
{
|
||||
// create a command that tries to launch a bunch of likely browsers
|
||||
static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
|
||||
"google-chrome", "chromium-browser", "opera", "konqueror" };
|
||||
StringArray cmdLines;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (browserNames); ++i)
|
||||
cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted());
|
||||
for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
|
||||
"google-chrome", "chromium-browser", "opera", "konqueror" })
|
||||
{
|
||||
cmdLines.add (String (browserName) + " " + cmdString.trim());
|
||||
}
|
||||
|
||||
cmdString = cmdLines.joinIntoString (" || ");
|
||||
}
|
||||
|
||||
const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 };
|
||||
|
||||
const int cpid = fork();
|
||||
auto cpid = fork();
|
||||
|
||||
if (cpid == 0)
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ String SystemStats::getCpuModel()
|
||||
return getCpuInfo ("model name");
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegaherz()
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
return roundToInt (getCpuInfo ("cpu MHz").getFloatValue());
|
||||
}
|
||||
@ -142,16 +142,26 @@ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getU
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
auto flags = getCpuInfo ("flags");
|
||||
hasMMX = flags.contains ("mmx");
|
||||
hasSSE = flags.contains ("sse");
|
||||
hasSSE2 = flags.contains ("sse2");
|
||||
hasSSE3 = flags.contains ("sse3");
|
||||
has3DNow = flags.contains ("3dnow");
|
||||
hasSSSE3 = flags.contains ("ssse3");
|
||||
hasSSE41 = flags.contains ("sse4_1");
|
||||
hasSSE42 = flags.contains ("sse4_2");
|
||||
hasAVX = flags.contains ("avx");
|
||||
hasAVX2 = flags.contains ("avx2");
|
||||
hasMMX = flags.contains ("mmx");
|
||||
hasSSE = flags.contains ("sse");
|
||||
hasSSE2 = flags.contains ("sse2");
|
||||
hasSSE3 = flags.contains ("sse3");
|
||||
has3DNow = flags.contains ("3dnow");
|
||||
hasSSSE3 = flags.contains ("ssse3");
|
||||
hasSSE41 = flags.contains ("sse4_1");
|
||||
hasSSE42 = flags.contains ("sse4_2");
|
||||
hasAVX = flags.contains ("avx");
|
||||
hasAVX2 = flags.contains ("avx2");
|
||||
hasAVX512F = flags.contains ("avx512f");
|
||||
hasAVX512BW = flags.contains ("avx512bw");
|
||||
hasAVX512CD = flags.contains ("avx512cd");
|
||||
hasAVX512DQ = flags.contains ("avx512dq");
|
||||
hasAVX512ER = flags.contains ("avx512er");
|
||||
hasAVX512IFMA = flags.contains ("avx512ifma");
|
||||
hasAVX512PF = flags.contains ("avx512pf");
|
||||
hasAVX512VBMI = flags.contains ("avx512vbmi");
|
||||
hasAVX512VL = flags.contains ("avx512vl");
|
||||
hasAVX512VPOPCNTDQ = flags.contains ("avx512_vpopcntdq");
|
||||
|
||||
numLogicalCPUs = getCpuInfo ("processor").getIntValue() + 1;
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace MacFileHelpers
|
||||
{
|
||||
const String type (buf.f_fstypename);
|
||||
|
||||
while (*types != 0)
|
||||
while (*types != nullptr)
|
||||
if (type.equalsIgnoreCase (*types++))
|
||||
return true;
|
||||
}
|
||||
@ -105,14 +105,14 @@ namespace MacFileHelpers
|
||||
#else
|
||||
static bool launchExecutable (const String& pathAndArguments)
|
||||
{
|
||||
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 };
|
||||
|
||||
const int cpid = fork();
|
||||
auto cpid = fork();
|
||||
|
||||
if (cpid == 0)
|
||||
{
|
||||
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), nullptr };
|
||||
|
||||
// Child process
|
||||
if (execve (argv[0], (char**) argv, 0) < 0)
|
||||
if (execve (argv[0], (char**) argv, nullptr) < 0)
|
||||
exit (0);
|
||||
}
|
||||
else
|
||||
@ -412,10 +412,8 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
NSString* fileNameAsNS (juceStringToNS (fileName));
|
||||
NSURL* filenameAsURL ([NSURL URLWithString: fileNameAsNS]);
|
||||
|
||||
if (filenameAsURL == nil)
|
||||
filenameAsURL = [NSURL fileURLWithPath: fileNameAsNS];
|
||||
NSURL* filenameAsURL = File::createFileWithoutCheckingPath (fileName).exists() ? [NSURL fileURLWithPath: fileNameAsNS]
|
||||
: [NSURL URLWithString: fileNameAsNS];
|
||||
|
||||
#if JUCE_IOS
|
||||
ignoreUnused (parameters);
|
||||
@ -442,11 +440,13 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
|
||||
StringArray params;
|
||||
params.addTokens (parameters, true);
|
||||
|
||||
NSMutableArray* paramArray = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
|
||||
|
||||
NSMutableArray* paramArray = [[NSMutableArray new] autorelease];
|
||||
|
||||
for (int i = 0; i < params.size(); ++i)
|
||||
[paramArray addObject: juceStringToNS (params[i])];
|
||||
|
||||
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
|
||||
[dict setObject: paramArray
|
||||
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];
|
||||
|
||||
|
@ -32,12 +32,12 @@ void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next)
|
||||
{
|
||||
// Required to avoid misaligned pointer access
|
||||
sockaddr_storage sto;
|
||||
std::memcpy (&sto, cursor->ifa_addr, sizeof (sockaddr_storage));
|
||||
sockaddr sto;
|
||||
std::memcpy (&sto, cursor->ifa_addr, sizeof (sockaddr));
|
||||
|
||||
if (sto.ss_family == AF_LINK)
|
||||
if (sto.sa_family == AF_LINK)
|
||||
{
|
||||
auto sadd = (const sockaddr_dl*) cursor->ifa_addr;
|
||||
auto sadd = reinterpret_cast<const sockaddr_dl*> (cursor->ifa_addr);
|
||||
|
||||
#ifndef IFT_ETHER
|
||||
enum { IFT_ETHER = 6 };
|
||||
@ -127,7 +127,7 @@ public:
|
||||
DelegateClass::setState (delegate, this);
|
||||
}
|
||||
|
||||
~URLConnectionState()
|
||||
~URLConnectionState() override
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
|
||||
@ -331,7 +331,8 @@ public:
|
||||
NSMutableData* data = nil;
|
||||
NSDictionary* headers = nil;
|
||||
int statusCode = 0;
|
||||
bool initialised = false, hasFailed = false, hasFinished = false, isBeingDeleted = false;
|
||||
std::atomic<bool> initialised { false };
|
||||
bool hasFailed = false, hasFinished = false, isBeingDeleted = false;
|
||||
const int numRedirectsToFollow;
|
||||
int numRedirects = 0;
|
||||
int64 latestTotalBytes = 0;
|
||||
@ -413,9 +414,10 @@ struct BackgroundDownloadTask : public URL::DownloadTask
|
||||
String extraHeadersToUse,
|
||||
URL::DownloadTask::Listener* listenerToUse,
|
||||
bool shouldUsePostRequest)
|
||||
: targetLocation (targetLocationToUse), listener (listenerToUse),
|
||||
: listener (listenerToUse),
|
||||
uniqueIdentifier (String (urlToUse.toString (true).hashCode64()) + String (Random().nextInt64()))
|
||||
{
|
||||
targetLocation = targetLocationToUse;
|
||||
downloaded = -1;
|
||||
|
||||
static DelegateClass cls;
|
||||
@ -488,7 +490,6 @@ struct BackgroundDownloadTask : public URL::DownloadTask
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File targetLocation;
|
||||
URL::DownloadTask::Listener* listener;
|
||||
NSObject<NSURLSessionDelegate>* delegate = nil;
|
||||
NSURLSession* session = nil;
|
||||
@ -691,7 +692,7 @@ public:
|
||||
DelegateClass::setState (delegate, this);
|
||||
}
|
||||
|
||||
~URLConnectionState()
|
||||
~URLConnectionState() override
|
||||
{
|
||||
stop();
|
||||
|
||||
@ -808,7 +809,8 @@ public:
|
||||
{
|
||||
DBG (nsStringToJuce ([error description])); ignoreUnused (error);
|
||||
nsUrlErrorCode = [error code];
|
||||
hasFailed = initialised = true;
|
||||
hasFailed = true;
|
||||
initialised = true;
|
||||
signalThreadShouldExit();
|
||||
}
|
||||
|
||||
@ -826,7 +828,8 @@ public:
|
||||
|
||||
void finishedLoading()
|
||||
{
|
||||
hasFinished = initialised = true;
|
||||
hasFinished = true;
|
||||
initialised = true;
|
||||
signalThreadShouldExit();
|
||||
}
|
||||
|
||||
@ -860,7 +863,7 @@ public:
|
||||
NSDictionary* headers = nil;
|
||||
NSInteger nsUrlErrorCode = 0;
|
||||
int statusCode = 0;
|
||||
bool initialised = false, hasFailed = false, hasFinished = false;
|
||||
std::atomic<bool> initialised { false }, hasFailed { false }, hasFinished { false };
|
||||
const int numRedirectsToFollow;
|
||||
int numRedirects = 0;
|
||||
int latestTotalBytes = 0;
|
||||
@ -962,6 +965,9 @@ public:
|
||||
createConnection();
|
||||
}
|
||||
|
||||
if (connection == nullptr)
|
||||
return false;
|
||||
|
||||
if (! connection->start (owner, webInputListener))
|
||||
{
|
||||
// Workaround for deployment targets below 10.10 where HTTPS POST requests with keep-alive fail with the NSURLErrorNetworkConnectionLost error code.
|
||||
@ -977,7 +983,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connection != nullptr && connection->headers != nil)
|
||||
if (connection->headers != nil)
|
||||
{
|
||||
statusCode = connection->statusCode;
|
||||
|
||||
@ -1093,38 +1099,44 @@ private:
|
||||
{
|
||||
jassert (connection == nullptr);
|
||||
|
||||
if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url.toString (! isPost))]
|
||||
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)])
|
||||
if (NSURL* nsURL = [NSURL URLWithString: juceStringToNS (url.toString (! isPost))])
|
||||
{
|
||||
[req setHTTPMethod: [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()]];
|
||||
|
||||
if (isPost)
|
||||
if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: nsURL
|
||||
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)])
|
||||
{
|
||||
WebInputStream::createHeadersAndPostData (url, headers, postData);
|
||||
if (NSString* httpMethod = [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()])
|
||||
{
|
||||
[req setHTTPMethod: httpMethod];
|
||||
|
||||
if (postData.getSize() > 0)
|
||||
[req setHTTPBody: [NSData dataWithBytes: postData.getData()
|
||||
length: postData.getSize()]];
|
||||
if (isPost)
|
||||
{
|
||||
WebInputStream::createHeadersAndPostData (url, headers, postData);
|
||||
|
||||
if (postData.getSize() > 0)
|
||||
[req setHTTPBody: [NSData dataWithBytes: postData.getData()
|
||||
length: postData.getSize()]];
|
||||
}
|
||||
|
||||
StringArray headerLines;
|
||||
headerLines.addLines (headers);
|
||||
headerLines.removeEmptyStrings (true);
|
||||
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
auto key = headerLines[i].upToFirstOccurrenceOf (":", false, false).trim();
|
||||
auto value = headerLines[i].fromFirstOccurrenceOf (":", false, false).trim();
|
||||
|
||||
if (key.isNotEmpty() && value.isNotEmpty())
|
||||
[req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)];
|
||||
}
|
||||
|
||||
// Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334
|
||||
[req HTTPBody];
|
||||
|
||||
connection.reset (new URLConnectionState (req, numRedirectsToFollow));
|
||||
}
|
||||
}
|
||||
|
||||
StringArray headerLines;
|
||||
headerLines.addLines (headers);
|
||||
headerLines.removeEmptyStrings (true);
|
||||
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
String key = headerLines[i].upToFirstOccurrenceOf (":", false, false).trim();
|
||||
String value = headerLines[i].fromFirstOccurrenceOf (":", false, false).trim();
|
||||
|
||||
if (key.isNotEmpty() && value.isNotEmpty())
|
||||
[req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)];
|
||||
}
|
||||
|
||||
// Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334
|
||||
[req HTTPBody];
|
||||
|
||||
connection.reset (new URLConnectionState (req, numRedirectsToFollow));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace juce
|
||||
|
||||
String String::fromCFString (CFStringRef cfString)
|
||||
{
|
||||
if (cfString == 0)
|
||||
if (cfString == nullptr)
|
||||
return {};
|
||||
|
||||
CFRange range = { 0, CFStringGetLength (cfString) };
|
||||
@ -72,7 +72,7 @@ String String::convertToPrecomposedUnicode() const
|
||||
|
||||
map.mappingVersion = kUnicodeUseLatestMapping;
|
||||
|
||||
UnicodeToTextInfo conversionInfo = 0;
|
||||
UnicodeToTextInfo conversionInfo = {};
|
||||
String result;
|
||||
|
||||
if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr)
|
||||
@ -88,11 +88,11 @@ String String::convertToPrecomposedUnicode() const
|
||||
if (ConvertFromUnicodeToText (conversionInfo,
|
||||
bytesNeeded, (ConstUniCharArrayPtr) toUTF16().getAddress(),
|
||||
kUnicodeDefaultDirectionMask,
|
||||
0, 0, 0, 0,
|
||||
0, {}, {}, {},
|
||||
bytesNeeded, &bytesRead,
|
||||
&outputBufferSize, tempOut) == noErr)
|
||||
{
|
||||
result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.get()));
|
||||
result = String (CharPointer_UTF16 (reinterpret_cast<CharPointer_UTF16::CharType*> (tempOut.get())));
|
||||
}
|
||||
|
||||
DisposeUnicodeToTextInfo (&conversionInfo);
|
||||
|
@ -51,14 +51,17 @@ namespace SystemStatsHelpers
|
||||
{
|
||||
uint32 la = a, lb = b, lc = c, ld = d;
|
||||
|
||||
asm ("mov %%ebx, %%esi \n\t"
|
||||
"cpuid \n\t"
|
||||
"xchg %%esi, %%ebx"
|
||||
: "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type)
|
||||
#if JUCE_64BIT
|
||||
, "b" (lb), "c" (lc), "d" (ld)
|
||||
#endif
|
||||
);
|
||||
#if JUCE_32BIT && defined (__pic__)
|
||||
asm ("mov %%ebx, %%edi\n"
|
||||
"cpuid\n"
|
||||
"xchg %%edi, %%ebx\n"
|
||||
: "=a" (la), "=D" (lb), "=c" (lc), "=d" (ld)
|
||||
: "a" (type), "c" (0));
|
||||
#else
|
||||
asm ("cpuid\n"
|
||||
: "=a" (la), "=b" (lb), "=c" (lc), "=d" (ld)
|
||||
: "a" (type), "c" (0));
|
||||
#endif
|
||||
|
||||
a = la; b = lb; c = lc; d = ld;
|
||||
}
|
||||
@ -83,7 +86,17 @@ void CPUInformation::initialise() noexcept
|
||||
hasAVX = (c & (1u << 28)) != 0;
|
||||
|
||||
SystemStatsHelpers::doCPUID (a, b, c, d, 7);
|
||||
hasAVX2 = (b & (1u << 5)) != 0;
|
||||
hasAVX2 = (b & (1u << 5)) != 0;
|
||||
hasAVX512F = (b & (1u << 16)) != 0;
|
||||
hasAVX512DQ = (b & (1u << 17)) != 0;
|
||||
hasAVX512IFMA = (b & (1u << 21)) != 0;
|
||||
hasAVX512PF = (b & (1u << 26)) != 0;
|
||||
hasAVX512ER = (b & (1u << 27)) != 0;
|
||||
hasAVX512CD = (b & (1u << 28)) != 0;
|
||||
hasAVX512BW = (b & (1u << 30)) != 0;
|
||||
hasAVX512VL = (b & (1u << 31)) != 0;
|
||||
hasAVX512VBMI = (c & (1u << 1)) != 0;
|
||||
hasAVX512VPOPCNTDQ = (c & (1u << 14)) != 0;
|
||||
#endif
|
||||
|
||||
numLogicalCPUs = (int) [[NSProcessInfo processInfo] activeProcessorCount];
|
||||
@ -140,17 +153,20 @@ String SystemStats::getOperatingSystemName()
|
||||
String SystemStats::getDeviceDescription()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return nsStringToJuce ([[UIDevice currentDevice] model]);
|
||||
const char* name = "hw.machine";
|
||||
#else
|
||||
const char* name = "hw.model";
|
||||
#endif
|
||||
|
||||
size_t size;
|
||||
if (sysctlbyname ("hw.model", nullptr, &size, nullptr, 0) >= 0)
|
||||
if (sysctlbyname (name, nullptr, &size, nullptr, 0) >= 0)
|
||||
{
|
||||
HeapBlock<char> model (size);
|
||||
if (sysctlbyname ("hw.model", model, &size, nullptr, 0) >= 0)
|
||||
if (sysctlbyname (name, model, &size, nullptr, 0) >= 0)
|
||||
return model.get();
|
||||
}
|
||||
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceManufacturer()
|
||||
@ -174,7 +190,7 @@ int SystemStats::getMemorySizeInMegabytes()
|
||||
uint64 mem = 0;
|
||||
size_t memSize = sizeof (mem);
|
||||
int mib[] = { CTL_HW, HW_MEMSIZE };
|
||||
sysctl (mib, 2, &mem, &memSize, 0, 0);
|
||||
sysctl (mib, 2, &mem, &memSize, nullptr, 0);
|
||||
return (int) (mem / (1024 * 1024));
|
||||
}
|
||||
|
||||
@ -203,12 +219,12 @@ String SystemStats::getCpuModel()
|
||||
return {};
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegaherz()
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
uint64 speedHz = 0;
|
||||
size_t speedSize = sizeof (speedHz);
|
||||
int mib[] = { CTL_HW, HW_CPU_FREQ };
|
||||
sysctl (mib, 2, &speedHz, &speedSize, 0, 0);
|
||||
sysctl (mib, 2, &speedHz, &speedSize, nullptr, 0);
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
if (speedSize == 4)
|
||||
|
@ -86,7 +86,7 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
struct kinfo_proc info;
|
||||
int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
|
||||
size_t sz = sizeof (info);
|
||||
sysctl (m, 4, &info, &sz, 0, 0);
|
||||
sysctl (m, 4, &info, &sz, nullptr, 0);
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ static inline NSURL* createNSURLFromFile (const File& f)
|
||||
|
||||
static inline NSArray* createNSArrayFromStringArray (const StringArray& strings)
|
||||
{
|
||||
auto* array = [[NSMutableArray alloc] init];
|
||||
auto array = [[NSMutableArray alloc] init];
|
||||
|
||||
for (auto string: strings)
|
||||
[array addObject:juceStringToNS (string)];
|
||||
@ -71,7 +71,7 @@ static NSArray* varArrayToNSArray (const var& varToParse);
|
||||
|
||||
static NSDictionary* varObjectToNSDictionary (const var& varToParse)
|
||||
{
|
||||
auto* dictionary = [NSMutableDictionary dictionary];
|
||||
auto dictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
if (varToParse.isObject())
|
||||
{
|
||||
@ -118,7 +118,7 @@ static NSArray* varArrayToNSArray (const var& varToParse)
|
||||
|
||||
const auto* varArray = varToParse.getArray();
|
||||
|
||||
auto* array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
|
||||
auto array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
|
||||
|
||||
for (const auto& aVar : *varArray)
|
||||
{
|
||||
@ -145,29 +145,14 @@ static NSArray* varArrayToNSArray (const var& varToParse)
|
||||
return array;
|
||||
}
|
||||
|
||||
static var nsArrayToVar (NSArray* array);
|
||||
static var nsObjectToVar (NSObject* array);
|
||||
|
||||
static var nsDictionaryToVar (NSDictionary* dictionary)
|
||||
{
|
||||
DynamicObject::Ptr dynamicObject = new DynamicObject();
|
||||
DynamicObject::Ptr dynamicObject (new DynamicObject());
|
||||
|
||||
for (NSString* key in dictionary)
|
||||
{
|
||||
const auto keyString = nsStringToJuce (key);
|
||||
|
||||
id value = dictionary[key];
|
||||
|
||||
if ([value isKindOfClass: [NSString class]])
|
||||
dynamicObject->setProperty (keyString, nsStringToJuce ((NSString*) value));
|
||||
else if ([value isKindOfClass: [NSNumber class]])
|
||||
dynamicObject->setProperty (keyString, nsStringToJuce ([(NSNumber*) value stringValue]));
|
||||
else if ([value isKindOfClass: [NSDictionary class]])
|
||||
dynamicObject->setProperty (keyString, nsDictionaryToVar ((NSDictionary*) value));
|
||||
else if ([value isKindOfClass: [NSArray class]])
|
||||
dynamicObject->setProperty (keyString, nsArrayToVar ((NSArray*) value));
|
||||
else
|
||||
jassertfalse; // Unsupported yet, add here!
|
||||
}
|
||||
dynamicObject->setProperty (nsStringToJuce (key), nsObjectToVar (dictionary[key]));
|
||||
|
||||
return var (dynamicObject.get());
|
||||
}
|
||||
@ -177,22 +162,26 @@ static var nsArrayToVar (NSArray* array)
|
||||
Array<var> resultArray;
|
||||
|
||||
for (id value in array)
|
||||
{
|
||||
if ([value isKindOfClass: [NSString class]])
|
||||
resultArray.add (var (nsStringToJuce ((NSString*) value)));
|
||||
else if ([value isKindOfClass: [NSNumber class]])
|
||||
resultArray.add (var (nsStringToJuce ([(NSNumber*) value stringValue])));
|
||||
else if ([value isKindOfClass: [NSDictionary class]])
|
||||
resultArray.add (nsDictionaryToVar ((NSDictionary*) value));
|
||||
else if ([value isKindOfClass: [NSArray class]])
|
||||
resultArray.add (nsArrayToVar ((NSArray*) value));
|
||||
else
|
||||
jassertfalse; // Unsupported yet, add here!
|
||||
}
|
||||
resultArray.add (nsObjectToVar (value));
|
||||
|
||||
return var (resultArray);
|
||||
}
|
||||
|
||||
static var nsObjectToVar (NSObject* obj)
|
||||
{
|
||||
if ([obj isKindOfClass: [NSString class]]) return nsStringToJuce ((NSString*) obj);
|
||||
else if ([obj isKindOfClass: [NSNumber class]]) return nsStringToJuce ([(NSNumber*) obj stringValue]);
|
||||
else if ([obj isKindOfClass: [NSDictionary class]]) return nsDictionaryToVar ((NSDictionary*) obj);
|
||||
else if ([obj isKindOfClass: [NSArray class]]) return nsArrayToVar ((NSArray*) obj);
|
||||
else
|
||||
{
|
||||
// Unsupported yet, add here!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#if JUCE_MAC
|
||||
template <typename RectangleType>
|
||||
static NSRect makeNSRect (const RectangleType& r) noexcept
|
||||
@ -211,7 +200,7 @@ static NSRect makeNSRect (const RectangleType& r) noexcept
|
||||
template <typename ReturnValue, typename... Params>
|
||||
static inline ReturnValue ObjCMsgSendSuper (struct objc_super* s, SEL sel, Params... params)
|
||||
{
|
||||
typedef ReturnValue (*SuperFn)(struct objc_super*, SEL, Params...);
|
||||
using SuperFn = ReturnValue (*)(struct objc_super*, SEL, Params...);
|
||||
SuperFn fn = reinterpret_cast<SuperFn> (objc_msgSendSuper);
|
||||
return fn (s, sel, params...);
|
||||
}
|
||||
@ -226,16 +215,6 @@ static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
template <typename ObjectType>
|
||||
struct NSObjectRetainer
|
||||
{
|
||||
inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; }
|
||||
inline ~NSObjectRetainer() { [object release]; }
|
||||
|
||||
ObjectType* object;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct NSObjectDeleter
|
||||
{
|
||||
|
133
modules/juce_core/native/juce_posix_IPAddress.h
Normal file
133
modules/juce_core/native/juce_posix_IPAddress.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
static IPAddress makeAddress (const sockaddr_in6* addr_in)
|
||||
{
|
||||
if (addr_in == nullptr)
|
||||
return {};
|
||||
|
||||
auto addr = addr_in->sin6_addr;
|
||||
|
||||
IPAddressByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i) // Swap bytes from network to host order
|
||||
{
|
||||
temp.split[0] = addr.s6_addr[i * 2 + 1];
|
||||
temp.split[1] = addr.s6_addr[i * 2];
|
||||
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
return IPAddress (arr);
|
||||
}
|
||||
|
||||
static IPAddress makeAddress (const sockaddr_in* addr_in)
|
||||
{
|
||||
if (addr_in->sin_addr.s_addr == INADDR_NONE)
|
||||
return {};
|
||||
|
||||
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
struct InterfaceInfo
|
||||
{
|
||||
IPAddress interfaceAddress, broadcastAddress;
|
||||
};
|
||||
|
||||
bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs)
|
||||
{
|
||||
return lhs.interfaceAddress == rhs.interfaceAddress
|
||||
&& lhs.broadcastAddress == rhs.broadcastAddress;
|
||||
}
|
||||
|
||||
bool populateInterfaceInfo (struct ifaddrs* ifa, InterfaceInfo& interfaceInfo)
|
||||
{
|
||||
if (ifa->ifa_addr != nullptr)
|
||||
{
|
||||
if (ifa->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
auto interfaceAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_addr);
|
||||
auto broadcastAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_dstaddr);
|
||||
|
||||
if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE)
|
||||
{
|
||||
interfaceInfo.interfaceAddress = makeAddress (interfaceAddressInfo);
|
||||
interfaceInfo.broadcastAddress = makeAddress (broadcastAddressInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ifa->ifa_addr->sa_family == AF_INET6)
|
||||
{
|
||||
interfaceInfo.interfaceAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_addr));
|
||||
interfaceInfo.broadcastAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_dstaddr));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<InterfaceInfo> getAllInterfaceInfo()
|
||||
{
|
||||
Array<InterfaceInfo> interfaces;
|
||||
struct ifaddrs* ifaddr = nullptr;
|
||||
|
||||
if (getifaddrs (&ifaddr) != -1)
|
||||
{
|
||||
for (auto* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
|
||||
{
|
||||
InterfaceInfo i;
|
||||
|
||||
if (populateInterfaceInfo (ifa, i))
|
||||
interfaces.addIfNotAlreadyThere (i);
|
||||
}
|
||||
|
||||
freeifaddrs (ifaddr);
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
|
||||
{
|
||||
for (auto& i : getAllInterfaceInfo())
|
||||
if (includeIPv6 || ! i.interfaceAddress.isIPv6)
|
||||
result.addIfNotAlreadyThere (i.interfaceAddress);
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& interfaceAddress)
|
||||
{
|
||||
for (auto& i : getAllInterfaceInfo())
|
||||
if (i.interfaceAddress == interfaceAddress)
|
||||
return i.broadcastAddress;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace juce
|
@ -47,18 +47,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool connect (int timeOutMilliseconds)
|
||||
{
|
||||
return openPipe (true, getTimeoutEnd (timeOutMilliseconds));
|
||||
}
|
||||
|
||||
int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
|
||||
{
|
||||
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
|
||||
|
||||
if (pipeIn == -1)
|
||||
{
|
||||
pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd);
|
||||
|
||||
if (pipeIn == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bytesRead = 0;
|
||||
|
||||
while (bytesRead < maxBytesToRead)
|
||||
@ -89,13 +85,8 @@ public:
|
||||
{
|
||||
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
|
||||
|
||||
if (pipeOut == -1)
|
||||
{
|
||||
pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd);
|
||||
|
||||
if (pipeOut == -1)
|
||||
return -1;
|
||||
}
|
||||
if (! openPipe (false, timeoutEnd))
|
||||
return -1;
|
||||
|
||||
int bytesWritten = 0;
|
||||
|
||||
@ -160,6 +151,25 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool openPipe (bool isInput, uint32 timeoutEnd)
|
||||
{
|
||||
auto& pipe = isInput ? pipeIn : pipeOut;
|
||||
int flags = isInput ? O_RDWR | O_NONBLOCK : O_WRONLY;
|
||||
|
||||
const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName)
|
||||
: (createdPipe ? pipeOutName : pipeInName);
|
||||
|
||||
if (pipe == -1)
|
||||
{
|
||||
pipe = openPipe (pipeName, flags, timeoutEnd);
|
||||
|
||||
if (pipe == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void waitForInput (int handle, int timeoutMsecs) noexcept
|
||||
{
|
||||
struct timeval timeout;
|
||||
@ -170,7 +180,7 @@ private:
|
||||
FD_ZERO (&rset);
|
||||
FD_SET (handle, &rset);
|
||||
|
||||
select (handle + 1, &rset, nullptr, 0, &timeout);
|
||||
select (handle + 1, &rset, nullptr, nullptr, &timeout);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
@ -211,6 +221,12 @@ bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool must
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! pimpl->connect (200))
|
||||
{
|
||||
pimpl.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&loc
|
||||
WaitableEvent::WaitableEvent (bool useManualReset) noexcept
|
||||
: triggered (false), manualReset (useManualReset)
|
||||
{
|
||||
pthread_cond_init (&condition, 0);
|
||||
pthread_cond_init (&condition, {});
|
||||
|
||||
pthread_mutexattr_t atts;
|
||||
pthread_mutexattr_init (&atts);
|
||||
@ -78,7 +78,7 @@ bool WaitableEvent::wait (int timeOutMillisecs) const noexcept
|
||||
else
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday (&now, 0);
|
||||
gettimeofday (&now, nullptr);
|
||||
|
||||
struct timespec time;
|
||||
time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000);
|
||||
@ -428,11 +428,14 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
|
||||
|
||||
bool File::deleteFile() const
|
||||
{
|
||||
if (! exists() && ! isSymbolicLink())
|
||||
return true;
|
||||
if (! isSymbolicLink())
|
||||
{
|
||||
if (! exists())
|
||||
return true;
|
||||
|
||||
if (isDirectory())
|
||||
return rmdir (fullPath.toUTF8()) == 0;
|
||||
if (isDirectory())
|
||||
return rmdir (fullPath.toUTF8()) == 0;
|
||||
}
|
||||
|
||||
return remove (fullPath.toUTF8()) == 0;
|
||||
}
|
||||
@ -466,7 +469,7 @@ Result File::createDirectoryInternal (const String& fileName) const
|
||||
//==============================================================================
|
||||
int64 juce_fileSetPosition (void* handle, int64 pos)
|
||||
{
|
||||
if (handle != 0 && lseek (getFD (handle), (off_t) pos, SEEK_SET) == pos)
|
||||
if (handle != nullptr && lseek (getFD (handle), (off_t) pos, SEEK_SET) == pos)
|
||||
return pos;
|
||||
|
||||
return -1;
|
||||
@ -484,7 +487,7 @@ void FileInputStream::openHandle()
|
||||
|
||||
FileInputStream::~FileInputStream()
|
||||
{
|
||||
if (fileHandle != 0)
|
||||
if (fileHandle != nullptr)
|
||||
close (getFD (fileHandle));
|
||||
}
|
||||
|
||||
@ -492,7 +495,7 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
|
||||
{
|
||||
ssize_t result = 0;
|
||||
|
||||
if (fileHandle != 0)
|
||||
if (fileHandle != nullptr)
|
||||
{
|
||||
result = ::read (getFD (fileHandle), buffer, numBytes);
|
||||
|
||||
@ -545,16 +548,16 @@ void FileOutputStream::openHandle()
|
||||
|
||||
void FileOutputStream::closeHandle()
|
||||
{
|
||||
if (fileHandle != 0)
|
||||
if (fileHandle != nullptr)
|
||||
{
|
||||
close (getFD (fileHandle));
|
||||
fileHandle = 0;
|
||||
fileHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
|
||||
{
|
||||
if (fileHandle == 0)
|
||||
if (fileHandle == nullptr)
|
||||
return 0;
|
||||
|
||||
auto result = ::write (getFD (fileHandle), data, numBytes);
|
||||
@ -568,14 +571,14 @@ ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
|
||||
#ifndef JUCE_ANDROID
|
||||
void FileOutputStream::flushInternal()
|
||||
{
|
||||
if (fileHandle != 0 && fsync (getFD (fileHandle)) == -1)
|
||||
if (fileHandle != nullptr && fsync (getFD (fileHandle)) == -1)
|
||||
status = getResultForErrno();
|
||||
}
|
||||
#endif
|
||||
|
||||
Result FileOutputStream::truncate()
|
||||
{
|
||||
if (fileHandle == 0)
|
||||
if (fileHandle == nullptr)
|
||||
return status;
|
||||
|
||||
flush();
|
||||
@ -607,10 +610,10 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc
|
||||
|
||||
if (fileHandle != -1)
|
||||
{
|
||||
void* m = mmap (0, (size_t) range.getLength(),
|
||||
mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
|
||||
exclusive ? MAP_PRIVATE : MAP_SHARED, fileHandle,
|
||||
(off_t) range.getStart());
|
||||
auto m = mmap (nullptr, (size_t) range.getLength(),
|
||||
mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
|
||||
exclusive ? MAP_PRIVATE : MAP_SHARED, fileHandle,
|
||||
(off_t) range.getStart());
|
||||
|
||||
if (m != MAP_FAILED)
|
||||
{
|
||||
@ -637,9 +640,6 @@ MemoryMappedFile::~MemoryMappedFile()
|
||||
File juce_getExecutableFile();
|
||||
File juce_getExecutableFile()
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
return File (android.appFile);
|
||||
#else
|
||||
struct DLAddrReader
|
||||
{
|
||||
static String getFilename()
|
||||
@ -654,7 +654,6 @@ File juce_getExecutableFile()
|
||||
|
||||
static String filename = DLAddrReader::getFilename();
|
||||
return File::getCurrentWorkingDirectory().getChildFile (filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -715,23 +714,7 @@ String File::getVolumeLabel() const
|
||||
|
||||
int File::getVolumeSerialNumber() const
|
||||
{
|
||||
int result = 0;
|
||||
/* int fd = open (getFullPathName().toUTF8(), O_RDONLY | O_NONBLOCK);
|
||||
|
||||
char info[512];
|
||||
|
||||
#ifndef HDIO_GET_IDENTITY
|
||||
#define HDIO_GET_IDENTITY 0x030d
|
||||
#endif
|
||||
|
||||
if (ioctl (fd, HDIO_GET_IDENTITY, info) == 0)
|
||||
{
|
||||
DBG (String (info + 20, 20));
|
||||
result = String (info + 20, 20).trim().getIntValue();
|
||||
}
|
||||
|
||||
close (fd);*/
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -907,21 +890,25 @@ extern JavaVM* androidJNIJavaVM;
|
||||
extern "C" void* threadEntryProc (void*);
|
||||
extern "C" void* threadEntryProc (void* userData)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
// JNI_OnLoad was not called - make sure you load the JUCE shared library
|
||||
// using System.load inside of Java
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
setEnv (env);
|
||||
#endif
|
||||
auto* myself = static_cast<Thread*> (userData);
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
juce_threadEntryPoint (userData);
|
||||
juce_threadEntryPoint (myself);
|
||||
}
|
||||
|
||||
#if JUCE_ANDROID
|
||||
if (androidJNIJavaVM != nullptr)
|
||||
{
|
||||
void* env = nullptr;
|
||||
androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
|
||||
|
||||
// only detach if we have actually been attached
|
||||
if (env != nullptr)
|
||||
androidJNIJavaVM->DetachCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -952,8 +939,8 @@ void Thread::launchThread()
|
||||
}
|
||||
#endif
|
||||
|
||||
threadHandle = 0;
|
||||
pthread_t handle = 0;
|
||||
threadHandle = {};
|
||||
pthread_t handle = {};
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_t* attrPtr = nullptr;
|
||||
|
||||
@ -963,6 +950,7 @@ void Thread::launchThread()
|
||||
pthread_attr_setstacksize (attrPtr, threadStackSize);
|
||||
}
|
||||
|
||||
|
||||
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
|
||||
{
|
||||
pthread_detach (handle);
|
||||
@ -976,13 +964,13 @@ void Thread::launchThread()
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
threadId = 0;
|
||||
threadHandle = 0;
|
||||
threadId = {};
|
||||
threadHandle = {};
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle.get() != 0)
|
||||
if (threadHandle.get() != nullptr)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
jassertfalse; // pthread_cancel not available!
|
||||
@ -1312,7 +1300,7 @@ struct HighResolutionTimer::Pimpl
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
if (thread == 0)
|
||||
if (thread == pthread_t())
|
||||
return;
|
||||
|
||||
if (thread == pthread_self())
|
||||
@ -1329,7 +1317,7 @@ struct HighResolutionTimer::Pimpl
|
||||
pthread_mutex_unlock (&timerMutex);
|
||||
|
||||
pthread_join (thread, nullptr);
|
||||
thread = 0;
|
||||
thread = {};
|
||||
}
|
||||
|
||||
HighResolutionTimer& owner;
|
||||
@ -1343,15 +1331,7 @@ private:
|
||||
|
||||
static void* timerThread (void* param)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
// JNI_OnLoad was not called - make sure you load the JUCE shared library
|
||||
// using System.load inside of Java
|
||||
jassert (androidJNIJavaVM != nullptr);
|
||||
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
setEnv (env);
|
||||
#else
|
||||
#if ! JUCE_ANDROID
|
||||
int dummy;
|
||||
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy);
|
||||
#endif
|
||||
|
@ -129,7 +129,7 @@ namespace WindowsFileHelpers
|
||||
|
||||
File getSpecialFolderPath (int type)
|
||||
{
|
||||
WCHAR path [MAX_PATH + 256];
|
||||
WCHAR path[MAX_PATH + 256];
|
||||
|
||||
if (SHGetSpecialFolderPath (0, path, type, FALSE))
|
||||
return File (String (path));
|
||||
@ -139,7 +139,7 @@ namespace WindowsFileHelpers
|
||||
|
||||
File getModuleFileName (HINSTANCE moduleHandle)
|
||||
{
|
||||
WCHAR dest [MAX_PATH + 256];
|
||||
WCHAR dest[MAX_PATH + 256];
|
||||
dest[0] = 0;
|
||||
GetModuleFileName (moduleHandle, dest, (DWORD) numElementsInArray (dest));
|
||||
return File (String (dest));
|
||||
@ -147,7 +147,7 @@ namespace WindowsFileHelpers
|
||||
|
||||
Result getResultForLastError()
|
||||
{
|
||||
TCHAR messageBuffer [256] = { 0 };
|
||||
TCHAR messageBuffer[256] = { 0 };
|
||||
|
||||
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
@ -181,7 +181,7 @@ bool File::existsAsFile() const
|
||||
|
||||
bool File::isDirectory() const
|
||||
{
|
||||
const DWORD attr = WindowsFileHelpers::getAtts (fullPath);
|
||||
auto attr = WindowsFileHelpers::getAtts (fullPath);
|
||||
return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 && attr != INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
@ -250,22 +250,22 @@ bool File::moveToTrash() const
|
||||
|
||||
bool File::copyInternal (const File& dest) const
|
||||
{
|
||||
return CopyFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer(), false) != 0;
|
||||
return CopyFile (fullPath.toWideCharPointer(),
|
||||
dest.getFullPathName().toWideCharPointer(), false) != 0;
|
||||
}
|
||||
|
||||
bool File::moveInternal (const File& dest) const
|
||||
{
|
||||
return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0;
|
||||
return MoveFile (fullPath.toWideCharPointer(),
|
||||
dest.getFullPathName().toWideCharPointer()) != 0;
|
||||
}
|
||||
|
||||
bool File::replaceInternal (const File& dest) const
|
||||
{
|
||||
void* lpExclude = 0;
|
||||
void* lpReserved = 0;
|
||||
|
||||
return ReplaceFile (dest.getFullPathName().toWideCharPointer(), fullPath.toWideCharPointer(),
|
||||
0, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS,
|
||||
lpExclude, lpReserved) != 0;
|
||||
return ReplaceFile (dest.getFullPathName().toWideCharPointer(),
|
||||
fullPath.toWideCharPointer(),
|
||||
0, REPLACEFILE_IGNORE_MERGE_ERRORS | 4 /*REPLACEFILE_IGNORE_ACL_ERRORS*/,
|
||||
nullptr, nullptr) != 0;
|
||||
}
|
||||
|
||||
Result File::createDirectoryInternal (const String& fileName) const
|
||||
@ -279,14 +279,16 @@ int64 juce_fileSetPosition (void* handle, int64 pos)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = pos;
|
||||
li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
|
||||
li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart,
|
||||
&li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
void FileInputStream::openHandle()
|
||||
{
|
||||
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
auto h = CreateFile (file.getFullPathName().toWideCharPointer(),
|
||||
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
fileHandle = (void*) h;
|
||||
@ -304,6 +306,7 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
|
||||
if (fileHandle != 0)
|
||||
{
|
||||
DWORD actualNum = 0;
|
||||
|
||||
if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0))
|
||||
status = WindowsFileHelpers::getResultForLastError();
|
||||
|
||||
@ -316,8 +319,9 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
|
||||
//==============================================================================
|
||||
void FileOutputStream::openHandle()
|
||||
{
|
||||
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
auto h = CreateFile (file.getFullPathName().toWideCharPointer(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -343,16 +347,13 @@ void FileOutputStream::closeHandle()
|
||||
|
||||
ssize_t FileOutputStream::writeInternal (const void* bufferToWrite, size_t numBytes)
|
||||
{
|
||||
DWORD actualNum = 0;
|
||||
|
||||
if (fileHandle != nullptr)
|
||||
{
|
||||
DWORD actualNum = 0;
|
||||
if (! WriteFile ((HANDLE) fileHandle, bufferToWrite, (DWORD) numBytes, &actualNum, 0))
|
||||
status = WindowsFileHelpers::getResultForLastError();
|
||||
|
||||
return (ssize_t) actualNum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (ssize_t) actualNum;
|
||||
}
|
||||
|
||||
void FileOutputStream::flushInternal()
|
||||
@ -396,15 +397,17 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc
|
||||
access = FILE_MAP_ALL_ACCESS;
|
||||
}
|
||||
|
||||
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode,
|
||||
exclusive ? 0 : (FILE_SHARE_READ | (mode == readWrite ? FILE_SHARE_WRITE : 0)), 0,
|
||||
createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
auto h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode,
|
||||
exclusive ? 0 : (FILE_SHARE_READ | FILE_SHARE_DELETE | (mode == readWrite ? FILE_SHARE_WRITE : 0)), 0,
|
||||
createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fileHandle = (void*) h;
|
||||
|
||||
HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0);
|
||||
auto mappingHandle = CreateFileMapping (h, 0, protect,
|
||||
(DWORD) (range.getEnd() >> 32),
|
||||
(DWORD) range.getEnd(), 0);
|
||||
|
||||
if (mappingHandle != 0)
|
||||
{
|
||||
@ -461,8 +464,9 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
|
||||
using namespace WindowsFileHelpers;
|
||||
|
||||
bool ok = false;
|
||||
HANDLE h = CreateFile (fullPath.toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
auto h = CreateFile (fullPath.toWideCharPointer(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -482,7 +486,7 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
|
||||
//==============================================================================
|
||||
void File::findFileSystemRoots (Array<File>& destArray)
|
||||
{
|
||||
TCHAR buffer [2048] = { 0 };
|
||||
TCHAR buffer[2048] = { 0 };
|
||||
GetLogicalDriveStrings (2048, buffer);
|
||||
|
||||
const TCHAR* n = buffer;
|
||||
@ -499,13 +503,14 @@ void File::findFileSystemRoots (Array<File>& destArray)
|
||||
roots.sort (true);
|
||||
|
||||
for (int i = 0; i < roots.size(); ++i)
|
||||
destArray.add (roots [i]);
|
||||
destArray.add (roots[i]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String File::getVolumeLabel() const
|
||||
{
|
||||
TCHAR dest[64];
|
||||
|
||||
if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
|
||||
(DWORD) numElementsInArray (dest), 0, 0, 0, 0, 0))
|
||||
dest[0] = 0;
|
||||
@ -539,9 +544,9 @@ uint64 File::getFileIdentifier() const
|
||||
{
|
||||
uint64 result = 0;
|
||||
|
||||
HANDLE h = CreateFile (getFullPathName().toWideCharPointer(),
|
||||
GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
auto h = CreateFile (getFullPathName().toWideCharPointer(),
|
||||
GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -568,7 +573,7 @@ bool File::isOnHardDisk() const
|
||||
if (fullPath.isEmpty())
|
||||
return false;
|
||||
|
||||
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
||||
auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
||||
|
||||
if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':')
|
||||
return n != DRIVE_REMOVABLE;
|
||||
@ -583,7 +588,7 @@ bool File::isOnRemovableDrive() const
|
||||
if (fullPath.isEmpty())
|
||||
return false;
|
||||
|
||||
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
||||
auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
|
||||
|
||||
return n == DRIVE_CDROM
|
||||
|| n == DRIVE_REMOTE
|
||||
@ -612,7 +617,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
WCHAR dest [2048];
|
||||
WCHAR dest[2048];
|
||||
dest[0] = 0;
|
||||
GetTempPath ((DWORD) numElementsInArray (dest), dest);
|
||||
return File (String (dest));
|
||||
@ -620,7 +625,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
|
||||
|
||||
case windowsSystemDirectory:
|
||||
{
|
||||
WCHAR dest [2048];
|
||||
WCHAR dest[2048];
|
||||
dest[0] = 0;
|
||||
GetSystemDirectoryW (dest, (UINT) numElementsInArray (dest));
|
||||
return File (String (dest));
|
||||
@ -645,7 +650,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
|
||||
//==============================================================================
|
||||
File File::getCurrentWorkingDirectory()
|
||||
{
|
||||
WCHAR dest [MAX_PATH + 256];
|
||||
WCHAR dest[MAX_PATH + 256];
|
||||
dest[0] = 0;
|
||||
GetCurrentDirectory ((DWORD) numElementsInArray (dest), dest);
|
||||
return File (String (dest));
|
||||
@ -710,7 +715,7 @@ static String readWindowsLnkFile (File lnkFile, bool wantsAbsolutePath)
|
||||
&& (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))))
|
||||
{
|
||||
WIN32_FIND_DATA winFindData;
|
||||
WCHAR resolvedPath [MAX_PATH];
|
||||
WCHAR resolvedPath[MAX_PATH];
|
||||
|
||||
DWORD flags = SLGP_UNCPRIORITY;
|
||||
|
||||
@ -1175,7 +1180,15 @@ bool NamedPipe::openInternal (const String& pipeName, const bool createPipe, boo
|
||||
{
|
||||
pimpl.reset (new Pimpl (pipeName, createPipe, mustNotExist));
|
||||
|
||||
if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE)
|
||||
if (createPipe)
|
||||
{
|
||||
if (pimpl->pipeH == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
pimpl.reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (! pimpl->connect (200))
|
||||
{
|
||||
pimpl.reset();
|
||||
return false;
|
||||
|
@ -343,6 +343,35 @@ private:
|
||||
InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs));
|
||||
}
|
||||
|
||||
void sendHTTPRequest (INTERNET_BUFFERS& buffers, WebInputStream::Listener* listener)
|
||||
{
|
||||
if (! HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
|
||||
return;
|
||||
|
||||
int totalBytesSent = 0;
|
||||
|
||||
while (totalBytesSent < (int) postData.getSize())
|
||||
{
|
||||
auto bytesToSend = jmin (1024, (int) postData.getSize() - totalBytesSent);
|
||||
DWORD bytesSent = 0;
|
||||
|
||||
if (bytesToSend == 0
|
||||
|| ! InternetWriteFile (request, static_cast<const char*> (postData.getData()) + totalBytesSent,
|
||||
(DWORD) bytesToSend, &bytesSent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
totalBytesSent += bytesSent;
|
||||
|
||||
if (listener != nullptr
|
||||
&& ! listener->postDataSendProgress (owner, totalBytesSent, (int) postData.getSize()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openHTTPConnection (URL_COMPONENTS& uc, const String& address, WebInputStream::Listener* listener)
|
||||
{
|
||||
const TCHAR* mimeTypes[] = { _T("*/*"), nullptr };
|
||||
@ -365,43 +394,29 @@ private:
|
||||
if (request != 0)
|
||||
{
|
||||
INTERNET_BUFFERS buffers = { 0 };
|
||||
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
|
||||
buffers.lpcszHeader = headers.toWideCharPointer();
|
||||
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
|
||||
buffers.lpcszHeader = headers.toWideCharPointer();
|
||||
buffers.dwHeadersLength = (DWORD) headers.length();
|
||||
buffers.dwBufferTotal = (DWORD) postData.getSize();
|
||||
buffers.dwBufferTotal = (DWORD) postData.getSize();
|
||||
|
||||
if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
|
||||
auto sendRequestAndTryEnd = [this, &buffers, &listener]() -> bool
|
||||
{
|
||||
int bytesSent = 0;
|
||||
sendHTTPRequest (buffers, listener);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent);
|
||||
DWORD bytesDone = 0;
|
||||
if (HttpEndRequest (request, 0, 0, 0))
|
||||
return true;
|
||||
|
||||
if (bytesToDo > 0
|
||||
&& ! InternetWriteFile (request,
|
||||
static_cast<const char*> (postData.getData()) + bytesSent,
|
||||
(DWORD) bytesToDo, &bytesDone))
|
||||
{
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (bytesToDo == 0 || (int) bytesDone < bytesToDo)
|
||||
{
|
||||
if (HttpEndRequest (request, 0, 0, 0))
|
||||
return;
|
||||
auto closed = sendRequestAndTryEnd();
|
||||
|
||||
break;
|
||||
}
|
||||
// N.B. this is needed for some authenticated HTTP connections
|
||||
if (! closed && GetLastError() == ERROR_INTERNET_FORCE_RETRY)
|
||||
closed = sendRequestAndTryEnd();
|
||||
|
||||
bytesSent += bytesDone;
|
||||
|
||||
if (listener != nullptr
|
||||
&& ! listener->postDataSendProgress (owner, bytesSent, (int) postData.getSize()))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (closed)
|
||||
return;
|
||||
}
|
||||
|
||||
closeConnection();
|
||||
@ -514,6 +529,37 @@ namespace MACAddressHelpers
|
||||
split[1] = sa_in6->sin6_addr.u.Byte[off];
|
||||
#endif
|
||||
}
|
||||
|
||||
static IPAddress createAddress (const sockaddr_in6* sa_in6)
|
||||
{
|
||||
IPAddressByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
split (sa_in6, i * 2, temp.split);
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
return IPAddress (arr);
|
||||
}
|
||||
|
||||
static IPAddress createAddress (const sockaddr_in* sa_in)
|
||||
{
|
||||
return IPAddress ((uint8*) &sa_in->sin_addr.s_addr, false);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static void findAddresses (Array<IPAddress>& result, bool includeIPv6, Type start)
|
||||
{
|
||||
for (auto addr = start; addr != nullptr; addr = addr->Next)
|
||||
{
|
||||
if (addr->Address.lpSockaddr->sa_family == AF_INET)
|
||||
result.addIfNotAlreadyThere (createAddress ((sockaddr_in*) addr->Address.lpSockaddr));
|
||||
else if (addr->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
|
||||
result.addIfNotAlreadyThere (createAddress ((sockaddr_in6*) addr->Address.lpSockaddr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
@ -530,94 +576,25 @@ void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
|
||||
result.addIfNotAlreadyThere (IPAddress::local (true));
|
||||
|
||||
GetAdaptersAddressesHelper addressesHelper;
|
||||
|
||||
if (addressesHelper.callGetAdaptersAddresses())
|
||||
{
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next)
|
||||
{
|
||||
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
|
||||
for (pUnicast = adapter->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next)
|
||||
{
|
||||
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)
|
||||
{
|
||||
const sockaddr_in* sa_in = (sockaddr_in*)pUnicast->Address.lpSockaddr;
|
||||
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
|
||||
{
|
||||
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pUnicast->Address.lpSockaddr;
|
||||
|
||||
ByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
IPAddress ip (arr);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
}
|
||||
|
||||
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = nullptr;
|
||||
for (pAnycast = adapter->FirstAnycastAddress; pAnycast != nullptr; pAnycast = pAnycast->Next)
|
||||
{
|
||||
if (pAnycast->Address.lpSockaddr->sa_family == AF_INET)
|
||||
{
|
||||
const sockaddr_in* sa_in = (sockaddr_in*)pAnycast->Address.lpSockaddr;
|
||||
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
else if (pAnycast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
|
||||
{
|
||||
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pAnycast->Address.lpSockaddr;
|
||||
|
||||
ByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
IPAddress ip (arr);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
}
|
||||
|
||||
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = nullptr;
|
||||
for (pMulticast = adapter->FirstMulticastAddress; pMulticast != nullptr; pMulticast = pMulticast->Next)
|
||||
{
|
||||
if (pMulticast->Address.lpSockaddr->sa_family == AF_INET)
|
||||
{
|
||||
const sockaddr_in* sa_in = (sockaddr_in*)pMulticast->Address.lpSockaddr;
|
||||
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
else if (pMulticast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
|
||||
{
|
||||
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pMulticast->Address.lpSockaddr;
|
||||
|
||||
ByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
IPAddress ip (arr);
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
}
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstUnicastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstAnycastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstMulticastAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress&)
|
||||
{
|
||||
// TODO
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
|
@ -151,7 +151,17 @@ void CPUInformation::initialise() noexcept
|
||||
|
||||
callCPUID (info, 7);
|
||||
|
||||
hasAVX2 = (info[1] & (1 << 5)) != 0;
|
||||
hasAVX2 = (info[1] & (1 << 5)) != 0;
|
||||
hasAVX512F = (info[1] & (1u << 16)) != 0;
|
||||
hasAVX512DQ = (info[1] & (1u << 17)) != 0;
|
||||
hasAVX512IFMA = (info[1] & (1u << 21)) != 0;
|
||||
hasAVX512PF = (info[1] & (1u << 26)) != 0;
|
||||
hasAVX512ER = (info[1] & (1u << 27)) != 0;
|
||||
hasAVX512CD = (info[1] & (1u << 28)) != 0;
|
||||
hasAVX512BW = (info[1] & (1u << 30)) != 0;
|
||||
hasAVX512VL = (info[1] & (1u << 31)) != 0;
|
||||
hasAVX512VBMI = (info[2] & (1u << 1)) != 0;
|
||||
hasAVX512VPOPCNTDQ = (info[2] & (1u << 14)) != 0;
|
||||
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetNativeSystemInfo (&systemInfo);
|
||||
@ -288,8 +298,9 @@ int SystemStats::getMemorySizeInMegabytes()
|
||||
//==============================================================================
|
||||
String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
|
||||
{
|
||||
DWORD len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0);
|
||||
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
|
||||
auto len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0);
|
||||
|
||||
if (len == 0)
|
||||
return String (defaultValue);
|
||||
|
||||
HeapBlock<WCHAR> buffer (len);
|
||||
@ -325,7 +336,7 @@ public:
|
||||
#endif
|
||||
|
||||
#if JUCE_WIN32_TIMER_PERIOD > 0
|
||||
const MMRESULT res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD);
|
||||
auto res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD);
|
||||
ignoreUnused (res);
|
||||
jassert (res == TIMERR_NOERROR);
|
||||
#endif
|
||||
@ -386,10 +397,10 @@ static int64 juce_getClockCycleCounter() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegaherz()
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
const int64 cycles = juce_getClockCycleCounter();
|
||||
const uint32 millis = Time::getMillisecondCounter();
|
||||
auto cycles = juce_getClockCycleCounter();
|
||||
auto millis = Time::getMillisecondCounter();
|
||||
int lastResult = 0;
|
||||
|
||||
for (;;)
|
||||
@ -397,12 +408,12 @@ int SystemStats::getCpuSpeedInMegaherz()
|
||||
int n = 1000000;
|
||||
while (--n > 0) {}
|
||||
|
||||
const uint32 millisElapsed = Time::getMillisecondCounter() - millis;
|
||||
const int64 cyclesNow = juce_getClockCycleCounter();
|
||||
auto millisElapsed = Time::getMillisecondCounter() - millis;
|
||||
auto cyclesNow = juce_getClockCycleCounter();
|
||||
|
||||
if (millisElapsed > 80)
|
||||
{
|
||||
const int newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000);
|
||||
auto newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000);
|
||||
|
||||
if (millisElapsed > 500 || (lastResult == newResult && newResult > 100))
|
||||
return newResult;
|
||||
@ -431,7 +442,7 @@ bool Time::setSystemTimeToThisTime() const
|
||||
// first one sets it up, the second one kicks it in.
|
||||
// NB: the local variable is here to avoid analysers warning about having
|
||||
// two identical sub-expressions in the return statement
|
||||
bool firstCallToSetTimezone = SetLocalTime (&st) != 0;
|
||||
auto firstCallToSetTimezone = SetLocalTime (&st) != 0;
|
||||
return firstCallToSetTimezone && SetLocalTime (&st) != 0;
|
||||
}
|
||||
|
||||
@ -447,7 +458,7 @@ int SystemStats::getPageSize()
|
||||
String SystemStats::getLogonName()
|
||||
{
|
||||
TCHAR text [256] = { 0 };
|
||||
DWORD len = (DWORD) numElementsInArray (text) - 1;
|
||||
auto len = (DWORD) numElementsInArray (text) - 1;
|
||||
GetUserName (text, &len);
|
||||
return String (text, len);
|
||||
}
|
||||
@ -460,8 +471,8 @@ String SystemStats::getFullUserName()
|
||||
String SystemStats::getComputerName()
|
||||
{
|
||||
TCHAR text[128] = { 0 };
|
||||
DWORD len = (DWORD) numElementsInArray (text) - 1;
|
||||
GetComputerName (text, &len);
|
||||
auto len = (DWORD) numElementsInArray (text) - 1;
|
||||
GetComputerNameEx (ComputerNamePhysicalDnsHostname, text, &len);
|
||||
return String (text, len);
|
||||
}
|
||||
|
||||
@ -485,10 +496,10 @@ String SystemStats::getDisplayLanguage()
|
||||
if (getUserDefaultUILanguage == nullptr)
|
||||
return "en";
|
||||
|
||||
const DWORD langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT);
|
||||
auto langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT);
|
||||
|
||||
String mainLang (getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en"));
|
||||
String region (getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr));
|
||||
auto mainLang = getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en");
|
||||
auto region = getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr);
|
||||
|
||||
if (region.isNotEmpty())
|
||||
mainLang << '-' << region;
|
||||
|
Reference in New Issue
Block a user