From f1dc2a71f38b9940f6a6be980d28638bd9156a85 Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 21 Jun 2017 09:51:16 +0100 Subject: [PATCH] Android: Fixed a compiler error when compiling for Android SDKs < 16 (and re-saved all projects) --- .../com/yourcompany/jucedemo/JuceDemo.java | 167 +++++++++++++++--- .../com/yourcompany/miditest/MidiTest.java | 167 +++++++++++++++--- .../JUCENetworkGraphicsDemo.java | 167 +++++++++++++++--- .../yourcompany/oscreceiver/OSCReceiver.java | 167 +++++++++++++++--- .../com/yourcompany/oscsender/OSCSender.java | 167 +++++++++++++++--- .../juce/jucedemoplugin/JuceDemoPlugin.java | 167 +++++++++++++++--- .../AudioPerformanceTest.java | 167 +++++++++++++++--- .../native/java/JuceAppActivity.java | 3 +- 8 files changed, 1017 insertions(+), 155 deletions(-) diff --git a/examples/Demo/Builds/Android/app/src/main/java/com/yourcompany/jucedemo/JuceDemo.java b/examples/Demo/Builds/Android/app/src/main/java/com/yourcompany/jucedemo/JuceDemo.java index b32914171b..5580d26015 100644 --- a/examples/Demo/Builds/Android/app/src/main/java/com/yourcompany/jucedemo/JuceDemo.java +++ b/examples/Demo/Builds/Android/app/src/main/java/com/yourcompany/jucedemo/JuceDemo.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; @@ -106,7 +116,8 @@ public class JuceDemo extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -703,6 +714,7 @@ public class JuceDemo extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -730,8 +742,14 @@ public class JuceDemo extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -951,43 +969,136 @@ public class JuceDemo extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -998,7 +1109,11 @@ public class JuceDemo extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1015,8 +1130,16 @@ public class JuceDemo extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/examples/MidiTest/Builds/Android/app/src/main/java/com/yourcompany/miditest/MidiTest.java b/examples/MidiTest/Builds/Android/app/src/main/java/com/yourcompany/miditest/MidiTest.java index 303065d5e7..2c72cc52f3 100644 --- a/examples/MidiTest/Builds/Android/app/src/main/java/com/yourcompany/miditest/MidiTest.java +++ b/examples/MidiTest/Builds/Android/app/src/main/java/com/yourcompany/miditest/MidiTest.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; import android.media.midi.*; import android.bluetooth.*; @@ -109,7 +119,8 @@ public class MidiTest extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -1632,6 +1643,7 @@ public class MidiTest extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -1659,8 +1671,14 @@ public class MidiTest extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -1880,43 +1898,136 @@ public class MidiTest extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -1927,7 +2038,11 @@ public class MidiTest extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1944,8 +2059,16 @@ public class MidiTest extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/examples/NetworkGraphicsDemo/Builds/Android/app/src/main/java/com/juce/networkgraphicsdemo/JUCENetworkGraphicsDemo.java b/examples/NetworkGraphicsDemo/Builds/Android/app/src/main/java/com/juce/networkgraphicsdemo/JUCENetworkGraphicsDemo.java index 6ee2fd1db2..c955cd3cdf 100644 --- a/examples/NetworkGraphicsDemo/Builds/Android/app/src/main/java/com/juce/networkgraphicsdemo/JUCENetworkGraphicsDemo.java +++ b/examples/NetworkGraphicsDemo/Builds/Android/app/src/main/java/com/juce/networkgraphicsdemo/JUCENetworkGraphicsDemo.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; @@ -106,7 +116,8 @@ public class JUCENetworkGraphicsDemo extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -703,6 +714,7 @@ public class JUCENetworkGraphicsDemo extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -730,8 +742,14 @@ public class JUCENetworkGraphicsDemo extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -951,43 +969,136 @@ public class JUCENetworkGraphicsDemo extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -998,7 +1109,11 @@ public class JUCENetworkGraphicsDemo extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1015,8 +1130,16 @@ public class JUCENetworkGraphicsDemo extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/examples/OSCReceiver/Builds/Android/app/src/main/java/com/yourcompany/oscreceiver/OSCReceiver.java b/examples/OSCReceiver/Builds/Android/app/src/main/java/com/yourcompany/oscreceiver/OSCReceiver.java index 602ac2bc26..6999b9cf67 100644 --- a/examples/OSCReceiver/Builds/Android/app/src/main/java/com/yourcompany/oscreceiver/OSCReceiver.java +++ b/examples/OSCReceiver/Builds/Android/app/src/main/java/com/yourcompany/oscreceiver/OSCReceiver.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; @@ -106,7 +116,8 @@ public class OSCReceiver extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -703,6 +714,7 @@ public class OSCReceiver extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -730,8 +742,14 @@ public class OSCReceiver extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -951,43 +969,136 @@ public class OSCReceiver extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -998,7 +1109,11 @@ public class OSCReceiver extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1015,8 +1130,16 @@ public class OSCReceiver extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/examples/OSCSender/Builds/Android/app/src/main/java/com/yourcompany/oscsender/OSCSender.java b/examples/OSCSender/Builds/Android/app/src/main/java/com/yourcompany/oscsender/OSCSender.java index c4a979bfc6..bc2f475872 100644 --- a/examples/OSCSender/Builds/Android/app/src/main/java/com/yourcompany/oscsender/OSCSender.java +++ b/examples/OSCSender/Builds/Android/app/src/main/java/com/yourcompany/oscsender/OSCSender.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; @@ -106,7 +116,8 @@ public class OSCSender extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -703,6 +714,7 @@ public class OSCSender extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -730,8 +742,14 @@ public class OSCSender extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -951,43 +969,136 @@ public class OSCSender extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -998,7 +1109,11 @@ public class OSCSender extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1015,8 +1130,16 @@ public class OSCSender extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/examples/audio plugin demo/Builds/Android/app/src/main/java/com/juce/jucedemoplugin/JuceDemoPlugin.java b/examples/audio plugin demo/Builds/Android/app/src/main/java/com/juce/jucedemoplugin/JuceDemoPlugin.java index 989aafcfb2..771774e919 100644 --- a/examples/audio plugin demo/Builds/Android/app/src/main/java/com/juce/jucedemoplugin/JuceDemoPlugin.java +++ b/examples/audio plugin demo/Builds/Android/app/src/main/java/com/juce/jucedemoplugin/JuceDemoPlugin.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; import android.media.midi.*; import android.bluetooth.*; @@ -109,7 +119,8 @@ public class JuceDemoPlugin extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -1632,6 +1643,7 @@ public class JuceDemoPlugin extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -1659,8 +1671,14 @@ public class JuceDemoPlugin extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -1880,43 +1898,136 @@ public class JuceDemoPlugin extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -1927,7 +2038,11 @@ public class JuceDemoPlugin extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1944,8 +2059,16 @@ public class JuceDemoPlugin extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/extras/AudioPerformanceTest/Builds/Android/app/src/main/java/com/juce/audioperformancetest/AudioPerformanceTest.java b/extras/AudioPerformanceTest/Builds/Android/app/src/main/java/com/juce/audioperformancetest/AudioPerformanceTest.java index 243d395081..80c95d6473 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/src/main/java/com/juce/audioperformancetest/AudioPerformanceTest.java +++ b/extras/AudioPerformanceTest/Builds/Android/app/src/main/java/com/juce/audioperformancetest/AudioPerformanceTest.java @@ -58,6 +58,16 @@ import android.media.AudioManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.Manifest; +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.TimeUnit; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.*; import android.media.midi.*; import android.bluetooth.*; @@ -109,7 +119,8 @@ public class AudioPerformanceTest extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; } @@ -1632,6 +1643,7 @@ public class AudioPerformanceTest extends Activity //============================================================================== private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar); + private native void handleBackButton (long host); public void showKeyboard (String type) { @@ -1659,8 +1671,14 @@ public class AudioPerformanceTest extends Activity case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown (keyCode, event); + case KeyEvent.KEYCODE_BACK: + { + handleBackButton (host); + return true; + } - default: break; + default: + break; } handleKeyDown (host, keyCode, event.getUnicodeChar()); @@ -1880,43 +1898,136 @@ public class AudioPerformanceTest extends Activity public static class HTTPStream { public HTTPStream (HttpURLConnection connection_, - int[] statusCode, StringBuffer responseHeaders) throws IOException + int[] statusCode_, + StringBuffer responseHeaders_) { connection = connection_; + statusCode = statusCode_; + responseHeaders = responseHeaders_; + } + + private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException + { + synchronized (createFutureLock) + { + if (hasBeenCancelled.get()) + return null; + + streamFuture = executor.submit (new Callable() + { + @Override + public BufferedInputStream call() throws IOException + { + return new BufferedInputStream (isInput ? connection.getInputStream() + : connection.getErrorStream()); + } + }); + } try { - inputStream = new BufferedInputStream (connection.getInputStream()); + if (connection.getConnectTimeout() > 0) + return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); + else + return streamFuture.get(); + } + catch (InterruptedException e) + { + return null; + } + catch (TimeoutException e) + { + return null; + } + catch (CancellationException e) + { + return null; + } + } + + public final boolean connect() + { + try + { + try + { + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + inputStream = getCancellableStream (true); + } + } + catch (ExecutionException e) + { + if (connection.getResponseCode() < 400) + { + statusCode[0] = connection.getResponseCode(); + connection.disconnect(); + return false; + } + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + try + { + if (statusCode[0] >= 400) + inputStream = getCancellableStream (false); + else + inputStream = getCancellableStream (true); + } + catch (ExecutionException e) + {} + } + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); + + return true; } catch (IOException e) { - if (connection.getResponseCode() < 400) - throw e; + return false; } - finally - { - statusCode[0] = connection.getResponseCode(); - } - - if (statusCode[0] >= 400) - inputStream = connection.getErrorStream(); - else - inputStream = connection.getInputStream(); - - for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) - if (entry.getKey() != null && entry.getValue() != null) - responseHeaders.append (entry.getKey() + ": " - + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() { + hasBeenCancelled.set (true); + try { - inputStream.close(); + if (! createStreamLock.tryLock()) + { + synchronized (createFutureLock) + { + if (streamFuture != null) + streamFuture.cancel (true); + } + + createStreamLock.lock(); + } + + if (inputStream != null) + inputStream.close(); } catch (IOException e) {} + finally + { + createStreamLock.unlock(); + } connection.disconnect(); } @@ -1927,7 +2038,11 @@ public class AudioPerformanceTest extends Activity try { - num = inputStream.read (buffer, 0, numBytes); + synchronized (createStreamLock) + { + if (inputStream != null) + num = inputStream.read (buffer, 0, numBytes); + } } catch (IOException e) {} @@ -1944,8 +2059,16 @@ public class AudioPerformanceTest extends Activity public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; + private int[] statusCode; + private StringBuffer responseHeaders; 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 streamFuture; } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, diff --git a/modules/juce_core/native/java/JuceAppActivity.java b/modules/juce_core/native/java/JuceAppActivity.java index 5253c30ea2..aad23e8c91 100644 --- a/modules/juce_core/native/java/JuceAppActivity.java +++ b/modules/juce_core/native/java/JuceAppActivity.java @@ -116,7 +116,8 @@ public class JuceAppActivity extends Activity { case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO; case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION; - case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return Manifest.permission.READ_EXTERNAL_STORAGE; + // use string value as this is not defined in SDKs < 16 + case JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE: return "android.permission.READ_EXTERNAL_STORAGE"; case JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE: return Manifest.permission.WRITE_EXTERNAL_STORAGE; }