From 1b7d30f0f492d7a8dffd394fe9ec74942e7ccb72 Mon Sep 17 00:00:00 2001 From: hogliux Date: Fri, 4 Aug 2017 18:49:14 +0100 Subject: [PATCH] Android: Moved more Java code into C++ --- .../com/yourcompany/jucedemo/JuceDemo.java | 81 ++++++------- .../com/yourcompany/miditest/MidiTest.java | 81 ++++++------- .../JUCENetworkGraphicsDemo.java | 81 ++++++------- .../yourcompany/oscreceiver/OSCReceiver.java | 81 ++++++------- .../com/yourcompany/oscsender/OSCSender.java | 81 ++++++------- .../juce/jucedemoplugin/JuceDemoPlugin.java | 81 ++++++------- .../AudioPerformanceTest.java | 81 ++++++------- .../native/java/JuceAppActivity.java | 81 ++++++------- .../juce_core/native/juce_android_Files.cpp | 86 ++++++++++++++ .../native/juce_android_JNIHelpers.h | 107 ++++++++++++++++-- .../native/juce_android_SystemStats.cpp | 93 +++++++++++++++ .../juce_core/native/juce_posix_SharedCode.h | 18 +-- .../native/juce_android_Messaging.cpp | 102 +++++++++++++---- .../marketplace/juce_OnlineUnlockStatus.cpp | 2 +- 14 files changed, 623 insertions(+), 433 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 5580d26015..983bf3e60f 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -385,23 +383,6 @@ public class JuceDemo extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -965,6 +946,38 @@ public class JuceDemo extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -1268,36 +1281,6 @@ public class JuceDemo extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 2c72cc52f3..04e2242e81 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -1314,23 +1312,6 @@ public class MidiTest extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -1894,6 +1875,38 @@ public class MidiTest extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -2197,36 +2210,6 @@ public class MidiTest extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 c955cd3cdf..a7116fb2b5 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -385,23 +383,6 @@ public class JUCENetworkGraphicsDemo extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -965,6 +946,38 @@ public class JUCENetworkGraphicsDemo extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -1268,36 +1281,6 @@ public class JUCENetworkGraphicsDemo extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 6999b9cf67..f76b5daf25 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -385,23 +383,6 @@ public class OSCReceiver extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -965,6 +946,38 @@ public class OSCReceiver extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -1268,36 +1281,6 @@ public class OSCReceiver extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 bc2f475872..6cc619575f 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -385,23 +383,6 @@ public class OSCSender extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -965,6 +946,38 @@ public class OSCSender extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -1268,36 +1281,6 @@ public class OSCSender extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 771774e919..15f8b5db71 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -1314,23 +1312,6 @@ public class JuceDemoPlugin extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -1894,6 +1875,38 @@ public class JuceDemoPlugin extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -2197,36 +2210,6 @@ public class JuceDemoPlugin extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try 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 80c95d6473..4ebb4c124b 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 @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -1314,23 +1312,6 @@ public class AudioPerformanceTest extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -1894,6 +1875,38 @@ public class AudioPerformanceTest extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -2197,36 +2210,6 @@ public class AudioPerformanceTest extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try diff --git a/modules/juce_core/native/java/JuceAppActivity.java b/modules/juce_core/native/java/JuceAppActivity.java index aad23e8c91..1b806d9198 100644 --- a/modules/juce_core/native/java/JuceAppActivity.java +++ b/modules/juce_core/native/java/JuceAppActivity.java @@ -55,8 +55,6 @@ import java.io.*; import java.net.URL; import java.net.HttpURLConnection; 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; @@ -302,23 +300,6 @@ public class JuceAppActivity extends Activity private native void resumeApp(); private native void setScreenSize (int screenWidth, int screenHeight, int dpi); - //============================================================================== - public native void deliverMessage (long value); - private android.os.Handler messageHandler = new android.os.Handler(); - - public final void postMessage (long value) - { - messageHandler.post (new MessageCallback (value)); - } - - private final class MessageCallback implements Runnable - { - public MessageCallback (long value_) { value = value_; } - public final void run() { deliverMessage (value); } - - private long value; - } - //============================================================================== private ViewHolder viewHolder; private MidiDeviceManager midiDeviceManager = null; @@ -882,6 +863,38 @@ public class JuceAppActivity extends Activity private int[] cachedRenderArray = new int [256]; + //============================================================================== + public static class NativeInvocationHandler implements InvocationHandler + { + public NativeInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + @Override + public void finalize() + { + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + return dispatchInvoke (nativeContext, proxy, method, args); + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); + } + + public static InvocationHandler createInvocationHandler (long nativeContextRef) + { + return new NativeInvocationHandler (nativeContextRef); + } + //============================================================================== public static class HTTPStream { @@ -1185,36 +1198,6 @@ public class JuceAppActivity extends Activity public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); } //============================================================================== - private final class SingleMediaScanner implements MediaScannerConnectionClient - { - public SingleMediaScanner (Context context, String filename) - { - file = filename; - msc = new MediaScannerConnection (context, this); - msc.connect(); - } - - @Override - public void onMediaScannerConnected() - { - msc.scanFile (file, null); - } - - @Override - public void onScanCompleted (String path, Uri uri) - { - msc.disconnect(); - } - - private MediaScannerConnection msc; - private String file; - } - - public final void scanFile (String filename) - { - new SingleMediaScanner (this, filename); - } - public final Typeface getTypeFaceFromAsset (String assetName) { try diff --git a/modules/juce_core/native/juce_android_Files.cpp b/modules/juce_core/native/juce_android_Files.cpp index a6afc4a093..2b8f7ea998 100644 --- a/modules/juce_core/native/juce_android_Files.cpp +++ b/modules/juce_core/native/juce_android_Files.cpp @@ -20,6 +20,46 @@ ============================================================================== */ +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (constructor, "", "(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"); +#undef JNI_CLASS_MEMBERS + +//============================================================================== +class MediaScannerConnectionClient : public AndroidInterfaceImplementer +{ +public: + virtual void onMediaScannerConnected() = 0; + virtual void onScanCompleted() = 0; + +private: + jobject invoke (jobject proxy, jobject method, jobjectArray args) override + { + auto* env = getEnv(); + + auto methodName = juceString ((jstring) env->CallObjectMethod (method, Method.getName)); + + if (methodName == "onMediaScannerConnected") + { + onMediaScannerConnected(); + return nullptr; + } + else if (methodName == "onScanCompleted") + { + onScanCompleted(); + return nullptr; + } + + return AndroidInterfaceImplementer::invoke (proxy, method, args); + } +}; + +//============================================================================== bool File::isOnCDRomDrive() const { return false; @@ -100,3 +140,49 @@ JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const void File::revealToUser() const { } + +//============================================================================== +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())), + file (filename) + { + getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect); + } + + void onMediaScannerConnected() override + { + auto* env = getEnv(); + + env->CallVoidMethod (msc.get(), MediaScannerConnection.scanFile, javaString (file).get(), 0); + } + + void onScanCompleted() override + { + getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.disconnect); + } + +private: + GlobalRef msc; + String file; +}; + +void FileOutputStream::flushInternal() +{ + if (fileHandle != 0) + { + if (fsync (getFD (fileHandle)) == -1) + status = getResultForErrno(); + + // This stuff tells the OS to asynchronously update the metadata + // that the OS has cached aboud the file - this metadata is used + // when the device is acting as a USB drive, and unless it's explicitly + // refreshed, it'll get out of step with the real file. + new SingleMediaScanner (file.getFullPathName()); + } +} diff --git a/modules/juce_core/native/juce_android_JNIHelpers.h b/modules/juce_core/native/juce_android_JNIHelpers.h index a7742dcd7d..676353cfb5 100644 --- a/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/modules/juce_core/native/juce_android_JNIHelpers.h @@ -40,10 +40,11 @@ extern JNIEnv* attachAndroidJNI() noexcept; class GlobalRef { public: - inline GlobalRef() noexcept : obj (0) {} - inline explicit GlobalRef (jobject o) : obj (retain (o)) {} - inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {} - ~GlobalRef() { clear(); } + inline GlobalRef() noexcept : obj (0) {} + inline explicit GlobalRef (jobject o) : obj (retain (o)) {} + inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {} + inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); } + ~GlobalRef() { clear(); } inline void clear() { @@ -62,6 +63,14 @@ public: return *this; } + inline GlobalRef& operator= (GlobalRef&& other) + { + clear(); + std::swap (obj, other.obj); + + return *this; + } + //============================================================================== inline operator jobject() const noexcept { return obj; } inline jobject get() const noexcept { return obj; } @@ -98,7 +107,7 @@ public: private: //============================================================================== - jobject obj; + jobject obj = 0; static inline jobject retain (jobject obj) { @@ -111,14 +120,19 @@ template class LocalRef { public: + explicit inline LocalRef () noexcept : obj (0) {} explicit inline LocalRef (JavaType o) noexcept : obj (o) {} inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} + inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); } ~LocalRef() { clear(); } void clear() { if (obj != 0) + { getEnv()->DeleteLocalRef (obj); + obj = 0; + } } LocalRef& operator= (const LocalRef& other) @@ -129,6 +143,13 @@ public: return *this; } + LocalRef& operator= (LocalRef&& other) + { + clear(); + std::swap (other.obj, obj); + return *this; + } + inline operator JavaType() const noexcept { return obj; } inline JavaType get() const noexcept { return obj; } @@ -261,7 +282,6 @@ extern AndroidSystem android; METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \ - METHOD (postMessage, "postMessage", "(J)V") \ METHOD (finish, "finish", "()V") \ METHOD (setRequestedOrientation,"setRequestedOrientation", "(I)V") \ METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ @@ -279,7 +299,6 @@ extern AndroidSystem android; STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \ STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \ STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \ - METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") \ METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") \ METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \ @@ -293,6 +312,7 @@ extern AndroidSystem android; METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ + STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); #undef JNI_CLASS_MEMBERS @@ -332,3 +352,76 @@ DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix"); DECLARE_JNI_CLASS (RectClass, "android/graphics/Rect"); #undef JNI_CLASS_MEMBERS + +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (getName, "getName", "()Ljava/lang/String;") \ + METHOD (getModifiers, "getModifiers", "()I") \ + METHOD (getParameterTypes, "getParameterTypes", "()[Ljava/lang/Class;") \ + METHOD (getReturnType, "getReturnType", "()Ljava/lang/Class;") \ + METHOD (invoke, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") \ + METHOD (hashCode, "hashCode", "()I") \ + METHOD (equals, "equals", "(Ljava/lang/Object;)Z") \ + +DECLARE_JNI_CLASS (Method, "java/lang/reflect/Method"); +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (getName, "getName", "()Ljava/lang/String;") \ + METHOD (getModifiers, "getModifiers", "()I") \ + METHOD (isAnnotation, "isAnnotation", "()Z") \ + METHOD (isAnonymousClass, "isAnonymousClass", "()Z") \ + METHOD (isArray, "isArray", "()Z") \ + METHOD (isEnum, "isEnum", "()Z") \ + METHOD (isInterface, "isInterface", "()Z") \ + METHOD (isLocalClass, "isLocalClass", "()Z") \ + METHOD (isMemberClass, "isMemberClass", "()Z") \ + METHOD (isPrimitive, "isPrimitive", "()Z") \ + METHOD (isSynthetic, "isSynthetic", "()Z") \ + METHOD (getComponentType, "getComponentType", "()Ljava/lang/Class;") \ + METHOD (getSuperclass, "getSuperclass", "()Ljava/lang/Class;") \ + METHOD (getClassLoader, "getClassLoader", "()Ljava/lang/ClassLoader;") \ + +DECLARE_JNI_CLASS (JavaClass, "java/lang/Class"); +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (constructor, "", "()V") \ + +DECLARE_JNI_CLASS (JavaObject, "java/lang/Object"); +#undef JNI_CLASS_MEMBERS + + +//============================================================================== +class AndroidInterfaceImplementer; + +// This function takes ownership of the implementer. When the returned GlobalRef +// goes out of scope (and no other Java routine has a reference on the return-value) +// then the implementer will be deleted as well. +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const StringArray& interfaceNames, + LocalRef subclass); + +//============================================================================== +jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray); +void juce_dispatchDelete (JNIEnv*, jlong); + +//============================================================================== +class AndroidInterfaceImplementer +{ +protected: + virtual ~AndroidInterfaceImplementer() {} + virtual jobject invoke (jobject proxy, jobject method, jobjectArray args); + + //============================================================================== + friend LocalRef CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef); + friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray); + friend void juce_dispatchDelete (JNIEnv*, jlong); +private: + GlobalRef javaSubClass; +}; + +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const StringArray& interfaceNames); +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const String& interfaceName); diff --git a/modules/juce_core/native/juce_android_SystemStats.cpp b/modules/juce_core/native/juce_android_SystemStats.cpp index 8a8e772913..762ce02f48 100644 --- a/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/modules/juce_core/native/juce_android_SystemStats.cpp @@ -20,6 +20,13 @@ ============================================================================== */ +//============================================================================== +#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); @@ -91,6 +98,92 @@ jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, c return f; } +//============================================================================== +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const StringArray& interfaceNames, + LocalRef subclass) +{ + auto* env = getEnv(); + + implementer->javaSubClass = GlobalRef (subclass); + + // you need to override at least one interface + jassert (interfaceNames.size() > 0); + + auto classArray = LocalRef (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr)); + LocalRef classLoader; + + for (auto i = 0; i < interfaceNames.size(); ++i) + { + auto aClass = LocalRef (env->FindClass (interfaceNames[i].toRawUTF8())); + + if (aClass != nullptr) + { + if (i == 0) + classLoader = LocalRef (env->CallObjectMethod (aClass, JavaClass.getClassLoader)); + + env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass); + } + else + { + // interface class not found + jassertfalse; + } + } + + auto invocationHandler = LocalRef (env->CallStaticObjectMethod (JuceAppActivity, + JuceAppActivity.createInvocationHandler, + reinterpret_cast (implementer))); + + return LocalRef (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance, + classLoader.get(), classArray.get(), + invocationHandler.get())); +} + +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const StringArray& interfaceNames) +{ + return CreateJavaInterface (implementer, interfaceNames, + LocalRef (getEnv()->NewObject (JavaObject, + JavaObject.constructor))); +} + +LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, + const String& interfaceName) +{ + return CreateJavaInterface (implementer, StringArray (interfaceName)); +} + +jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args) +{ + auto* env = getEnv(); + return env->CallObjectMethod (method, Method.invoke, javaSubClass.get(), args); +} + +jobject juce_invokeImplementer (JNIEnv* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args) +{ + setEnv (env); + return reinterpret_cast (thisPtr)->invoke (proxy, method, args); +} + +void juce_dispatchDelete (JNIEnv* env, jlong thisPtr) +{ + setEnv (env); + delete reinterpret_cast (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; diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h index ea007b1f06..d4702a31e8 100644 --- a/modules/juce_core/native/juce_posix_SharedCode.h +++ b/modules/juce_core/native/juce_posix_SharedCode.h @@ -554,23 +554,13 @@ ssize_t FileOutputStream::writeInternal (const void* const data, const size_t nu return result; } +#ifndef JUCE_ANDROID void FileOutputStream::flushInternal() { - if (fileHandle != 0) - { - if (fsync (getFD (fileHandle)) == -1) - status = getResultForErrno(); - - #if JUCE_ANDROID - // This stuff tells the OS to asynchronously update the metadata - // that the OS has cached aboud the file - this metadata is used - // when the device is acting as a USB drive, and unless it's explicitly - // refreshed, it'll get out of step with the real file. - const LocalRef t (javaString (file.getFullPathName())); - android.activity.callVoidMethod (JuceAppActivity.scanFile, t.get()); - #endif - } + if (fileHandle != 0 && fsync (getFD (fileHandle)) == -1) + status = getResultForErrno(); } +#endif Result FileOutputStream::truncate() { diff --git a/modules/juce_events/native/juce_android_Messaging.cpp b/modules/juce_events/native/juce_android_Messaging.cpp index 35a2be5af9..922704ddd7 100644 --- a/modules/juce_events/native/juce_android_Messaging.cpp +++ b/modules/juce_events/native/juce_android_Messaging.cpp @@ -20,8 +20,61 @@ ============================================================================== */ -void MessageManager::doPlatformSpecificInitialisation() {} -void MessageManager::doPlatformSpecificShutdown() {} +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (constructor, "", "()V") \ + METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \ + +DECLARE_JNI_CLASS (JNIHandler, "android/os/Handler"); +#undef JNI_CLASS_MEMBERS + + +//============================================================================== +namespace Android +{ + class Runnable : public juce::AndroidInterfaceImplementer + { + public: + virtual void run() = 0; + + private: + jobject invoke (jobject proxy, jobject method, jobjectArray args) override + { + auto* env = getEnv(); + auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, Method.getName)); + + if (methodName == "run") + { + run(); + return nullptr; + } + + // invoke base class + return AndroidInterfaceImplementer::invoke (proxy, method, args); + } + }; + + struct Handler + { + juce_DeclareSingleton (Handler, false) + + Handler() : nativeHandler (getEnv()->NewObject (JNIHandler, JNIHandler.constructor)) {} + + bool post (Runnable* runnable) + { + return (getEnv()->CallBooleanMethod (nativeHandler.get(), JNIHandler.post, + CreateJavaInterface (runnable, "java/lang/Runnable").get()) != 0); + } + + GlobalRef nativeHandler; + }; + + juce_ImplementSingleton (Handler); +} + +//============================================================================== +void MessageManager::doPlatformSpecificInitialisation() { Android::Handler::getInstance(); } +void MessageManager::doPlatformSpecificShutdown() {} //============================================================================== bool MessageManager::dispatchNextMessageOnSystemQueue (const bool) @@ -33,26 +86,37 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool) } //============================================================================== +struct AndroidMessageCallback : public Android::Runnable +{ + AndroidMessageCallback (const MessageManager::MessageBase::Ptr& messageToDeliver) + : message (messageToDeliver) + {} + + AndroidMessageCallback (MessageManager::MessageBase::Ptr && messageToDeliver) + : message (static_cast (messageToDeliver)) + {} + + void run() override + { + JUCE_TRY + { + message->messageCallback(); + + // delete the message already here as Java will only run the + // destructor of this runnable the next time the garbage + // collector kicks in. + message = nullptr; + } + JUCE_CATCH_EXCEPTION + } + + MessageManager::MessageBase::Ptr message; +}; + bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { - message->incReferenceCount(); - android.activity.callVoidMethod (JuceAppActivity.postMessage, (jlong) (pointer_sized_uint) message); - return true; + return Android::Handler::getInstance()->post (new AndroidMessageCallback (message)); } - -JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (JNIEnv* env, jobject, jlong value)) -{ - setEnv (env); - - JUCE_TRY - { - MessageManager::MessageBase* const message = (MessageManager::MessageBase*) (pointer_sized_uint) value; - message->messageCallback(); - message->decReferenceCount(); - } - JUCE_CATCH_EXCEPTION -} - //============================================================================== void MessageManager::broadcastMessage (const String&) { diff --git a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp index 4c71af3328..d4001fd084 100644 --- a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp +++ b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp @@ -284,7 +284,7 @@ char OnlineUnlockStatus::MachineIDUtilities::getPlatformPrefix() String OnlineUnlockStatus::MachineIDUtilities::getEncodedIDString (const String& input) { - const String platform (String::charToString (getPlatformPrefix())); + const String platform (String::charToString (static_cast (getPlatformPrefix()))); return platform + MD5 ((input + "salt_1" + platform).toUTF8()) .toHexString().substring (0, 9).toUpperCase();