| @@ -4,6 +4,33 @@ JUCE breaking changes | |||
| Develop | |||
| ======= | |||
| Change | |||
| ------ | |||
| CameraDevice::Listener::imageReceived() has been replaced by a new function | |||
| CameraDevice::takeStillPicture(). The callback passed in takeStillPicture() | |||
| will always be triggered on the message thread. | |||
| Possible Issues | |||
| --------------- | |||
| The code handling image capture needs to be adjusted to work well on a message | |||
| thread. This means that you should not perform any lengthy operations in your | |||
| callback, as this will stall your UI. | |||
| Workaround | |||
| ---------- | |||
| Use CameraDevice::takeStillPicture() instead of old listener callback. Pass | |||
| your lambda or other std::function compliant object to takeStillPicture which | |||
| will be called for you when the capture has finished. If you want to perform | |||
| any time consuming operation upon receiving the picture, schedule it on a | |||
| separate worker thread. | |||
| Rationale | |||
| --------- | |||
| The old Listener interface was not working in a typical listener pattern. It | |||
| feels more natural to request still picture capture with a dedicated function. | |||
| This is also more compliant with async mobile APIs. | |||
| Change | |||
| ------ | |||
| JUCE no longer supports OS X deployment targets earlier than 10.7. | |||
| @@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni") | |||
| add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c") | |||
| set_source_files_properties("${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-gnu-statement-expression") | |||
| add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_roli_juce_demorunner_DemoRunner" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/roli/juce/demorunner/DemoRunner\"" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME=com_roli_juce_demorunner_SharingContentProvider" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH=\"com/roli/juce/demorunner/SharingContentProvider\"" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=5.3.1" "-DJUCE_APP_VERSION_HEX=0x50301") | |||
| add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=23" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_demorunner_DemoRunner" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/demorunner/DemoRunner\"" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME=com_juce_demorunner_SharingContentProvider" "-DJUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH=\"com/juce/demorunner/SharingContentProvider\"" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCE_ANDROID_GL_ES_VERSION_3_0=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=5.3.1" "-DJUCE_APP_VERSION_HEX=0x50301") | |||
| include_directories( AFTER | |||
| "../../../JuceLibraryCode" | |||
| @@ -546,6 +546,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" | |||
| "../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" | |||
| "../../../../../modules/juce_core/containers/juce_SortedSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.h" | |||
| @@ -1465,6 +1466,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" | |||
| "../../../../../modules/juce_video/capture/juce_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_android_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_mac_Video.h" | |||
| "../../../../../modules/juce_video/native/juce_win32_CameraDevice.h" | |||
| @@ -2008,6 +2010,7 @@ set_source_files_properties("../../../../../modules/juce_core/containers/juce_Pr | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SortedSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -2927,6 +2930,7 @@ set_source_files_properties("../../../../../modules/juce_product_unlocking/juce_ | |||
| set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_win32_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -19,7 +19,7 @@ android { | |||
| } | |||
| defaultConfig { | |||
| applicationId "com.roli.juce.demorunner" | |||
| applicationId "com.juce.demorunner" | |||
| minSdkVersion 23 | |||
| targetSdkVersion 23 | |||
| externalNativeBuild { | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="5.3.1" | |||
| package="com.roli.juce.demorunner"> | |||
| package="com.juce.demorunner"> | |||
| <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true"/> | |||
| <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23"/> | |||
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | |||
| @@ -9,18 +9,19 @@ | |||
| <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> | |||
| <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> | |||
| <uses-permission android:name="android.permission.BLUETOOTH"/> | |||
| <uses-permission android:name="android.permission.CAMERA"/> | |||
| <uses-permission android:name="android.permission.RECORD_AUDIO"/> | |||
| <uses-permission android:name="android.permission.INTERNET"/> | |||
| <uses-feature android:glEsVersion="0x00030000" android:required="true"/> | |||
| <application android:label="@string/app_name" android:icon="@drawable/icon" android:hardwareAccelerated="false"> | |||
| <activity android:name="DemoRunner" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize" | |||
| android:screenOrientation="userLandscape" android:launchMode="singleTask" android:hardwareAccelerated="true"> | |||
| android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true"> | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN"/> | |||
| <category android:name="android.intent.category.LAUNCHER"/> | |||
| </intent-filter> | |||
| </activity> | |||
| <provider android:name="com.roli.juce.demorunner.SharingContentProvider" android:authorities="com.roli.juce.demorunner.sharingcontentprovider" | |||
| <provider android:name="com.juce.demorunner.SharingContentProvider" android:authorities="com.juce.demorunner.sharingcontentprovider" | |||
| android:grantUriPermissions="true" android:exported="false"/> | |||
| </application> | |||
| </manifest> | |||
| @@ -30,6 +30,7 @@ import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.content.pm.PackageInfo; | |||
| import android.content.pm.PackageManager; | |||
| import android.hardware.camera2.*; | |||
| import android.net.http.SslError; | |||
| import android.net.Uri; | |||
| import android.os.Bundle; | |||
| @@ -119,6 +120,7 @@ public class DemoRunner extends Activity | |||
| private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
| private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3; | |||
| private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4; | |||
| private static final int JUCE_PERMISSIONS_CAMERA = 5; | |||
| private static String getAndroidPermissionName (int permissionID) | |||
| { | |||
| @@ -129,6 +131,7 @@ public class DemoRunner extends Activity | |||
| // 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; | |||
| case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA; | |||
| } | |||
| // unknown permission ID! | |||
| @@ -1205,6 +1208,7 @@ public class DemoRunner extends Activity | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
| appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>(); | |||
| } | |||
| @Override | |||
| @@ -1221,6 +1225,11 @@ public class DemoRunner extends Activity | |||
| { | |||
| suspendApp(); | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appPaused(); | |||
| try | |||
| { | |||
| Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down | |||
| @@ -1236,12 +1245,10 @@ public class DemoRunner extends Activity | |||
| super.onResume(); | |||
| resumeApp(); | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| for (int i = 0; i < viewHolder.getChildCount(); ++i) | |||
| { | |||
| if (viewHolder.getChildAt (i) instanceof ComponentPeerView) | |||
| ((ComponentPeerView) viewHolder.getChildAt (i)).appResumed(); | |||
| } | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appResumed(); | |||
| } | |||
| @Override | |||
| @@ -1368,11 +1375,14 @@ public class DemoRunner extends Activity | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
| viewHolder.addView (v); | |||
| addAppPausedResumedListener (v, host); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| removeAppPausedResumedListener (view, view.host); | |||
| view.host = 0; | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| @@ -1590,9 +1600,28 @@ public class DemoRunner extends Activity | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public interface AppPausedResumedListener | |||
| { | |||
| void appPaused(); | |||
| void appResumed(); | |||
| } | |||
| private Map<Long, AppPausedResumedListener> appPausedResumedListeners; | |||
| public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.put (new Long (listenerHost), l); | |||
| } | |||
| public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.remove (new Long (listenerHost)); | |||
| } | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| implements View.OnFocusChangeListener, AppPausedResumedListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_, long host) | |||
| { | |||
| @@ -1940,13 +1969,25 @@ public class DemoRunner extends Activity | |||
| } | |||
| //============================================================================== | |||
| private native void handleAppPaused (long host); | |||
| private native void handleAppResumed (long host); | |||
| @Override | |||
| public void appPaused() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| handleAppPaused (host); | |||
| } | |||
| @Override | |||
| public void appResumed() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| handleAppResumed (host); | |||
| } | |||
| } | |||
| @@ -2616,6 +2657,179 @@ public class DemoRunner extends Activity | |||
| private final Object hostLock = new Object(); | |||
| } | |||
| //============================================================================== | |||
| public class CameraDeviceStateCallback extends CameraDevice.StateCallback | |||
| { | |||
| private native void cameraDeviceStateClosed (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateDisconnected (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateError (long host, CameraDevice camera, int error); | |||
| private native void cameraDeviceStateOpened (long host, CameraDevice camera); | |||
| CameraDeviceStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onClosed (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateClosed (host, camera); | |||
| } | |||
| @Override | |||
| public void onDisconnected (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateDisconnected (host, camera); | |||
| } | |||
| @Override | |||
| public void onError (CameraDevice camera, int error) | |||
| { | |||
| cameraDeviceStateError (host, camera, error); | |||
| } | |||
| @Override | |||
| public void onOpened (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateOpened (host, camera); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback | |||
| { | |||
| private native void cameraCaptureSessionActive (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionReady (long host, CameraCaptureSession session); | |||
| CameraCaptureSessionStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onActive (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionActive (host, session); | |||
| } | |||
| @Override | |||
| public void onClosed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionClosed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigureFailed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigureFailed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigured (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigured (host, session); | |||
| } | |||
| @Override | |||
| public void onReady (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionReady (host, session); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback | |||
| { | |||
| private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, | |||
| CaptureRequest request, TotalCaptureResult result); | |||
| private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, | |||
| CaptureRequest request, CaptureFailure failure); | |||
| private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, | |||
| CaptureRequest request, CaptureResult partialResult); | |||
| private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId); | |||
| private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber); | |||
| private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, | |||
| long timestamp, long frameNumber); | |||
| CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview) | |||
| { | |||
| host = hostToUse; | |||
| preview = shouldBePreview; | |||
| } | |||
| @Override | |||
| public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request, | |||
| TotalCaptureResult result) | |||
| { | |||
| cameraCaptureSessionCaptureCompleted (host, preview, session, request, result); | |||
| } | |||
| @Override | |||
| public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) | |||
| { | |||
| cameraCaptureSessionCaptureFailed (host, preview, session, request, failure); | |||
| } | |||
| @Override | |||
| public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request, | |||
| CaptureResult partialResult) | |||
| { | |||
| cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber); | |||
| } | |||
| @Override | |||
| public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp, | |||
| long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber); | |||
| } | |||
| private long host; | |||
| private boolean preview; | |||
| } | |||
| //============================================================================== | |||
| public class JuceOrientationEventListener extends OrientationEventListener | |||
| { | |||
| private native void deviceOrientationChanged (long host, int orientation); | |||
| public JuceOrientationEventListener (long hostToUse, Context context, int rate) | |||
| { | |||
| super (context, rate); | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onOrientationChanged (int orientation) | |||
| { | |||
| deviceOrientationChanged (host, orientation); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| @@ -259,7 +259,7 @@ | |||
| MACOSX_DEPLOYMENT_TARGET = 10.11; | |||
| MACOSX_DEPLOYMENT_TARGET_ppc = 10.4; | |||
| OTHER_CPLUSPLUSFLAGS = "-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.roli.juce.demorunner; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.juce.demorunner; | |||
| SDKROOT_ppc = macosx10.5; | |||
| USE_HEADERMAP = NO; }; name = Debug; }; | |||
| 69330F27DD2C71609336C7D2 = {isa = XCBuildConfiguration; buildSettings = { | |||
| @@ -296,7 +296,7 @@ | |||
| MACOSX_DEPLOYMENT_TARGET = 10.11; | |||
| MACOSX_DEPLOYMENT_TARGET_ppc = 10.4; | |||
| OTHER_CPLUSPLUSFLAGS = "-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.roli.juce.demorunner; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.juce.demorunner; | |||
| SDKROOT_ppc = macosx10.5; | |||
| USE_HEADERMAP = NO; }; name = Release; }; | |||
| C01EC82F42B640CA1E54AD53 = {isa = XCBuildConfiguration; buildSettings = { | |||
| @@ -8,7 +8,7 @@ | |||
| <key>CFBundleIconFile</key> | |||
| <string>Icon.icns</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>com.roli.juce.demorunner</string> | |||
| <string>com.juce.demorunner</string> | |||
| <key>CFBundleName</key> | |||
| <string>DemoRunner</string> | |||
| <key>CFBundleDisplayName</key> | |||
| @@ -772,6 +772,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2836,6 +2839,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -1141,6 +1141,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4821,6 +4824,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -772,6 +772,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2836,6 +2839,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -1141,6 +1141,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4821,6 +4824,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -772,6 +772,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2836,6 +2839,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -1141,6 +1141,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4821,6 +4824,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -19,6 +19,7 @@ | |||
| 6A61CBB4E39BFD392D97528F = {isa = PBXBuildFile; fileRef = 61AE09C749B007B70A265D9B; }; | |||
| 0B0CE6D5062E5C02A41F24BC = {isa = PBXBuildFile; fileRef = 873F9DD54978E601102353B4; }; | |||
| 5E4310B3F6BB639875D3E9B8 = {isa = PBXBuildFile; fileRef = 49ECA8B998B339A083674A22; }; | |||
| AE7FB2AC3885F4BF53A5DDA1 = {isa = PBXBuildFile; fileRef = 7983C452610C1638B7E78F12; }; | |||
| 1FB200F4AE3E4E7CDFF629BB = {isa = PBXBuildFile; fileRef = 24D74AF1C95BEF957DC4FA77; }; | |||
| AC783ECD84496E0B77911EEE = {isa = PBXBuildFile; fileRef = 34F1320BC5C23702C08DF9F0; }; | |||
| B1981F62F6A91FD2F579A198 = {isa = PBXBuildFile; fileRef = 23CD1A3F9067C3A0ECE7BB67; }; | |||
| @@ -92,6 +93,7 @@ | |||
| 6C5E26B4D28F8450435B8AE1 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_cryptography.mm"; path = "../../JuceLibraryCode/include_juce_cryptography.mm"; sourceTree = "SOURCE_ROOT"; }; | |||
| 72129757D2A553B90A7157C6 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = "SOURCE_ROOT"; }; | |||
| 76A157A111866670A4678F04 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; | |||
| 7983C452610C1638B7E78F12 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; | |||
| 7A5AAE9EE573FC6105CC4AAC = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SettingsContent.h; path = ../../Source/UI/SettingsContent.h; sourceTree = "SOURCE_ROOT"; }; | |||
| 8135645508EEFDBDCDF2ADC6 = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = DemoRunner/Images.xcassets; sourceTree = "SOURCE_ROOT"; }; | |||
| 831A01C745C905F5715CD822 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_juce_blocks_basics.cpp"; path = "../../JuceLibraryCode/include_juce_blocks_basics.cpp"; sourceTree = "SOURCE_ROOT"; }; | |||
| @@ -209,6 +211,7 @@ | |||
| 61AE09C749B007B70A265D9B, | |||
| 873F9DD54978E601102353B4, | |||
| 49ECA8B998B339A083674A22, | |||
| 7983C452610C1638B7E78F12, | |||
| 24D74AF1C95BEF957DC4FA77, | |||
| 34F1320BC5C23702C08DF9F0, | |||
| 23CD1A3F9067C3A0ECE7BB67, | |||
| @@ -256,7 +259,7 @@ | |||
| INFOPLIST_PREPROCESS = NO; | |||
| INSTALL_PATH = "$(HOME)/Applications"; | |||
| OTHER_CPLUSPLUSFLAGS = "-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.roli.juce.demorunner; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.juce.demorunner; | |||
| USE_HEADERMAP = NO; }; name = Debug; }; | |||
| 69330F27DD2C71609336C7D2 = {isa = XCBuildConfiguration; buildSettings = { | |||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | |||
| @@ -293,7 +296,7 @@ | |||
| INSTALL_PATH = "$(HOME)/Applications"; | |||
| LLVM_LTO = YES; | |||
| OTHER_CPLUSPLUSFLAGS = "-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.roli.juce.demorunner; | |||
| PRODUCT_BUNDLE_IDENTIFIER = com.juce.demorunner; | |||
| USE_HEADERMAP = NO; }; name = Release; }; | |||
| C01EC82F42B640CA1E54AD53 = {isa = XCBuildConfiguration; buildSettings = { | |||
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | |||
| @@ -428,6 +431,7 @@ | |||
| 6A61CBB4E39BFD392D97528F, | |||
| 0B0CE6D5062E5C02A41F24BC, | |||
| 5E4310B3F6BB639875D3E9B8, | |||
| AE7FB2AC3885F4BF53A5DDA1, | |||
| 1FB200F4AE3E4E7CDFF629BB, | |||
| AC783ECD84496E0B77911EEE, | |||
| B1981F62F6A91FD2F579A198, | |||
| @@ -7,12 +7,14 @@ | |||
| <true/> | |||
| <key>NSMicrophoneUsageDescription</key> | |||
| <string>This is an audio app which requires audio input. If you do not have a USB audio interface connected it will use the microphone.</string> | |||
| <key>NSCameraUsageDescription</key> | |||
| <string>This app requires camera usage to function properly.</string> | |||
| <key>UIViewControllerBasedStatusBarAppearance</key> | |||
| <false/> | |||
| <key>CFBundleExecutable</key> | |||
| <string>${EXECUTABLE_NAME}</string> | |||
| <key>CFBundleIdentifier</key> | |||
| <string>com.roli.juce.demorunner</string> | |||
| <string>com.juce.demorunner</string> | |||
| <key>CFBundleName</key> | |||
| <string>DemoRunner</string> | |||
| <key>CFBundleDisplayName</key> | |||
| @@ -39,6 +41,7 @@ | |||
| <true/> | |||
| <key>UISupportedInterfaceOrientations</key> | |||
| <array> | |||
| <string>UIInterfaceOrientationPortrait</string> | |||
| <string>UIInterfaceOrientationLandscapeLeft</string> | |||
| <string>UIInterfaceOrientationLandscapeRight</string> | |||
| </array> | |||
| @@ -1,9 +1,9 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT name="DemoRunner" projectType="guiapp" jucerVersion="5.3.1" defines="JUCE_DEMO_RUNNER=1 JUCE_UNIT_TESTS=1" | |||
| bundleIdentifier="com.roli.juce.demorunner" version="5.3.1" companyName="ROLI Ltd." | |||
| bundleIdentifier="com.juce.demorunner" version="5.3.1" companyName="ROLI Ltd." | |||
| companyCopyright="Copyright (c) 2018 - ROLI Ltd." companyWebsite="https://www.juce.com/" | |||
| companyEmail="info@juce.com" id="yj7xMM"> | |||
| companyEmail="info@juce.com" id="yj7xMM" reportAppUsage="1"> | |||
| <MAINGROUP id="G8kbr7" name="DemoRunner"> | |||
| <GROUP id="{20E3F84A-29E9-D5FF-4559-1A9E4A70CD60}" name="Source"> | |||
| <GROUP id="{272A692A-6AFE-68BD-C8E8-63B3D62245B1}" name="Demos"> | |||
| @@ -88,10 +88,10 @@ | |||
| </MODULEPATHS> | |||
| </LINUX_MAKE> | |||
| <ANDROIDSTUDIO targetFolder="Builds/Android" androidSDKPath="" androidNDKPath="" | |||
| androidMinimumSDK="23" androidScreenOrientation="landscape" microphonePermissionNeeded="1" | |||
| androidBluetoothNeeded="1" androidExternalReadNeeded="1" androidExternalWriteNeeded="1" | |||
| androidMinimumSDK="23" microphonePermissionNeeded="1" androidBluetoothNeeded="1" | |||
| androidExternalReadNeeded="1" androidExternalWriteNeeded="1" | |||
| androidEnableContentSharing="1" androidExtraAssetsFolder="../Assets" | |||
| smallIcon="YyqWd2" bigIcon="YyqWd2"> | |||
| smallIcon="YyqWd2" bigIcon="YyqWd2" cameraPermissionNeeded="1"> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION isDebug="1" name="Debug" androidArchitectures="armeabi x86"/> | |||
| <CONFIGURATION isDebug="0" name="Release"/> | |||
| @@ -119,10 +119,10 @@ | |||
| <MODULEPATH id="juce_analytics" path="../../modules"/> | |||
| </MODULEPATHS> | |||
| </ANDROIDSTUDIO> | |||
| <XCODE_IPHONE targetFolder="Builds/iOS" iosScreenOrientation="landscape" iPadScreenOrientation="landscape" | |||
| UISupportsDocumentBrowser="1" microphonePermissionNeeded="1" | |||
| iCloudPermissions="1" UIFileSharingEnabled="1" customXcodeResourceFolders="../Assets" | |||
| smallIcon="YyqWd2" bigIcon="YyqWd2" extraCompilerFlags="-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"> | |||
| <XCODE_IPHONE targetFolder="Builds/iOS" UISupportsDocumentBrowser="1" microphonePermissionNeeded="1" | |||
| cameraPermissionNeeded="1" iCloudPermissions="1" UIFileSharingEnabled="1" | |||
| customXcodeResourceFolders="../Assets" smallIcon="YyqWd2" bigIcon="YyqWd2" | |||
| extraCompilerFlags="-Wall -Wshadow -Wno-missing-field-initializers -Wshadow -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wsign-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wno-ignored-qualifiers -Wunreachable-code"> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION isDebug="1" name="Debug"/> | |||
| <CONFIGURATION isDebug="0" name="Release"/> | |||
| @@ -156,6 +156,10 @@ | |||
| //#define JUCE_PLUGINHOST_AU 0 | |||
| #endif | |||
| #ifndef JUCE_PLUGINHOST_LADSPA | |||
| //#define JUCE_PLUGINHOST_LADSPA 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_audio_utils flags: | |||
| @@ -34,7 +34,7 @@ | |||
| #include "../../../GUI/AnimationAppDemo.h" | |||
| #include "../../../GUI/AnimationDemo.h" | |||
| #include "../../../GUI/BouncingBallWavetableDemo.h" | |||
| #if JUCE_MAC || JUCE_WINDOWS | |||
| #if JUCE_USE_CAMERA && ! JUCE_LINUX | |||
| #include "../../../GUI/CameraDemo.h" | |||
| #endif | |||
| #if ! JUCE_ANDROID | |||
| @@ -73,7 +73,7 @@ void registerDemos_Two() noexcept | |||
| REGISTER_DEMO (AnimationAppDemo, GUI, false) | |||
| REGISTER_DEMO (AnimationDemo, GUI, false) | |||
| REGISTER_DEMO (BouncingBallWavetableDemo, GUI, false) | |||
| #if JUCE_MAC || JUCE_WINDOWS | |||
| #if JUCE_USE_CAMERA && ! JUCE_LINUX | |||
| REGISTER_DEMO (CameraDemo, GUI, true) | |||
| #endif | |||
| #if ! JUCE_ANDROID | |||
| @@ -122,6 +122,7 @@ private: | |||
| #if JUCE_IOS || JUCE_ANDROID | |||
| setFullScreen (true); | |||
| Desktop::getInstance().setOrientationsEnabled (Desktop::rotatedClockwise | Desktop::rotatedAntiClockwise); | |||
| #else | |||
| setBounds ((int) (0.1f * getParentWidth()), | |||
| (int) (0.1f * getParentHeight()), | |||
| @@ -31,7 +31,7 @@ | |||
| dependencies: juce_core, juce_cryptography, juce_data_structures, juce_events, | |||
| juce_graphics, juce_gui_basics, juce_gui_extra, juce_video | |||
| exporters: xcode_mac, vs2017, linux_make | |||
| exporters: xcode_mac, vs2017, androidstudio, xcode_iphone | |||
| moduleFlags: JUCE_USE_CAMERA=1 | |||
| @@ -49,15 +49,18 @@ | |||
| #include "../Assets/DemoUtilities.h" | |||
| //============================================================================== | |||
| class CameraDemo : public Component, | |||
| private CameraDevice::Listener, | |||
| private AsyncUpdater | |||
| class CameraDemo : public Component | |||
| { | |||
| public: | |||
| CameraDemo() | |||
| { | |||
| setOpaque (true); | |||
| #if JUCE_ANDROID | |||
| // Android requires exclusive access to the audio device when recording videos. | |||
| audioDeviceManager.closeAudioDevice(); | |||
| #endif | |||
| addAndMakeVisible (cameraSelectorComboBox); | |||
| updateCameraList(); | |||
| cameraSelectorComboBox.setSelectedId (1); | |||
| @@ -76,6 +79,21 @@ public: | |||
| cameraSelectorComboBox.setSelectedId (2); | |||
| setSize (500, 500); | |||
| #if JUCE_IOS || JUCE_ANDROID | |||
| setPortraitOrientationEnabled (true); | |||
| #endif | |||
| } | |||
| ~CameraDemo() | |||
| { | |||
| #if JUCE_IOS || JUCE_ANDROID | |||
| setPortraitOrientationEnabled (false); | |||
| #endif | |||
| #if JUCE_ANDROID | |||
| audioDeviceManager.restartLastAudioDevice(); | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| @@ -101,26 +119,66 @@ public: | |||
| recordMovieButton.setBounds (top.removeFromLeft (recordMovieButton.getWidth())); | |||
| r.removeFromTop (4); | |||
| auto previewArea = r.removeFromTop (r.getHeight() / 2); | |||
| auto previewArea = shouldUseLandscapeLayout() ? r.removeFromLeft (r.getWidth() / 2) | |||
| : r.removeFromTop (r.getHeight() / 2); | |||
| if (cameraPreviewComp.get() != nullptr) | |||
| cameraPreviewComp->setBounds (previewArea); | |||
| r.removeFromTop (4); | |||
| if (shouldUseLandscapeLayout()) | |||
| r.removeFromLeft (4); | |||
| else | |||
| r.removeFromTop (4); | |||
| lastSnapshot.setBounds (r); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| // if this PIP is running inside the demo runner, we'll use the shared device manager instead | |||
| #ifndef JUCE_DEMO_RUNNER | |||
| AudioDeviceManager audioDeviceManager; | |||
| #else | |||
| AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) }; | |||
| #endif | |||
| std::unique_ptr<CameraDevice> cameraDevice; | |||
| std::unique_ptr<Component> cameraPreviewComp; | |||
| ImageComponent lastSnapshot; | |||
| ComboBox cameraSelectorComboBox { "Camera" }; | |||
| TextButton snapshotButton { "Take a snapshot" }; | |||
| #if ! JUCE_ANDROID && ! JUCE_IOS | |||
| TextButton recordMovieButton { "Record a movie (to your desktop)..." }; | |||
| #else | |||
| TextButton recordMovieButton { "Record a movie" }; | |||
| #endif | |||
| bool recordingMovie = false; | |||
| File recordingFile; | |||
| bool contentSharingPending = false; | |||
| void setPortraitOrientationEnabled (bool shouldBeEnabled) | |||
| { | |||
| auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled(); | |||
| if (shouldBeEnabled) | |||
| allowedOrientations |= Desktop::upright; | |||
| else | |||
| allowedOrientations &= ~Desktop::upright; | |||
| Desktop::getInstance().setOrientationsEnabled (allowedOrientations); | |||
| } | |||
| bool shouldUseLandscapeLayout() const noexcept | |||
| { | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| auto orientation = Desktop::getInstance().getCurrentOrientation(); | |||
| return orientation == Desktop::rotatedClockwise || orientation == Desktop::rotatedAntiClockwise; | |||
| #else | |||
| return false; | |||
| #endif | |||
| } | |||
| void updateCameraList() | |||
| { | |||
| @@ -137,25 +195,68 @@ private: | |||
| void cameraChanged() | |||
| { | |||
| // This is called when the user chooses a camera from the drop-down list. | |||
| cameraDevice .reset(); | |||
| #if JUCE_IOS | |||
| // On iOS, when switching camera, open the new camera first, so that it can | |||
| // share the underlying camera session with the old camera. Otherwise, the | |||
| // session would have to be closed first, which can take several seconds. | |||
| if (cameraSelectorComboBox.getSelectedId() == 1) | |||
| cameraDevice.reset(); | |||
| #else | |||
| cameraDevice.reset(); | |||
| #endif | |||
| cameraPreviewComp.reset(); | |||
| recordingMovie = false; | |||
| if (cameraSelectorComboBox.getSelectedId() > 1) | |||
| { | |||
| // Try to open the user's choice of camera.. | |||
| cameraDevice.reset (CameraDevice::openDevice (cameraSelectorComboBox.getSelectedId() - 2)); | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| openCameraAsync(); | |||
| #else | |||
| cameraDeviceOpenResult (CameraDevice::openDevice (cameraSelectorComboBox.getSelectedId() - 2), {}); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| snapshotButton .setEnabled (cameraDevice != nullptr && ! contentSharingPending); | |||
| recordMovieButton.setEnabled (cameraDevice != nullptr && ! contentSharingPending); | |||
| resized(); | |||
| } | |||
| } | |||
| // and if it worked, create a preview component for it.. | |||
| if (cameraDevice.get() != nullptr) | |||
| { | |||
| cameraPreviewComp.reset (cameraDevice->createViewerComponent()); | |||
| addAndMakeVisible (cameraPreviewComp.get()); | |||
| } | |||
| void openCameraAsync() | |||
| { | |||
| SafePointer<CameraDemo> safeThis (this); | |||
| CameraDevice::openDeviceAsync (cameraSelectorComboBox.getSelectedId() - 2, | |||
| [safeThis] (CameraDevice* device, const String& error) mutable | |||
| { | |||
| if (safeThis) | |||
| safeThis->cameraDeviceOpenResult (device, error); | |||
| }); | |||
| } | |||
| void cameraDeviceOpenResult (CameraDevice* device, const String& error) | |||
| { | |||
| // If camera opening worked, create a preview component for it.. | |||
| cameraDevice.reset (device); | |||
| if (cameraDevice.get() != nullptr) | |||
| { | |||
| #if JUCE_ANDROID | |||
| SafePointer<CameraDemo> safeThis (this); | |||
| cameraDevice->onErrorOccurred = [safeThis] (const String& error) mutable { if (safeThis) safeThis->errorOccurred (error); }; | |||
| #endif | |||
| cameraPreviewComp.reset (cameraDevice->createViewerComponent()); | |||
| addAndMakeVisible (cameraPreviewComp.get()); | |||
| } | |||
| else | |||
| { | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Camera open failed", | |||
| "Camera open failed, reason: " + error); | |||
| } | |||
| snapshotButton .setEnabled (cameraDevice.get() != nullptr); | |||
| recordMovieButton.setEnabled (cameraDevice.get() != nullptr); | |||
| snapshotButton .setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending); | |||
| recordMovieButton.setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending); | |||
| resized(); | |||
| } | |||
| @@ -169,10 +270,20 @@ private: | |||
| // Start recording to a file on the user's desktop.. | |||
| recordingMovie = true; | |||
| auto file = File::getSpecialLocation (File::userDesktopDirectory) | |||
| .getNonexistentChildFile ("JuceCameraDemo", CameraDevice::getFileExtension()); | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| recordingFile = File::getSpecialLocation (File::tempDirectory) | |||
| #else | |||
| recordingFile = File::getSpecialLocation (File::userDesktopDirectory) | |||
| #endif | |||
| .getNonexistentChildFile ("JuceCameraVideoDemo", CameraDevice::getFileExtension()); | |||
| #if JUCE_ANDROID | |||
| // Android does not support taking pictures while recording video. | |||
| snapshotButton.setEnabled (false); | |||
| #endif | |||
| cameraDevice->startRecordingToFile (file); | |||
| cameraSelectorComboBox.setEnabled (false); | |||
| cameraDevice->startRecordingToFile (recordingFile); | |||
| recordMovieButton.setButtonText ("Stop Recording"); | |||
| } | |||
| else | |||
| @@ -180,40 +291,99 @@ private: | |||
| // Already recording, so stop... | |||
| recordingMovie = false; | |||
| cameraDevice->stopRecording(); | |||
| #if ! JUCE_ANDROID && ! JUCE_IOS | |||
| recordMovieButton.setButtonText ("Start recording (to a file on your desktop)"); | |||
| #else | |||
| recordMovieButton.setButtonText ("Record a movie"); | |||
| #endif | |||
| cameraSelectorComboBox.setEnabled (true); | |||
| #if JUCE_ANDROID | |||
| snapshotButton.setEnabled (true); | |||
| #endif | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| URL url (recordingFile); | |||
| snapshotButton .setEnabled (false); | |||
| recordMovieButton.setEnabled (false); | |||
| contentSharingPending = true; | |||
| SafePointer<CameraDemo> safeThis (this); | |||
| juce::ContentSharer::getInstance()->shareFiles ({url}, | |||
| [safeThis] (bool success, const String&) mutable | |||
| { | |||
| if (safeThis) | |||
| safeThis->sharingFinished (success, false); | |||
| }); | |||
| #endif | |||
| } | |||
| } | |||
| } | |||
| void takeSnapshot() | |||
| { | |||
| // When the user clicks the snapshot button, we'll attach ourselves to | |||
| // the camera as a listener, and wait for an image to arrive... | |||
| cameraDevice->addListener (this); | |||
| SafePointer<CameraDemo> safeThis (this); | |||
| cameraDevice->takeStillPicture ([safeThis] (const Image& image) mutable { safeThis->imageReceived (image); }); | |||
| } | |||
| // This is called by the camera device when a new image arrives | |||
| void imageReceived (const Image& image) override | |||
| void imageReceived (const Image& image) | |||
| { | |||
| // In this app we just want to take one image, so as soon as this happens, | |||
| // we'll unregister ourselves as a listener. | |||
| if (cameraDevice.get() != nullptr) | |||
| cameraDevice->removeListener (this); | |||
| // This callback won't be on the message thread, so to get the image back to | |||
| // the message thread, we'll stash a pointer to it (which is reference-counted in | |||
| // a thead-safe way), and trigger an async callback which will then display the | |||
| // new image.. | |||
| incomingImage = image; | |||
| triggerAsyncUpdate(); | |||
| if (! image.isValid()) | |||
| return; | |||
| lastSnapshot.setImage (image); | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| auto imageFile = File::getSpecialLocation (File::tempDirectory).getNonexistentChildFile ("JuceCameraPhotoDemo", ".jpg"); | |||
| if (auto stream = std::unique_ptr<OutputStream> (imageFile.createOutputStream())) | |||
| { | |||
| if (JPEGImageFormat().writeImageToStream (image, *stream)) | |||
| { | |||
| URL url (imageFile); | |||
| snapshotButton .setEnabled (false); | |||
| recordMovieButton.setEnabled (false); | |||
| contentSharingPending = true; | |||
| SafePointer<CameraDemo> safeThis (this); | |||
| juce::ContentSharer::getInstance()->shareFiles ({url}, | |||
| [safeThis] (bool success, const String&) mutable | |||
| { | |||
| if (safeThis) | |||
| safeThis->sharingFinished (success, true); | |||
| }); | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| Image incomingImage; | |||
| void errorOccurred (const String& error) | |||
| { | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||
| "Camera Device Error", | |||
| "An error has occurred: " + error + " Camera will be closed."); | |||
| cameraDevice.reset(); | |||
| cameraSelectorComboBox.setSelectedId (1); | |||
| snapshotButton .setEnabled (false); | |||
| recordMovieButton.setEnabled (false); | |||
| } | |||
| void handleAsyncUpdate() override | |||
| void sharingFinished (bool success, bool isCapture) | |||
| { | |||
| if (incomingImage.isValid()) | |||
| lastSnapshot.setImage (incomingImage); | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||
| isCapture ? "Image sharing result" : "Video sharing result", | |||
| success ? "Success!" : "Failed!"); | |||
| contentSharingPending = false; | |||
| snapshotButton .setEnabled (true); | |||
| recordMovieButton.setEnabled (true); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDemo) | |||
| @@ -399,6 +399,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" | |||
| "../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" | |||
| "../../../../../modules/juce_core/containers/juce_SortedSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.h" | |||
| @@ -1566,6 +1567,7 @@ set_source_files_properties("../../../../../modules/juce_core/containers/juce_Pr | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SortedSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -30,6 +30,7 @@ import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.content.pm.PackageInfo; | |||
| import android.content.pm.PackageManager; | |||
| import android.hardware.camera2.*; | |||
| import android.net.http.SslError; | |||
| import android.net.Uri; | |||
| import android.os.Bundle; | |||
| @@ -119,6 +120,7 @@ public class AudioPerformanceTest extends Activity | |||
| private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
| private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3; | |||
| private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4; | |||
| private static final int JUCE_PERMISSIONS_CAMERA = 5; | |||
| private static String getAndroidPermissionName (int permissionID) | |||
| { | |||
| @@ -129,6 +131,7 @@ public class AudioPerformanceTest extends Activity | |||
| // 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; | |||
| case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA; | |||
| } | |||
| // unknown permission ID! | |||
| @@ -1205,6 +1208,7 @@ public class AudioPerformanceTest extends Activity | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
| appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>(); | |||
| } | |||
| @Override | |||
| @@ -1221,6 +1225,11 @@ public class AudioPerformanceTest extends Activity | |||
| { | |||
| suspendApp(); | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appPaused(); | |||
| try | |||
| { | |||
| Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down | |||
| @@ -1236,12 +1245,10 @@ public class AudioPerformanceTest extends Activity | |||
| super.onResume(); | |||
| resumeApp(); | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| for (int i = 0; i < viewHolder.getChildCount(); ++i) | |||
| { | |||
| if (viewHolder.getChildAt (i) instanceof ComponentPeerView) | |||
| ((ComponentPeerView) viewHolder.getChildAt (i)).appResumed(); | |||
| } | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appResumed(); | |||
| } | |||
| @Override | |||
| @@ -1368,11 +1375,14 @@ public class AudioPerformanceTest extends Activity | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
| viewHolder.addView (v); | |||
| addAppPausedResumedListener (v, host); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| removeAppPausedResumedListener (view, view.host); | |||
| view.host = 0; | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| @@ -1590,9 +1600,28 @@ public class AudioPerformanceTest extends Activity | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public interface AppPausedResumedListener | |||
| { | |||
| void appPaused(); | |||
| void appResumed(); | |||
| } | |||
| private Map<Long, AppPausedResumedListener> appPausedResumedListeners; | |||
| public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.put (new Long (listenerHost), l); | |||
| } | |||
| public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.remove (new Long (listenerHost)); | |||
| } | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| implements View.OnFocusChangeListener, AppPausedResumedListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_, long host) | |||
| { | |||
| @@ -1940,13 +1969,25 @@ public class AudioPerformanceTest extends Activity | |||
| } | |||
| //============================================================================== | |||
| private native void handleAppPaused (long host); | |||
| private native void handleAppResumed (long host); | |||
| @Override | |||
| public void appPaused() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| handleAppPaused (host); | |||
| } | |||
| @Override | |||
| public void appResumed() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| handleAppResumed (host); | |||
| } | |||
| } | |||
| @@ -2616,6 +2657,175 @@ public class AudioPerformanceTest extends Activity | |||
| private final Object hostLock = new Object(); | |||
| } | |||
| //============================================================================== | |||
| public class CameraDeviceStateCallback extends CameraDevice.StateCallback | |||
| { | |||
| private native void cameraDeviceStateClosed (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateDisconnected (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateError (long host, CameraDevice camera, int error); | |||
| private native void cameraDeviceStateOpened (long host, CameraDevice camera); | |||
| CameraDeviceStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onClosed (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateClosed (host, camera); | |||
| } | |||
| @Override | |||
| public void onDisconnected (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateDisconnected (host, camera); | |||
| } | |||
| @Override | |||
| public void onError (CameraDevice camera, int error) | |||
| { | |||
| cameraDeviceStateError (host, camera, error); | |||
| } | |||
| @Override | |||
| public void onOpened (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateOpened (host, camera); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback | |||
| { | |||
| private native void cameraCaptureSessionActive (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionReady (long host, CameraCaptureSession session); | |||
| CameraCaptureSessionStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onActive (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionActive (host, session); | |||
| } | |||
| @Override | |||
| public void onClosed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionClosed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigureFailed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigureFailed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigured (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigured (host, session); | |||
| } | |||
| @Override | |||
| public void onReady (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionReady (host, session); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback | |||
| { | |||
| private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result); | |||
| private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure); | |||
| private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult); | |||
| private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber); | |||
| private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId); | |||
| private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber); | |||
| CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview) | |||
| { | |||
| host = hostToUse; | |||
| preview = shouldBePreview; | |||
| } | |||
| @Override | |||
| public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request, | |||
| TotalCaptureResult result) | |||
| { | |||
| cameraCaptureSessionCaptureCompleted (host, preview, session, request, result); | |||
| } | |||
| @Override | |||
| public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) | |||
| { | |||
| cameraCaptureSessionCaptureFailed (host, preview, session, request, failure); | |||
| } | |||
| @Override | |||
| public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request, | |||
| CaptureResult partialResult) | |||
| { | |||
| cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber); | |||
| } | |||
| @Override | |||
| public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp, | |||
| long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber); | |||
| } | |||
| private long host; | |||
| private boolean preview; | |||
| } | |||
| //============================================================================== | |||
| public class JuceOrientationEventListener extends OrientationEventListener | |||
| { | |||
| private native void deviceOrientationChanged (long host, int orientation); | |||
| public JuceOrientationEventListener (long hostToUse, Context context, int rate) | |||
| { | |||
| super (context, rate); | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onOrientationChanged (int orientation) | |||
| { | |||
| deviceOrientationChanged (host, orientation); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| @@ -596,6 +596,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -817,6 +817,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -147,6 +147,10 @@ | |||
| //#define JUCE_PLUGINHOST_AU 0 | |||
| #endif | |||
| #ifndef JUCE_PLUGINHOST_LADSPA | |||
| //#define JUCE_PLUGINHOST_LADSPA 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_audio_utils flags: | |||
| @@ -415,6 +415,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" | |||
| "../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" | |||
| "../../../../../modules/juce_core/containers/juce_SortedSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.h" | |||
| @@ -1250,6 +1251,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" | |||
| "../../../../../modules/juce_video/capture/juce_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_android_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" | |||
| "../../../../../modules/juce_video/native/juce_mac_Video.h" | |||
| "../../../../../modules/juce_video/native/juce_win32_CameraDevice.h" | |||
| @@ -1653,6 +1655,7 @@ set_source_files_properties("../../../../../modules/juce_core/containers/juce_Pr | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SortedSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -2488,6 +2491,7 @@ set_source_files_properties("../../../../../modules/juce_opengl/juce_opengl.h" P | |||
| set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_video/native/juce_win32_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -30,6 +30,7 @@ import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.content.pm.PackageInfo; | |||
| import android.content.pm.PackageManager; | |||
| import android.hardware.camera2.*; | |||
| import android.net.http.SslError; | |||
| import android.net.Uri; | |||
| import android.os.Bundle; | |||
| @@ -119,6 +120,7 @@ public class AudioPluginHost extends Activity | |||
| private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
| private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3; | |||
| private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4; | |||
| private static final int JUCE_PERMISSIONS_CAMERA = 5; | |||
| private static String getAndroidPermissionName (int permissionID) | |||
| { | |||
| @@ -129,6 +131,7 @@ public class AudioPluginHost extends Activity | |||
| // 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; | |||
| case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA; | |||
| } | |||
| // unknown permission ID! | |||
| @@ -1205,6 +1208,7 @@ public class AudioPluginHost extends Activity | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
| appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>(); | |||
| } | |||
| @Override | |||
| @@ -1221,6 +1225,11 @@ public class AudioPluginHost extends Activity | |||
| { | |||
| suspendApp(); | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appPaused(); | |||
| try | |||
| { | |||
| Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down | |||
| @@ -1236,12 +1245,10 @@ public class AudioPluginHost extends Activity | |||
| super.onResume(); | |||
| resumeApp(); | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| for (int i = 0; i < viewHolder.getChildCount(); ++i) | |||
| { | |||
| if (viewHolder.getChildAt (i) instanceof ComponentPeerView) | |||
| ((ComponentPeerView) viewHolder.getChildAt (i)).appResumed(); | |||
| } | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appResumed(); | |||
| } | |||
| @Override | |||
| @@ -1368,11 +1375,14 @@ public class AudioPluginHost extends Activity | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
| viewHolder.addView (v); | |||
| addAppPausedResumedListener (v, host); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| removeAppPausedResumedListener (view, view.host); | |||
| view.host = 0; | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| @@ -1590,9 +1600,28 @@ public class AudioPluginHost extends Activity | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public interface AppPausedResumedListener | |||
| { | |||
| void appPaused(); | |||
| void appResumed(); | |||
| } | |||
| private Map<Long, AppPausedResumedListener> appPausedResumedListeners; | |||
| public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.put (new Long (listenerHost), l); | |||
| } | |||
| public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.remove (new Long (listenerHost)); | |||
| } | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| implements View.OnFocusChangeListener, AppPausedResumedListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_, long host) | |||
| { | |||
| @@ -1940,13 +1969,25 @@ public class AudioPluginHost extends Activity | |||
| } | |||
| //============================================================================== | |||
| private native void handleAppPaused (long host); | |||
| private native void handleAppResumed (long host); | |||
| @Override | |||
| public void appPaused() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| handleAppPaused (host); | |||
| } | |||
| @Override | |||
| public void appResumed() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| handleAppResumed (host); | |||
| } | |||
| } | |||
| @@ -2616,6 +2657,175 @@ public class AudioPluginHost extends Activity | |||
| private final Object hostLock = new Object(); | |||
| } | |||
| //============================================================================== | |||
| public class CameraDeviceStateCallback extends CameraDevice.StateCallback | |||
| { | |||
| private native void cameraDeviceStateClosed (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateDisconnected (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateError (long host, CameraDevice camera, int error); | |||
| private native void cameraDeviceStateOpened (long host, CameraDevice camera); | |||
| CameraDeviceStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onClosed (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateClosed (host, camera); | |||
| } | |||
| @Override | |||
| public void onDisconnected (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateDisconnected (host, camera); | |||
| } | |||
| @Override | |||
| public void onError (CameraDevice camera, int error) | |||
| { | |||
| cameraDeviceStateError (host, camera, error); | |||
| } | |||
| @Override | |||
| public void onOpened (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateOpened (host, camera); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback | |||
| { | |||
| private native void cameraCaptureSessionActive (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionReady (long host, CameraCaptureSession session); | |||
| CameraCaptureSessionStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onActive (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionActive (host, session); | |||
| } | |||
| @Override | |||
| public void onClosed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionClosed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigureFailed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigureFailed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigured (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigured (host, session); | |||
| } | |||
| @Override | |||
| public void onReady (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionReady (host, session); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback | |||
| { | |||
| private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result); | |||
| private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure); | |||
| private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult); | |||
| private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber); | |||
| private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId); | |||
| private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber); | |||
| CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview) | |||
| { | |||
| host = hostToUse; | |||
| preview = shouldBePreview; | |||
| } | |||
| @Override | |||
| public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request, | |||
| TotalCaptureResult result) | |||
| { | |||
| cameraCaptureSessionCaptureCompleted (host, preview, session, request, result); | |||
| } | |||
| @Override | |||
| public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) | |||
| { | |||
| cameraCaptureSessionCaptureFailed (host, preview, session, request, failure); | |||
| } | |||
| @Override | |||
| public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request, | |||
| CaptureResult partialResult) | |||
| { | |||
| cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber); | |||
| } | |||
| @Override | |||
| public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp, | |||
| long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber); | |||
| } | |||
| private long host; | |||
| private boolean preview; | |||
| } | |||
| //============================================================================== | |||
| public class JuceOrientationEventListener extends OrientationEventListener | |||
| { | |||
| private native void deviceOrientationChanged (long host, int orientation); | |||
| public JuceOrientationEventListener (long hostToUse, Context context, int rate) | |||
| { | |||
| super (context, rate); | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onOrientationChanged (int orientation) | |||
| { | |||
| deviceOrientationChanged (host, orientation); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| @@ -601,6 +601,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2433,6 +2436,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -874,6 +874,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4062,6 +4065,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -601,6 +601,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2433,6 +2436,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -874,6 +874,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4062,6 +4065,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -601,6 +601,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2433,6 +2436,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -874,6 +874,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4062,6 +4065,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -164,6 +164,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -91,6 +91,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -403,6 +403,7 @@ add_library( ${BINARY_NAME} | |||
| "../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" | |||
| "../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" | |||
| "../../../../../modules/juce_core/containers/juce_SortedSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_SparseSet.h" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.cpp" | |||
| "../../../../../modules/juce_core/containers/juce_Variant.h" | |||
| @@ -1645,6 +1646,7 @@ set_source_files_properties("../../../../../modules/juce_core/containers/juce_Pr | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ReferenceCountedArray.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_ScopedValueSetter.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SortedSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_SparseSet.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.cpp" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| set_source_files_properties("../../../../../modules/juce_core/containers/juce_Variant.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
| @@ -113,6 +113,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
| private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3; | |||
| private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4; | |||
| private static final int JUCE_PERMISSIONS_CAMERA = 5; | |||
| private static String getAndroidPermissionName (int permissionID) | |||
| { | |||
| @@ -123,6 +124,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| // 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; | |||
| case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA; | |||
| } | |||
| // unknown permission ID! | |||
| @@ -273,6 +275,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
| appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>(); | |||
| } | |||
| @Override | |||
| @@ -289,6 +292,11 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| { | |||
| suspendApp(); | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appPaused(); | |||
| try | |||
| { | |||
| Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down | |||
| @@ -304,12 +312,10 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| super.onResume(); | |||
| resumeApp(); | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| for (int i = 0; i < viewHolder.getChildCount(); ++i) | |||
| { | |||
| if (viewHolder.getChildAt (i) instanceof ComponentPeerView) | |||
| ((ComponentPeerView) viewHolder.getChildAt (i)).appResumed(); | |||
| } | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appResumed(); | |||
| } | |||
| @Override | |||
| @@ -436,11 +442,14 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
| viewHolder.addView (v); | |||
| addAppPausedResumedListener (v, host); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| removeAppPausedResumedListener (view, view.host); | |||
| view.host = 0; | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| @@ -658,9 +667,28 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public interface AppPausedResumedListener | |||
| { | |||
| void appPaused(); | |||
| void appResumed(); | |||
| } | |||
| private Map<Long, AppPausedResumedListener> appPausedResumedListeners; | |||
| public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.put (new Long (listenerHost), l); | |||
| } | |||
| public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.remove (new Long (listenerHost)); | |||
| } | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| implements View.OnFocusChangeListener, AppPausedResumedListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_, long host) | |||
| { | |||
| @@ -1008,13 +1036,25 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| } | |||
| //============================================================================== | |||
| private native void handleAppPaused (long host); | |||
| private native void handleAppResumed (long host); | |||
| @Override | |||
| public void appPaused() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| handleAppPaused (host); | |||
| } | |||
| @Override | |||
| public void appResumed() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| handleAppResumed (host); | |||
| } | |||
| } | |||
| @@ -1656,6 +1696,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| private final Object hostLock = new Object(); | |||
| } | |||
| //============================================================================== | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| @@ -596,6 +596,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -847,6 +847,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -596,6 +596,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -847,6 +847,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -150,6 +150,10 @@ | |||
| //#define JUCE_PLUGINHOST_AU 0 | |||
| #endif | |||
| #ifndef JUCE_PLUGINHOST_LADSPA | |||
| //#define JUCE_PLUGINHOST_LADSPA 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_audio_utils flags: | |||
| @@ -276,6 +276,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -541,6 +541,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -276,6 +276,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -541,6 +541,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -276,6 +276,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -541,6 +541,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -103,7 +103,7 @@ public: | |||
| ValueWithDefault androidJavaLibs, androidRepositories, androidDependencies, androidScreenOrientation, androidActivityClass, | |||
| androidActivitySubClassName, androidActivityBaseClassName, androidManifestCustomXmlElements, androidVersionCode, | |||
| androidMinimumSDK, androidTheme, androidSharedLibraries, androidStaticLibraries, androidExtraAssetsFolder, | |||
| androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidBluetoothNeeded, androidExternalReadPermission, | |||
| androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidCameraNeeded, androidBluetoothNeeded, androidExternalReadPermission, | |||
| androidExternalWritePermission, androidInAppBillingPermission, androidVibratePermission,androidOtherPermissions, | |||
| androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore, | |||
| androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, androidPluginVersion, buildToolsVersion; | |||
| @@ -128,6 +128,7 @@ public: | |||
| androidOboeRepositoryPath (settings, Ids::androidOboeRepositoryPath, getUndoManager()), | |||
| androidInternetNeeded (settings, Ids::androidInternetNeeded, getUndoManager(), true), | |||
| androidMicNeeded (settings, Ids::microphonePermissionNeeded, getUndoManager(), false), | |||
| androidCameraNeeded (settings, Ids::cameraPermissionNeeded, getUndoManager(), false), | |||
| androidBluetoothNeeded (settings, Ids::androidBluetoothNeeded, getUndoManager(), true), | |||
| androidExternalReadPermission (settings, Ids::androidExternalReadNeeded, getUndoManager(), true), | |||
| androidExternalWritePermission (settings, Ids::androidExternalWriteNeeded, getUndoManager(), true), | |||
| @@ -920,6 +921,9 @@ private: | |||
| props.add (new ChoicePropertyComponent (androidMicNeeded, "Audio Input Required"), | |||
| "If enabled, this will set the android.permission.RECORD_AUDIO flag in the manifest."); | |||
| props.add (new ChoicePropertyComponent (androidCameraNeeded, "Camera Required"), | |||
| "If enabled, this will set the android.permission.CAMERA flag in the manifest."); | |||
| props.add (new ChoicePropertyComponent (androidBluetoothNeeded, "Bluetooth permissions Required"), | |||
| "If enabled, this will set the android.permission.BLUETOOTH and android.permission.BLUETOOTH_ADMIN flag in the manifest. This is required for Bluetooth MIDI on Android."); | |||
| @@ -1034,25 +1038,92 @@ private: | |||
| createDirectoryOrThrow (targetFolder); | |||
| auto activityCode = getActivityCode (javaSourceFolder, className, package); | |||
| auto javaDestFile = targetFolder.getChildFile (className + ".java"); | |||
| overwriteFileIfDifferentOrThrow (javaDestFile, activityCode); | |||
| } | |||
| String getActivityCode (const File& javaSourceFolder, const String& className, const String& package) const | |||
| { | |||
| auto runtimePermissionsCode = getRuntimePermissionsCode (javaSourceFolder, className); | |||
| auto midiCode = getMidiCode (javaSourceFolder, className); | |||
| auto webViewCode = getWebViewCode (javaSourceFolder); | |||
| auto cameraCode = getCameraCode (javaSourceFolder); | |||
| auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java"); | |||
| auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString()); | |||
| { | |||
| MemoryOutputStream newFile; | |||
| for (auto& line : javaSourceLines) | |||
| { | |||
| if (line.contains ("$$JuceAndroidMidiImports$$")) | |||
| newFile << midiCode.imports; | |||
| else if (line.contains ("$$JuceAndroidMidiCode$$")) | |||
| newFile << midiCode.main; | |||
| else if (line.contains ("$$JuceAndroidRuntimePermissionsCode$$")) | |||
| newFile << runtimePermissionsCode; | |||
| else if (line.contains ("$$JuceAndroidWebViewImports$$")) | |||
| newFile << webViewCode.imports; | |||
| else if (line.contains ("$$JuceAndroidWebViewNativeCode$$")) | |||
| newFile << webViewCode.native; | |||
| else if (line.contains ("$$JuceAndroidWebViewCode$$")) | |||
| newFile << webViewCode.main; | |||
| else if (line.contains ("$$JuceAndroidCameraImports$$")) | |||
| newFile << cameraCode.imports; | |||
| else if (line.contains ("$$JuceAndroidCameraCode$$")) | |||
| newFile << cameraCode.main; | |||
| else | |||
| newFile << line.replace ("$$JuceAppActivityBaseClass$$", androidActivityBaseClassName.get().toString()) | |||
| .replace ("JuceAppActivity", className) | |||
| .replace ("package com.juce;", "package " + package + ";") << newLine; | |||
| } | |||
| javaSourceLines = StringArray::fromLines (newFile.toString()); | |||
| } | |||
| while (javaSourceLines.size() > 2 | |||
| && javaSourceLines[javaSourceLines.size() - 1].trim().isEmpty() | |||
| && javaSourceLines[javaSourceLines.size() - 2].trim().isEmpty()) | |||
| javaSourceLines.remove (javaSourceLines.size() - 1); | |||
| return javaSourceLines.joinIntoString (newLine); | |||
| } | |||
| String getRuntimePermissionsCode (const File& javaSourceFolder, const String& className) const | |||
| { | |||
| if (static_cast<int> (androidMinimumSDK.get()) >= 23) | |||
| { | |||
| auto javaRuntimePermissions = javaSourceFolder.getChildFile ("AndroidRuntimePermissions.java"); | |||
| return javaRuntimePermissions.loadFileAsString().replace ("JuceAppActivity", className); | |||
| } | |||
| return {}; | |||
| } | |||
| struct MidiCode | |||
| { | |||
| String imports; | |||
| String main; | |||
| }; | |||
| String juceMidiCode, juceMidiImports, juceRuntimePermissionsCode; | |||
| MidiCode getMidiCode (const File& javaSourceFolder, const String& className) const | |||
| { | |||
| String juceMidiCode, juceMidiImports; | |||
| juceMidiImports << newLine; | |||
| if (static_cast<int> (androidMinimumSDK.get()) >= 23) | |||
| { | |||
| auto javaAndroidMidi = javaSourceFolder.getChildFile ("AndroidMidi.java"); | |||
| auto javaRuntimePermissions = javaSourceFolder.getChildFile ("AndroidRuntimePermissions.java"); | |||
| juceMidiImports << "import android.media.midi.*;" << newLine | |||
| << "import android.bluetooth.*;" << newLine | |||
| << "import android.bluetooth.le.*;" << newLine; | |||
| juceMidiCode = javaAndroidMidi.loadFileAsString().replace ("JuceAppActivity", className); | |||
| juceRuntimePermissionsCode = javaRuntimePermissions.loadFileAsString().replace ("JuceAppActivity", className); | |||
| } | |||
| else | |||
| { | |||
| @@ -1061,6 +1132,18 @@ private: | |||
| .replace ("JuceAppActivity", className); | |||
| } | |||
| return { juceMidiImports, juceMidiCode }; | |||
| } | |||
| struct WebViewCode | |||
| { | |||
| String imports; | |||
| String native; | |||
| String main; | |||
| }; | |||
| WebViewCode getWebViewCode (const File& javaSourceFolder) const | |||
| { | |||
| String juceWebViewImports, juceWebViewCodeNative, juceWebViewCode; | |||
| if (static_cast<int> (androidMinimumSDK.get()) >= 23) | |||
| @@ -1106,41 +1189,32 @@ private: | |||
| } | |||
| } | |||
| auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java"); | |||
| auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString()); | |||
| return { juceWebViewImports, juceWebViewCodeNative, juceWebViewCode }; | |||
| } | |||
| { | |||
| MemoryOutputStream newFile; | |||
| struct CameraCode | |||
| { | |||
| String imports; | |||
| String main; | |||
| }; | |||
| for (auto& line : javaSourceLines) | |||
| { | |||
| if (line.contains ("$$JuceAndroidMidiImports$$")) | |||
| newFile << juceMidiImports; | |||
| else if (line.contains ("$$JuceAndroidMidiCode$$")) | |||
| newFile << juceMidiCode; | |||
| else if (line.contains ("$$JuceAndroidRuntimePermissionsCode$$")) | |||
| newFile << juceRuntimePermissionsCode; | |||
| else if (line.contains ("$$JuceAndroidWebViewImports$$")) | |||
| newFile << juceWebViewImports; | |||
| else if (line.contains ("$$JuceAndroidWebViewNativeCode$$")) | |||
| newFile << juceWebViewCodeNative; | |||
| else if (line.contains ("$$JuceAndroidWebViewCode$$")) | |||
| newFile << juceWebViewCode; | |||
| else | |||
| newFile << line.replace ("$$JuceAppActivityBaseClass$$", androidActivityBaseClassName.get().toString()) | |||
| .replace ("JuceAppActivity", className) | |||
| .replace ("package com.juce;", "package " + package + ";") << newLine; | |||
| } | |||
| CameraCode getCameraCode (const File& javaSourceFolder) const | |||
| { | |||
| String juceCameraImports, juceCameraCode; | |||
| javaSourceLines = StringArray::fromLines (newFile.toString()); | |||
| } | |||
| if (static_cast<int> (androidMinimumSDK.get()) >= 21) | |||
| juceCameraImports << "import android.hardware.camera2.*;" << newLine; | |||
| while (javaSourceLines.size() > 2 | |||
| && javaSourceLines[javaSourceLines.size() - 1].trim().isEmpty() | |||
| && javaSourceLines[javaSourceLines.size() - 2].trim().isEmpty()) | |||
| javaSourceLines.remove (javaSourceLines.size() - 1); | |||
| auto javaCameraFile = javaSourceFolder.getChildFile ("AndroidCamera.java"); | |||
| auto juceCameraCodeAll = javaCameraFile.loadFileAsString(); | |||
| overwriteFileIfDifferentOrThrow (javaDestFile, javaSourceLines.joinIntoString (newLine)); | |||
| if (static_cast<int> (androidMinimumSDK.get()) >= 21) | |||
| { | |||
| juceCameraCode << juceCameraCodeAll.fromFirstOccurrenceOf ("$$CameraApi21", false, false) | |||
| .upToFirstOccurrenceOf ("CameraApi21$$", false, false); | |||
| } | |||
| return { juceCameraImports, juceCameraCode }; | |||
| } | |||
| void copyAdditionalJavaFiles (const File& sourceFolder, const File& targetFolder) const | |||
| @@ -1882,6 +1956,9 @@ private: | |||
| if (androidMicNeeded.get()) | |||
| s.add ("android.permission.RECORD_AUDIO"); | |||
| if (androidCameraNeeded.get()) | |||
| s.add ("android.permission.CAMERA"); | |||
| if (androidBluetoothNeeded.get()) | |||
| { | |||
| s.add ("android.permission.BLUETOOTH"); | |||
| @@ -73,6 +73,9 @@ public: | |||
| microphonePermissionNeededValue (settings, Ids::microphonePermissionNeeded, getUndoManager()), | |||
| microphonePermissionsTextValue (settings, Ids::microphonePermissionsText, getUndoManager(), | |||
| "This is an audio app which requires audio input. If you do not have a USB audio interface connected it will use the microphone."), | |||
| cameraPermissionNeededValue (settings, Ids::cameraPermissionNeeded, getUndoManager()), | |||
| cameraPermissionTextValue (settings, Ids::cameraPermissionText, getUndoManager(), | |||
| "This app requires camera usage to function properly."), | |||
| uiFileSharingEnabledValue (settings, Ids::UIFileSharingEnabled, getUndoManager()), | |||
| uiSupportsDocumentBrowserValue (settings, Ids::UISupportsDocumentBrowser, getUndoManager()), | |||
| uiStatusBarHiddenValue (settings, Ids::UIStatusBarHidden, getUndoManager()), | |||
| @@ -124,6 +127,9 @@ public: | |||
| bool isMicrophonePermissionEnabled() const { return microphonePermissionNeededValue.get(); } | |||
| String getMicrophonePermissionsTextString() const { return microphonePermissionsTextValue.get(); } | |||
| bool isCameraPermissionEnabled() const { return cameraPermissionNeededValue.get(); } | |||
| String getCameraPermissionTextString() const { return cameraPermissionTextValue.get(); } | |||
| bool isInAppPurchasesEnabled() const { return iosInAppPurchasesValue.get(); } | |||
| bool isBackgroundAudioEnabled() const { return iosBackgroundAudioValue.get(); } | |||
| bool isBackgroundBleEnabled() const { return iosBackgroundBleValue.get(); } | |||
| @@ -236,6 +242,14 @@ public: | |||
| props.add (new TextPropertyComponentWithEnablement (microphonePermissionsTextValue, microphonePermissionNeededValue, | |||
| "Microphone Access Text", 1024, false), | |||
| "A short description of why your app requires microphone access."); | |||
| props.add (new ChoicePropertyComponent (cameraPermissionNeededValue, "Camera Access"), | |||
| "Enable this to allow your app to use the camera. " | |||
| "The user of your app will be prompted to grant camera access permissions."); | |||
| props.add (new TextPropertyComponentWithEnablement (cameraPermissionTextValue, cameraPermissionNeededValue, | |||
| "Camera Access Text", 1024, false), | |||
| "A short description of why your app requires camera access."); | |||
| } | |||
| else if (projectType.isGUIApplication()) | |||
| { | |||
| @@ -1280,9 +1294,13 @@ public: | |||
| if (owner.iOS) | |||
| { | |||
| addPlistDictionaryKeyBool (dict, "LSRequiresIPhoneOS", true); | |||
| if (owner.isMicrophonePermissionEnabled()) | |||
| addPlistDictionaryKey (dict, "NSMicrophoneUsageDescription", owner.getMicrophonePermissionsTextString()); | |||
| if (owner.isCameraPermissionEnabled()) | |||
| addPlistDictionaryKey (dict, "NSCameraUsageDescription", owner.getCameraPermissionTextString()); | |||
| if (type != AudioUnitv3PlugIn) | |||
| addPlistDictionaryKeyBool (dict, "UIViewControllerBasedStatusBarAppearance", false); | |||
| } | |||
| @@ -1682,7 +1700,8 @@ private: | |||
| ValueWithDefault customPListValue, pListPrefixHeaderValue, pListPreprocessValue, extraFrameworksValue, postbuildCommandValue, | |||
| prebuildCommandValue, duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue, | |||
| iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue, microphonePermissionNeededValue, microphonePermissionsTextValue, | |||
| iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue, | |||
| microphonePermissionNeededValue, microphonePermissionsTextValue, cameraPermissionNeededValue, cameraPermissionTextValue, | |||
| uiFileSharingEnabledValue, uiSupportsDocumentBrowserValue, uiStatusBarHiddenValue, documentExtensionsValue, iosInAppPurchasesValue, | |||
| iosBackgroundAudioValue, iosBackgroundBleValue, iosPushNotificationsValue, iosAppGroupsValue, iCloudPermissionsValue, | |||
| iosDevelopmentTeamIDValue, iosAppGroupsIDValue, keepCustomXcodeSchemesValue, useHeaderMapValue; | |||
| @@ -2299,6 +2318,9 @@ private: | |||
| if (iOS && isPushNotificationsEnabled()) | |||
| xcodeFrameworks.addIfNotAlreadyThere ("UserNotifications"); | |||
| if (isiOS() && project.getConfigFlag ("JUCE_USE_CAMERA").get()) | |||
| xcodeFrameworks.addIfNotAlreadyThere ("ImageIO"); | |||
| xcodeFrameworks.addTokens (getExtraFrameworksString(), ",;", "\"'"); | |||
| xcodeFrameworks.trim(); | |||
| @@ -178,6 +178,8 @@ namespace Ids | |||
| DECLARE_ID (overwriteOnSave); | |||
| DECLARE_ID (microphonePermissionNeeded); | |||
| DECLARE_ID (microphonePermissionsText); | |||
| DECLARE_ID (cameraPermissionNeeded); | |||
| DECLARE_ID (cameraPermissionText); | |||
| DECLARE_ID (androidJavaLibs); | |||
| DECLARE_ID (androidRepositories); | |||
| DECLARE_ID (androidDependencies); | |||
| @@ -628,6 +628,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2638,6 +2641,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -949,6 +949,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4467,6 +4470,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -155,6 +155,10 @@ | |||
| //#define JUCE_PLUGINHOST_AU 0 | |||
| #endif | |||
| #ifndef JUCE_PLUGINHOST_LADSPA | |||
| //#define JUCE_PLUGINHOST_LADSPA 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_audio_utils flags: | |||
| @@ -595,6 +595,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <ExcludedFromBuild>true</ExcludedFromBuild> | |||
| </ClCompile> | |||
| @@ -2420,6 +2423,7 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_win32_CameraDevice.h"/> | |||
| @@ -844,6 +844,9 @@ | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_PropertySet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_SparseSet.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\..\..\..\modules\juce_core\containers\juce_Variant.cpp"> | |||
| <Filter>JUCE Modules\juce_core\containers</Filter> | |||
| </ClCompile> | |||
| @@ -4011,6 +4014,9 @@ | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"> | |||
| <Filter>JUCE Modules\juce_video\native</Filter> | |||
| </ClInclude> | |||
| @@ -150,6 +150,10 @@ | |||
| //#define JUCE_PLUGINHOST_AU 0 | |||
| #endif | |||
| #ifndef JUCE_PLUGINHOST_LADSPA | |||
| //#define JUCE_PLUGINHOST_LADSPA 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_audio_utils flags: | |||
| @@ -221,22 +221,6 @@ public: | |||
| } | |||
| private: | |||
| static StringArray javaStringArrayToJuce (jobjectArray jStrings) | |||
| { | |||
| StringArray retval; | |||
| JNIEnv* env = getEnv(); | |||
| const int count = env->GetArrayLength (jStrings); | |||
| for (int i = 0; i < count; ++i) | |||
| { | |||
| LocalRef<jstring> string ((jstring) env->GetObjectArrayElement (jStrings, i)); | |||
| retval.add (juceString (string)); | |||
| } | |||
| return retval; | |||
| } | |||
| GlobalRef deviceManager; | |||
| }; | |||
| @@ -83,7 +83,10 @@ public: | |||
| readExternalStorage = 3, | |||
| /** Permission to write to external storage such as SD cards */ | |||
| writeExternalStorage = 4 | |||
| writeExternalStorage = 4, | |||
| /** Permission to use camera */ | |||
| camera = 5 | |||
| }; | |||
| //============================================================================== | |||
| @@ -0,0 +1,169 @@ | |||
| $$CameraApi21 | |||
| //============================================================================== | |||
| public class CameraDeviceStateCallback extends CameraDevice.StateCallback | |||
| { | |||
| private native void cameraDeviceStateClosed (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateDisconnected (long host, CameraDevice camera); | |||
| private native void cameraDeviceStateError (long host, CameraDevice camera, int error); | |||
| private native void cameraDeviceStateOpened (long host, CameraDevice camera); | |||
| CameraDeviceStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onClosed (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateClosed (host, camera); | |||
| } | |||
| @Override | |||
| public void onDisconnected (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateDisconnected (host, camera); | |||
| } | |||
| @Override | |||
| public void onError (CameraDevice camera, int error) | |||
| { | |||
| cameraDeviceStateError (host, camera, error); | |||
| } | |||
| @Override | |||
| public void onOpened (CameraDevice camera) | |||
| { | |||
| cameraDeviceStateOpened (host, camera); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback | |||
| { | |||
| private native void cameraCaptureSessionActive (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session); | |||
| private native void cameraCaptureSessionReady (long host, CameraCaptureSession session); | |||
| CameraCaptureSessionStateCallback (long hostToUse) | |||
| { | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onActive (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionActive (host, session); | |||
| } | |||
| @Override | |||
| public void onClosed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionClosed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigureFailed (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigureFailed (host, session); | |||
| } | |||
| @Override | |||
| public void onConfigured (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionConfigured (host, session); | |||
| } | |||
| @Override | |||
| public void onReady (CameraCaptureSession session) | |||
| { | |||
| cameraCaptureSessionReady (host, session); | |||
| } | |||
| private long host; | |||
| } | |||
| //============================================================================== | |||
| public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback | |||
| { | |||
| private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result); | |||
| private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure); | |||
| private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult); | |||
| private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber); | |||
| private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId); | |||
| private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber); | |||
| CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview) | |||
| { | |||
| host = hostToUse; | |||
| preview = shouldBePreview; | |||
| } | |||
| @Override | |||
| public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request, | |||
| TotalCaptureResult result) | |||
| { | |||
| cameraCaptureSessionCaptureCompleted (host, preview, session, request, result); | |||
| } | |||
| @Override | |||
| public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) | |||
| { | |||
| cameraCaptureSessionCaptureFailed (host, preview, session, request, failure); | |||
| } | |||
| @Override | |||
| public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request, | |||
| CaptureResult partialResult) | |||
| { | |||
| cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId); | |||
| } | |||
| @Override | |||
| public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber); | |||
| } | |||
| @Override | |||
| public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp, | |||
| long frameNumber) | |||
| { | |||
| cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber); | |||
| } | |||
| private long host; | |||
| private boolean preview; | |||
| } | |||
| //============================================================================== | |||
| public class JuceOrientationEventListener extends OrientationEventListener | |||
| { | |||
| private native void deviceOrientationChanged (long host, int orientation); | |||
| public JuceOrientationEventListener (long hostToUse, Context context, int rate) | |||
| { | |||
| super (context, rate); | |||
| host = hostToUse; | |||
| } | |||
| @Override | |||
| public void onOrientationChanged (int orientation) | |||
| { | |||
| deviceOrientationChanged (host, orientation); | |||
| } | |||
| private long host; | |||
| } | |||
| CameraApi21$$ | |||
| @@ -30,6 +30,7 @@ import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.content.pm.PackageInfo; | |||
| import android.content.pm.PackageManager; | |||
| $$JuceAndroidCameraImports$$ // If you get an error here, you need to re-save your project with the Projucer! | |||
| import android.net.http.SslError; | |||
| import android.net.Uri; | |||
| import android.os.Bundle; | |||
| @@ -114,6 +115,7 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2; | |||
| private static final int JUCE_PERMISSIONS_READ_EXTERNAL_STORAGE = 3; | |||
| private static final int JUCE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 4; | |||
| private static final int JUCE_PERMISSIONS_CAMERA = 5; | |||
| private static String getAndroidPermissionName (int permissionID) | |||
| { | |||
| @@ -124,6 +126,7 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| // 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; | |||
| case JUCE_PERMISSIONS_CAMERA: return Manifest.permission.CAMERA; | |||
| } | |||
| // unknown permission ID! | |||
| @@ -191,6 +194,7 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||
| appPausedResumedListeners = new HashMap<Long, AppPausedResumedListener>(); | |||
| } | |||
| @Override | |||
| @@ -207,6 +211,11 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| { | |||
| suspendApp(); | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appPaused(); | |||
| try | |||
| { | |||
| Thread.sleep (1000); // This is a bit of a hack to avoid some hard-to-track-down | |||
| @@ -222,12 +231,10 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| super.onResume(); | |||
| resumeApp(); | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| for (int i = 0; i < viewHolder.getChildCount(); ++i) | |||
| { | |||
| if (viewHolder.getChildAt (i) instanceof ComponentPeerView) | |||
| ((ComponentPeerView) viewHolder.getChildAt (i)).appResumed(); | |||
| } | |||
| Long[] keys = appPausedResumedListeners.keySet().toArray (new Long[appPausedResumedListeners.keySet().size()]); | |||
| for (Long k : keys) | |||
| appPausedResumedListeners.get (k).appResumed(); | |||
| } | |||
| @Override | |||
| @@ -354,11 +361,14 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque, host); | |||
| viewHolder.addView (v); | |||
| addAppPausedResumedListener (v, host); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| removeAppPausedResumedListener (view, view.host); | |||
| view.host = 0; | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| @@ -576,9 +586,28 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public interface AppPausedResumedListener | |||
| { | |||
| void appPaused(); | |||
| void appResumed(); | |||
| } | |||
| private Map<Long, AppPausedResumedListener> appPausedResumedListeners; | |||
| public void addAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.put (new Long (listenerHost), l); | |||
| } | |||
| public void removeAppPausedResumedListener (AppPausedResumedListener l, long listenerHost) | |||
| { | |||
| appPausedResumedListeners.remove (new Long (listenerHost)); | |||
| } | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| implements View.OnFocusChangeListener, AppPausedResumedListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_, long host) | |||
| { | |||
| @@ -926,13 +955,25 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$ | |||
| } | |||
| //============================================================================== | |||
| private native void handleAppPaused (long host); | |||
| private native void handleAppResumed (long host); | |||
| @Override | |||
| public void appPaused() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| handleAppPaused (host); | |||
| } | |||
| @Override | |||
| public void appResumed() | |||
| { | |||
| if (host == 0) | |||
| return; | |||
| // Ensure that navigation/status bar visibility is correctly restored. | |||
| handleAppResumed (host); | |||
| } | |||
| } | |||
| @@ -1569,6 +1610,8 @@ $$JuceAndroidWebViewNativeCode$$ // If you get an error here, you need to re-sav | |||
| private final Object hostLock = new Object(); | |||
| } | |||
| $$JuceAndroidCameraCode$$ // If you get an error here, you need to re-save your project with the Projucer! | |||
| //============================================================================== | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| @@ -163,39 +163,6 @@ private: | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| inline String juceString (JNIEnv* env, jstring s) | |||
| { | |||
| if (s == 0) | |||
| return {}; | |||
| const char* const utf8 = env->GetStringUTFChars (s, nullptr); | |||
| CharPointer_UTF8 utf8CP (utf8); | |||
| const String result (utf8CP); | |||
| env->ReleaseStringUTFChars (s, utf8); | |||
| return result; | |||
| } | |||
| inline String juceString (jstring s) | |||
| { | |||
| return juceString (getEnv(), s); | |||
| } | |||
| inline LocalRef<jstring> javaString (const String& s) | |||
| { | |||
| return LocalRef<jstring> (getEnv()->NewStringUTF (s.toUTF8())); | |||
| } | |||
| inline LocalRef<jstring> javaStringFromChar (const juce_wchar c) | |||
| { | |||
| char utf8[8] = { 0 }; | |||
| CharPointer_UTF8 (utf8).write (c); | |||
| return LocalRef<jstring> (getEnv()->NewStringUTF (utf8)); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| class JNIClassBase | |||
| { | |||
| @@ -287,6 +254,7 @@ extern AndroidSystem android; | |||
| METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ | |||
| METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \ | |||
| METHOD (finish, "finish", "()V") \ | |||
| METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \ | |||
| METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \ | |||
| METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ | |||
| METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ | |||
| @@ -329,14 +297,21 @@ extern AndroidSystem android; | |||
| METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ | |||
| METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \ | |||
| METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \ | |||
| METHOD (addAppPausedResumedListener, "addAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") \ | |||
| METHOD (removeAppPausedResumedListener, "removeAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") | |||
| DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \ | |||
| METHOD (setPixel, "setPixel", "(III)V") | |||
| STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \ | |||
| STATICMETHOD (createBitmapFrom, "createBitmap", "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;") \ | |||
| METHOD (compress, "compress", "(Landroid/graphics/Bitmap$CompressFormat;ILjava/io/OutputStream;)Z") \ | |||
| METHOD (getHeight, "getHeight", "()I") \ | |||
| METHOD (getWidth, "getWidth", "()I") \ | |||
| METHOD (recycle, "recycle", "()V") \ | |||
| METHOD (setPixel, "setPixel", "(III)V") | |||
| DECLARE_JNI_CLASS (AndroidBitmap, "android/graphics/Bitmap"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -347,6 +322,12 @@ DECLARE_JNI_CLASS (AndroidBitmap, "android/graphics/Bitmap"); | |||
| DECLARE_JNI_CLASS (AndroidBitmapConfig, "android/graphics/Bitmap$Config"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (decodeByteArray, "decodeByteArray", "([BII)Landroid/graphics/Bitmap;") | |||
| DECLARE_JNI_CLASS (AndroidBitmapFactory, "android/graphics/BitmapFactory"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (dumpReferenceTables, "dumpReferenceTables", "()V") | |||
| @@ -355,6 +336,31 @@ DECLARE_JNI_CLASS (AndroidBitmapConfig, "android/graphics/Bitmap$Config"); | |||
| #define JUCE_LOG_JNI_REFERENCES_TABLE getEnv()->CallStaticVoidMethod (AndroidDebug, AndroidDebug.dumpReferenceTables); | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (getRotation, "getRotation", "()I") | |||
| DECLARE_JNI_CLASS (AndroidDisplay, "android/view/Display"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "()V") \ | |||
| METHOD (constructorWithLooper, "<init>", "(Landroid/os/Looper;)V") \ | |||
| METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \ | |||
| METHOD (postDelayed, "postDelayed", "(Ljava/lang/Runnable;J)Z") \ | |||
| DECLARE_JNI_CLASS (AndroidHandler, "android/os/Handler"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \ | |||
| METHOD (getLooper, "getLooper", "()Landroid/os/Looper;") \ | |||
| METHOD (join, "join", "()V") \ | |||
| METHOD (quitSafely, "quitSafely", "()Z") \ | |||
| METHOD (start, "start", "()V") | |||
| DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (createChooser, "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;") \ | |||
| METHOD (addCategory, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;") \ | |||
| @@ -382,8 +388,11 @@ DECLARE_JNI_CLASS (AndroidIntent, "android/content/Intent"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "()V") \ | |||
| METHOD (setValues, "setValues", "([F)V") \ | |||
| METHOD (constructor, "<init>", "()V") \ | |||
| METHOD (postRotate, "postRotate", "(FFF)Z") \ | |||
| METHOD (postScale, "postScale", "(FFFF)Z") \ | |||
| METHOD (postTranslate, "postTranslate", "(FF)Z") \ | |||
| METHOD (setValues, "setValues", "([F)V") | |||
| DECLARE_JNI_CLASS (AndroidMatrix, "android/graphics/Matrix"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -417,6 +426,12 @@ DECLARE_JNI_CLASS (AndroidPaint, "android/graphics/Paint"); | |||
| DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (toString, "toString", "()Ljava/lang/String;") | |||
| DECLARE_JNI_CLASS (AndroidRange, "android/util/Range"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "(IIII)V") \ | |||
| FIELD (left, "left", "I") \ | |||
| @@ -424,7 +439,7 @@ DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent"); | |||
| FIELD (top, "top", "I") \ | |||
| FIELD (bottom, "bottom", "I") \ | |||
| DECLARE_JNI_CLASS (AndroidRectClass, "android/graphics/Rect"); | |||
| DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| @@ -434,6 +449,13 @@ DECLARE_JNI_CLASS (AndroidRectClass, "android/graphics/Rect"); | |||
| DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources") | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (getHeight, "getHeight", "()I") \ | |||
| METHOD (getWidth, "getWidth", "()I") | |||
| DECLARE_JNI_CLASS (AndroidSize, "android/util/Size"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (parse, "parse", "(Ljava/lang/String;)Landroid/net/Uri;") \ | |||
| METHOD (toString, "toString", "()Ljava/lang/String;") | |||
| @@ -465,6 +487,12 @@ DECLARE_JNI_CLASS (AndroidView, "android/view/View"); | |||
| DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup") | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;") | |||
| DECLARE_JNI_CLASS (AndroidWindowManager, "android/view/WindowManager"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "(I)V") \ | |||
| @@ -477,6 +505,7 @@ DECLARE_JNI_CLASS (JavaArrayList, "java/util/ArrayList"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (valueOf, "valueOf", "(Z)Ljava/lang/Boolean;") \ | |||
| METHOD (booleanValue, "booleanValue", "()Z") | |||
| DECLARE_JNI_CLASS (JavaBoolean, "java/lang/Boolean"); | |||
| @@ -507,6 +536,13 @@ DECLARE_JNI_CLASS (JavaBoolean, "java/lang/Boolean"); | |||
| DECLARE_JNI_CLASS (JavaBundle, "android/os/Bundle"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (get, "get", "([B)Ljava/nio/ByteBuffer;") \ | |||
| METHOD (remaining, "remaining", "()I") | |||
| DECLARE_JNI_CLASS (JavaByteBuffer, "java/nio/ByteBuffer"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (toString, "toString", "()Ljava/lang/String;") | |||
| @@ -514,6 +550,7 @@ DECLARE_JNI_CLASS (JavaCharSequence, "java/lang/CharSequence"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (forName, "forName", "(Ljava/lang/String;)Ljava/lang/Class;") \ | |||
| METHOD (getName, "getName", "()Ljava/lang/String;") \ | |||
| METHOD (getModifiers, "getModifiers", "()I") \ | |||
| METHOD (isAnnotation, "isAnnotation", "()Z") \ | |||
| @@ -571,7 +608,8 @@ DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap"); | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (parseInt, "parseInt", "(Ljava/lang/String;I)I") \ | |||
| STATICMETHOD (valueOf, "valueOf", "(I)Ljava/lang/Integer;") | |||
| STATICMETHOD (valueOf, "valueOf", "(I)Ljava/lang/Integer;") \ | |||
| METHOD (intValue, "intValue", "()I") | |||
| DECLARE_JNI_CLASS (JavaInteger, "java/lang/Integer"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| @@ -583,6 +621,13 @@ DECLARE_JNI_CLASS (JavaInteger, "java/lang/Integer"); | |||
| DECLARE_JNI_CLASS (JavaIterator, "java/util/Iterator"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (get, "get", "(I)Ljava/lang/Object;") \ | |||
| METHOD (size, "size", "()I") | |||
| DECLARE_JNI_CLASS (JavaList, "java/util/List"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "(J)V") | |||
| @@ -633,6 +678,71 @@ DECLARE_JNI_CLASS (JavaSet, "java/util/Set"); | |||
| DECLARE_JNI_CLASS (JavaString, "java/lang/String"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| inline String juceString (JNIEnv* env, jstring s) | |||
| { | |||
| if (s == 0) | |||
| return {}; | |||
| const char* const utf8 = env->GetStringUTFChars (s, nullptr); | |||
| CharPointer_UTF8 utf8CP (utf8); | |||
| const String result (utf8CP); | |||
| env->ReleaseStringUTFChars (s, utf8); | |||
| return result; | |||
| } | |||
| inline String juceString (jstring s) | |||
| { | |||
| return juceString (getEnv(), s); | |||
| } | |||
| inline LocalRef<jstring> javaString (const String& s) | |||
| { | |||
| return LocalRef<jstring> (getEnv()->NewStringUTF (s.toUTF8())); | |||
| } | |||
| inline LocalRef<jstring> javaStringFromChar (const juce_wchar c) | |||
| { | |||
| char utf8[8] = { 0 }; | |||
| CharPointer_UTF8 (utf8).write (c); | |||
| return LocalRef<jstring> (getEnv()->NewStringUTF (utf8)); | |||
| } | |||
| inline LocalRef<jobjectArray> juceStringArrayToJava (const StringArray& juceArray) | |||
| { | |||
| auto* env = getEnv(); | |||
| LocalRef<jobjectArray> result (env->NewObjectArray ((jsize) juceArray.size(), | |||
| JavaString, | |||
| javaString ("").get())); | |||
| for (int i = 0; i < juceArray.size(); ++i) | |||
| env->SetObjectArrayElement (result, i, javaString (juceArray [i]).get()); | |||
| return result; | |||
| } | |||
| inline StringArray javaStringArrayToJuce (const LocalRef<jobjectArray>& javaArray) | |||
| { | |||
| if (javaArray.get() == nullptr) | |||
| return {}; | |||
| auto* env = getEnv(); | |||
| StringArray result; | |||
| for (int i = 0; i < env->GetArrayLength (javaArray.get()); ++i) | |||
| { | |||
| LocalRef<jstring> javaString ((jstring) env->GetObjectArrayElement (javaArray.get(), i)); | |||
| result.add (juceString (javaString.get())); | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| class AndroidInterfaceImplementer; | |||
| @@ -23,14 +23,6 @@ | |||
| namespace juce | |||
| { | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (constructor, "<init>", "()V") \ | |||
| METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \ | |||
| DECLARE_JNI_CLASS (JNIHandler, "android/os/Handler"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| namespace Android | |||
| { | |||
| @@ -58,14 +50,14 @@ namespace Android | |||
| struct Handler | |||
| { | |||
| Handler() : nativeHandler (getEnv()->NewObject (JNIHandler, JNIHandler.constructor)) {} | |||
| Handler() : nativeHandler (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor)) {} | |||
| ~Handler() { clearSingletonInstance(); } | |||
| JUCE_DECLARE_SINGLETON (Handler, false) | |||
| bool post (jobject runnable) | |||
| { | |||
| return (getEnv()->CallBooleanMethod (nativeHandler.get(), JNIHandler.post, runnable) != 0); | |||
| return (getEnv()->CallBooleanMethod (nativeHandler.get(), AndroidHandler.post, runnable) != 0); | |||
| } | |||
| GlobalRef nativeHandler; | |||
| @@ -176,7 +176,7 @@ public: | |||
| void initialise (JNIEnv* const env) | |||
| { | |||
| rect = GlobalRef (env->NewObject (AndroidRectClass, AndroidRectClass.constructor, 0, 0, 0, 0)); | |||
| rect = GlobalRef (env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0)); | |||
| paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); | |||
| const LocalRef<jobject> ignored (paint.callObjectMethod (AndroidPaint.setTypeface, typeface.get())); | |||
| @@ -298,10 +298,10 @@ public: | |||
| env->DeleteLocalRef (matrix); | |||
| const int left = env->GetIntField (rect.get(), AndroidRectClass.left); | |||
| const int top = env->GetIntField (rect.get(), AndroidRectClass.top); | |||
| const int right = env->GetIntField (rect.get(), AndroidRectClass.right); | |||
| const int bottom = env->GetIntField (rect.get(), AndroidRectClass.bottom); | |||
| const int left = env->GetIntField (rect.get(), AndroidRect.left); | |||
| const int top = env->GetIntField (rect.get(), AndroidRect.top); | |||
| const int right = env->GetIntField (rect.get(), AndroidRect.right); | |||
| const int bottom = env->GetIntField (rect.get(), AndroidRect.bottom); | |||
| const Rectangle<int> bounds (left, top, right - left, bottom - top); | |||
| @@ -486,7 +486,7 @@ public: | |||
| { | |||
| ignoreUnused (selection, selectionArgs, sortOrder); | |||
| StringArray requestedColumns = javaStringArrayToJuceStringArray (projection); | |||
| StringArray requestedColumns = javaStringArrayToJuce (projection); | |||
| StringArray supportedColumns = getSupportedColumns(); | |||
| StringArray resultColumns; | |||
| @@ -501,7 +501,7 @@ public: | |||
| if (resultColumns.isEmpty()) | |||
| return nullptr; | |||
| auto resultJavaColumns = juceStringArrayToJavaStringArray (resultColumns); | |||
| auto resultJavaColumns = juceStringArrayToJava (resultColumns); | |||
| auto* env = getEnv(); | |||
| @@ -550,7 +550,7 @@ public: | |||
| if (extension.isEmpty()) | |||
| return nullptr; | |||
| return juceStringArrayToJavaStringArray (filterMimeTypes (getMimeTypesForFileExtension (extension), | |||
| return juceStringArrayToJava (filterMimeTypes (getMimeTypesForFileExtension (extension), | |||
| juceString (mimeTypeFilter.get()))); | |||
| } | |||
| @@ -683,40 +683,6 @@ private: | |||
| return { index, filename, prepareFilesThread->getFilePaths()[index.getIntValue()] }; | |||
| } | |||
| static LocalRef<jobjectArray> juceStringArrayToJavaStringArray (const StringArray& juceArray) | |||
| { | |||
| auto* env = getEnv(); | |||
| auto javaArray = LocalRef<jobjectArray> (env->NewObjectArray ((jsize) juceArray.size(), | |||
| JavaString, | |||
| javaString ("").get())); | |||
| for (int i = 0; i < juceArray.size(); ++i) | |||
| env->SetObjectArrayElement (javaArray, i, javaString (juceArray [i]).get()); | |||
| return javaArray; | |||
| } | |||
| static StringArray javaStringArrayToJuceStringArray (const LocalRef<jobjectArray>& javaArray) | |||
| { | |||
| if (javaArray.get() == 0) | |||
| return {}; | |||
| auto* env = getEnv(); | |||
| const int size = env->GetArrayLength (javaArray.get()); | |||
| StringArray juceArray; | |||
| for (int i = 0; i < size; ++i) | |||
| { | |||
| auto javaString = LocalRef<jstring> ((jstring) env->GetObjectArrayElement (javaArray.get(), i)); | |||
| juceArray.add (juceString (javaString.get())); | |||
| } | |||
| return juceArray; | |||
| } | |||
| static StringArray getSupportedColumns() | |||
| { | |||
| return StringArray ("_display_name", "_size"); | |||
| @@ -560,6 +560,8 @@ public: | |||
| Component::unfocusAllComponents(); | |||
| } | |||
| void handleAppPausedCallback() {} | |||
| void handleAppResumedCallback() | |||
| { | |||
| if (Component* kiosk = Desktop::getInstance().getKioskModeComponent()) | |||
| @@ -630,10 +632,10 @@ public: | |||
| void handlePaintCallback (JNIEnv* env, jobject canvas, jobject paint) | |||
| { | |||
| jobject rect = env->CallObjectMethod (canvas, CanvasMinimal.getClipBounds); | |||
| const int left = env->GetIntField (rect, AndroidRectClass.left); | |||
| const int top = env->GetIntField (rect, AndroidRectClass.top); | |||
| const int right = env->GetIntField (rect, AndroidRectClass.right); | |||
| const int bottom = env->GetIntField (rect, AndroidRectClass.bottom); | |||
| const int left = env->GetIntField (rect, AndroidRect.left); | |||
| const int top = env->GetIntField (rect, AndroidRect.top); | |||
| const int right = env->GetIntField (rect, AndroidRect.right); | |||
| const int bottom = env->GetIntField (rect, AndroidRect.bottom); | |||
| env->DeleteLocalRef (rect); | |||
| const Rectangle<int> clip (left, top, right - left, bottom - top); | |||
| @@ -810,6 +812,7 @@ JUCE_VIEW_CALLBACK (void, handleKeyDown, (JNIEnv* env, jobject /*view*/, | |||
| JUCE_VIEW_CALLBACK (void, handleKeyUp, (JNIEnv* env, jobject /*view*/, jlong host, jint k, jint kc), handleKeyUpCallback ((int) k, (int) kc)) | |||
| JUCE_VIEW_CALLBACK (void, handleBackButton, (JNIEnv* env, jobject /*view*/, jlong host), handleBackButtonCallback()) | |||
| JUCE_VIEW_CALLBACK (void, handleKeyboardHidden, (JNIEnv* env, jobject /*view*/, jlong host), handleKeyboardHiddenCallback()) | |||
| JUCE_VIEW_CALLBACK (void, handleAppPaused, (JNIEnv* env, jobject /*view*/, jlong host), handleAppPausedCallback()) | |||
| JUCE_VIEW_CALLBACK (void, handleAppResumed, (JNIEnv* env, jobject /*view*/, jlong host), handleAppResumedCallback()) | |||
| //============================================================================== | |||
| @@ -819,18 +822,6 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*) | |||
| } | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (getRotation, "getRotation", "()I") | |||
| DECLARE_JNI_CLASS (Display, "android/view/Display"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;") | |||
| DECLARE_JNI_CLASS (WindowManager, "android/view/WindowManager"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| { | |||
| return true; | |||
| @@ -857,11 +848,11 @@ Desktop::DisplayOrientation Desktop::getCurrentOrientation() const | |||
| if (windowManager.get() != 0) | |||
| { | |||
| LocalRef<jobject> display = LocalRef<jobject> (env->CallObjectMethod (windowManager, WindowManager.getDefaultDisplay)); | |||
| LocalRef<jobject> display = LocalRef<jobject> (env->CallObjectMethod (windowManager, AndroidWindowManager.getDefaultDisplay)); | |||
| if (display.get() != 0) | |||
| { | |||
| int rotation = env->CallIntMethod (display, Display.getRotation); | |||
| int rotation = env->CallIntMethod (display, AndroidDisplay.getRotation); | |||
| switch (rotation) | |||
| { | |||
| @@ -1427,8 +1427,8 @@ struct PushNotifications::Pimpl | |||
| propertiesDynamicObject->setProperty ("clickAction", juceString (clickAction.get())); | |||
| propertiesDynamicObject->setProperty ("bodyLocalizationKey", juceString (bodyLocalizationKey.get())); | |||
| propertiesDynamicObject->setProperty ("titleLocalizationKey", juceString (titleLocalizationKey.get())); | |||
| propertiesDynamicObject->setProperty ("bodyLocalizationArgs", jobjectArrayToStringArray (bodyLocalizationArgs)); | |||
| propertiesDynamicObject->setProperty ("titleLocalizationArgs", jobjectArrayToStringArray (titleLocalizationArgs)); | |||
| propertiesDynamicObject->setProperty ("bodyLocalizationArgs", javaStringArrayToJuce (bodyLocalizationArgs)); | |||
| propertiesDynamicObject->setProperty ("titleLocalizationArgs", javaStringArrayToJuce (titleLocalizationArgs)); | |||
| propertiesDynamicObject->setProperty ("link", link.get() != 0 ? juceString ((jstring) env->CallObjectMethod (link, AndroidUri.toString)) : String()); | |||
| } | |||
| @@ -1436,23 +1436,6 @@ struct PushNotifications::Pimpl | |||
| return n; | |||
| } | |||
| static StringArray jobjectArrayToStringArray (const LocalRef<jobjectArray>& array) | |||
| { | |||
| if (array == 0) | |||
| return {}; | |||
| auto* env = getEnv(); | |||
| const int size = env->GetArrayLength (array.get()); | |||
| StringArray stringArray; | |||
| for (int i = 0; i < size; ++i) | |||
| stringArray.add (juceString ((jstring) env->GetObjectArrayElement (array.get(), (jsize) i))); | |||
| return stringArray; | |||
| } | |||
| #endif | |||
| void setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels) | |||
| @@ -27,22 +27,129 @@ | |||
| namespace juce | |||
| { | |||
| #if JUCE_MAC || JUCE_IOS | |||
| #if JUCE_MAC | |||
| #include "../native/juce_mac_CameraDevice.h" | |||
| #elif JUCE_WINDOWS | |||
| #include "../native/juce_win32_CameraDevice.h" | |||
| #elif JUCE_IOS | |||
| #if JUCE_CLANG | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wunguarded-availability-new" | |||
| #endif | |||
| #include "../native/juce_ios_CameraDevice.h" | |||
| #if JUCE_CLANG | |||
| #pragma clang diagnostic pop | |||
| #endif | |||
| #elif JUCE_ANDROID | |||
| #include "../native/juce_android_CameraDevice.h" | |||
| #endif | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| //============================================================================== | |||
| class CameraDevice::CameraFactory | |||
| { | |||
| public: | |||
| static CameraFactory& getInstance() | |||
| { | |||
| static CameraFactory factory; | |||
| return factory; | |||
| } | |||
| void openCamera (int index, OpenCameraResultCallback resultCallback, | |||
| int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality) | |||
| { | |||
| auto cameraId = getAvailableDevices()[index]; | |||
| if (getCameraIndex (cameraId) != -1) | |||
| { | |||
| // You are trying to open the same camera twice. | |||
| jassertfalse; | |||
| return; | |||
| } | |||
| std::unique_ptr<CameraDevice> device (new CameraDevice (cameraId, index, | |||
| minWidth, minHeight, maxWidth, | |||
| maxHeight, useHighQuality)); | |||
| camerasToOpen.add ({ nextRequestId++, | |||
| std::unique_ptr<CameraDevice> (device.release()), | |||
| resultCallback }); | |||
| auto& pendingOpen = camerasToOpen.getReference (camerasToOpen.size() - 1); | |||
| pendingOpen.device->pimpl->open ([this](const String& deviceId, const String& error) | |||
| { | |||
| int index = getCameraIndex (deviceId); | |||
| if (index == -1) | |||
| return; | |||
| auto& pendingOpen = camerasToOpen.getReference (index); | |||
| if (error.isEmpty()) | |||
| pendingOpen.resultCallback (pendingOpen.device.release(), error); | |||
| else | |||
| pendingOpen.resultCallback (nullptr, error); | |||
| int id = pendingOpen.requestId; | |||
| MessageManager::callAsync ([this, id]() { removeRequestWithId (id); }); | |||
| }); | |||
| } | |||
| private: | |||
| int getCameraIndex (const String& cameraId) const | |||
| { | |||
| for (int i = 0; i < camerasToOpen.size(); ++i) | |||
| { | |||
| auto& pendingOpen = camerasToOpen.getReference (i); | |||
| if (pendingOpen.device->pimpl->getCameraId() == cameraId) | |||
| return i; | |||
| } | |||
| return -1; | |||
| } | |||
| void removeRequestWithId (int id) | |||
| { | |||
| for (int i = camerasToOpen.size(); --i >= 0;) | |||
| { | |||
| if (camerasToOpen.getReference (i).requestId == id) | |||
| { | |||
| camerasToOpen.remove (i); | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| struct PendingCameraOpen | |||
| { | |||
| int requestId; | |||
| std::unique_ptr<CameraDevice> device; | |||
| OpenCameraResultCallback resultCallback; | |||
| }; | |||
| Array<PendingCameraOpen> camerasToOpen; | |||
| static int nextRequestId; | |||
| }; | |||
| int CameraDevice::CameraFactory::nextRequestId = 0; | |||
| #endif | |||
| //============================================================================== | |||
| CameraDevice::CameraDevice (const String& nm, int index, int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality) | |||
| : name (nm), pimpl (new Pimpl (name, index, minWidth, minHeight, maxWidth, maxHeight, useHighQuality)) | |||
| : name (nm), pimpl (new Pimpl (*this, name, index, minWidth, minHeight, maxWidth, maxHeight, useHighQuality)) | |||
| { | |||
| } | |||
| CameraDevice::~CameraDevice() | |||
| { | |||
| jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager()); | |||
| stopRecording(); | |||
| pimpl.reset(); | |||
| } | |||
| @@ -52,6 +159,11 @@ Component* CameraDevice::createViewerComponent() | |||
| return new ViewerComponent (*this); | |||
| } | |||
| void CameraDevice::takeStillPicture (std::function<void (const Image&)> pictureTakenCallback) | |||
| { | |||
| pimpl->takeStillPicture (pictureTakenCallback); | |||
| } | |||
| void CameraDevice::startRecordingToFile (const File& file, int quality) | |||
| { | |||
| stopRecording(); | |||
| @@ -68,18 +180,6 @@ void CameraDevice::stopRecording() | |||
| pimpl->stopRecording(); | |||
| } | |||
| void CameraDevice::addListener (Listener* listenerToAdd) | |||
| { | |||
| if (listenerToAdd != nullptr) | |||
| pimpl->addListener (listenerToAdd); | |||
| } | |||
| void CameraDevice::removeListener (Listener* listenerToRemove) | |||
| { | |||
| if (listenerToRemove != nullptr) | |||
| pimpl->removeListener (listenerToRemove); | |||
| } | |||
| //============================================================================== | |||
| StringArray CameraDevice::getAvailableDevices() | |||
| { | |||
| @@ -94,12 +194,44 @@ CameraDevice* CameraDevice::openDevice (int index, | |||
| int maxWidth, int maxHeight, | |||
| bool useHighQuality) | |||
| { | |||
| jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager()); | |||
| #if ! JUCE_ANDROID && ! JUCE_IOS | |||
| std::unique_ptr<CameraDevice> d (new CameraDevice (getAvailableDevices() [index], index, | |||
| minWidth, minHeight, maxWidth, maxHeight, useHighQuality)); | |||
| if (d != nullptr && d->pimpl->openedOk()) | |||
| return d.release(); | |||
| #else | |||
| ignoreUnused (index, minWidth, minHeight); | |||
| ignoreUnused (maxWidth, maxHeight, useHighQuality); | |||
| // Use openDeviceAsync to open a camera device on iOS or Android. | |||
| jassertfalse; | |||
| #endif | |||
| return nullptr; | |||
| } | |||
| void CameraDevice::openDeviceAsync (int index, OpenCameraResultCallback resultCallback, | |||
| int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality) | |||
| { | |||
| jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager()); | |||
| if (resultCallback == nullptr) | |||
| { | |||
| // A valid callback must be passed. | |||
| jassertfalse; | |||
| return; | |||
| } | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| CameraFactory::getInstance().openCamera (index, static_cast<OpenCameraResultCallback&&> (resultCallback), | |||
| minWidth, minHeight, maxWidth, maxHeight, useHighQuality); | |||
| #else | |||
| auto* device = openDevice (index, minWidth, minHeight, maxWidth, maxHeight, useHighQuality); | |||
| resultCallback (device, device != nullptr ? String() : "Could not open camera device"); | |||
| #endif | |||
| } | |||
| } // namespace juce | |||
| @@ -35,9 +35,9 @@ namespace juce | |||
| Controls any video capture devices that might be available. | |||
| Use getAvailableDevices() to list the devices that are attached to the | |||
| system, then call openDevice to open one for use. Once you have a CameraDevice | |||
| object, you can get a viewer component from it, and use its methods to | |||
| stream to a file or capture still-frames. | |||
| system, then call openDevice() or openDeviceAsync() to open one for use. | |||
| Once you have a CameraDevice object, you can get a viewer component from it, | |||
| and use its methods to stream to a file or capture still-frames. | |||
| @tags{Video} | |||
| */ | |||
| @@ -50,17 +50,18 @@ public: | |||
| //============================================================================== | |||
| /** Returns a list of the available cameras on this machine. | |||
| You can open one of these devices by calling openDevice(). | |||
| You can open one of these devices by calling openDevice() or openDeviceAsync(). | |||
| */ | |||
| static StringArray getAvailableDevices(); | |||
| /** Opens a camera device. | |||
| /** Synchronously opens a camera device. This function should not be used on iOS or | |||
| Android, use openDeviceAsync() instead. | |||
| The index parameter indicates which of the items returned by getAvailableDevices() | |||
| to open. | |||
| The size constraints allow the method to choose between different resolutions if | |||
| the camera supports this. If the resolution cam't be specified (e.g. on the Mac) | |||
| the camera supports this. If the resolution can't be specified (e.g. on the Mac) | |||
| then these will be ignored. | |||
| On Mac, if highQuality is false, then the camera will be opened in preview mode | |||
| @@ -72,16 +73,62 @@ public: | |||
| int maxWidth = 1024, int maxHeight = 768, | |||
| bool highQuality = true); | |||
| using OpenCameraResultCallback = std::function<void (CameraDevice*, const String& /*error*/)>; | |||
| /** Asynchronously opens a camera device on iOS (iOS 7+) or Android (API 21+). | |||
| On other platforms, the function will simply call openDevice(). Upon completion, | |||
| resultCallback will be invoked with valid CameraDevice* and an empty error | |||
| String on success, or nullptr CameraDevice and a non-empty error String on failure. | |||
| This is the preferred method of opening a camera device, because it works on all | |||
| platforms, whereas synchronous openDevice() does not work on iOS & Android. | |||
| The index parameter indicates which of the items returned by getAvailableDevices() | |||
| to open. | |||
| The size constraints allow the method to choose between different resolutions if | |||
| the camera supports this. If the resolution can't be specified then these will be | |||
| ignored. | |||
| On iOS, if you want to switch a device, it is more efficient to open a new device | |||
| before closing the older one, because this way both devices can share the same | |||
| underlying camera session. Otherwise, the session needs to be close first, and this | |||
| is a lengthy process that can take several seconds. | |||
| The Android implementation currently supports a maximum recording resolution of | |||
| 1080p. Choosing a larger size will result in larger pictures taken, but the video | |||
| will be capped at 1080p. | |||
| */ | |||
| static void openDeviceAsync (int deviceIndex, | |||
| OpenCameraResultCallback resultCallback, | |||
| int minWidth = 128, int minHeight = 64, | |||
| int maxWidth = 1024, int maxHeight = 768, | |||
| bool highQuality = true); | |||
| //============================================================================== | |||
| /** Returns the name of this device */ | |||
| const String& getName() const noexcept { return name; } | |||
| /** Creates a component that can be used to display a preview of the | |||
| video from this camera. | |||
| Note: while you can change the size of the preview component, the actual | |||
| preview display may be smaller than the size requested, because the correct | |||
| aspect ratio is maintained automatically. | |||
| */ | |||
| Component* createViewerComponent(); | |||
| //============================================================================== | |||
| /** Triggers a still picture capture. Upon completion, pictureTakenCallback will | |||
| be invoked on a message thread. | |||
| On Android, before calling takeStillPicture(), you need to create a preview with | |||
| createViewerComponent() and you need to make it visible on screen. | |||
| Android does not support simultaneous video recording and still picture capture. | |||
| */ | |||
| void takeStillPicture (std::function<void (const Image&)> pictureTakenCallback); | |||
| /** Starts recording video to the specified file. | |||
| You should use getFileExtension() to find out the correct extension to | |||
| @@ -95,6 +142,16 @@ public: | |||
| The quality parameter can be 0, 1, or 2, to indicate low, medium, or high. It may | |||
| or may not be used, depending on the driver. | |||
| On Android, before calling startRecordingToFile(), you need to create a preview with | |||
| createViewerComponent() and you need to make it visible on screen. | |||
| The Android camera also requires exclusive access to the audio device, so make sure | |||
| you close any open audio devices with AudioDeviceManager::closeAudioDevice() first. | |||
| Android does not support simultaneous video recording and still picture capture. | |||
| @see AudioDeviceManager::closeAudioDevice, AudioDeviceManager::restartLastAudioDevice | |||
| */ | |||
| void startRecordingToFile (const File& file, int quality = 2); | |||
| @@ -113,36 +170,9 @@ public: | |||
| */ | |||
| Time getTimeOfFirstRecordedFrame() const; | |||
| //============================================================================== | |||
| /** | |||
| Receives callbacks with images from a CameraDevice. | |||
| @see CameraDevice::addListener | |||
| */ | |||
| class JUCE_API Listener | |||
| { | |||
| public: | |||
| Listener() {} | |||
| virtual ~Listener() {} | |||
| /** This method is called when a new image arrives. | |||
| This may be called by any thread, so be careful about thread-safety, | |||
| and make sure that you process the data as quickly as possible to | |||
| avoid glitching! | |||
| */ | |||
| virtual void imageReceived (const Image& image) = 0; | |||
| }; | |||
| /** Adds a listener to receive images from the camera. | |||
| Be very careful not to delete the listener without first removing it by calling | |||
| removeListener(). | |||
| */ | |||
| void addListener (Listener* listenerToAdd); | |||
| /** Removes a listener that was previously added with addListener(). */ | |||
| void removeListener (Listener* listenerToRemove); | |||
| /** Set this callback to be notified whenever an error occurs. You may need to close | |||
| and reopen the device to be able to use it further. */ | |||
| std::function<void (const String& /*error*/)> onErrorOccurred; | |||
| private: | |||
| String name; | |||
| @@ -158,6 +188,32 @@ private: | |||
| CameraDevice (const String& name, int index, | |||
| int minWidth, int minHeight, int maxWidth, int maxHeight, bool highQuality); | |||
| #if JUCE_ANDROID || JUCE_IOS | |||
| class CameraFactory; | |||
| #endif | |||
| #if JUCE_ANDROID | |||
| friend void juce_cameraDeviceStateClosed (int64); | |||
| friend void juce_cameraDeviceStateDisconnected (int64); | |||
| friend void juce_cameraDeviceStateError (int64, int); | |||
| friend void juce_cameraDeviceStateOpened (int64, void*); | |||
| friend void juce_cameraCaptureSessionActive (int64, void*); | |||
| friend void juce_cameraCaptureSessionClosed (int64, void*); | |||
| friend void juce_cameraCaptureSessionConfigureFailed (int64, void*); | |||
| friend void juce_cameraCaptureSessionConfigured (int64, void*); | |||
| friend void juce_cameraCaptureSessionReady (int64, void*); | |||
| friend void juce_cameraCaptureSessionCaptureCompleted (int64, bool, void*, void*, void*); | |||
| friend void juce_cameraCaptureSessionCaptureFailed (int64, bool, void*, void*, void*); | |||
| friend void juce_cameraCaptureSessionCaptureProgressed (int64, bool, void*, void*, void*); | |||
| friend void juce_cameraCaptureSessionCaptureSequenceAborted (int64, bool, void*, int); | |||
| friend void juce_cameraCaptureSessionCaptureSequenceCompleted (int64, bool, void*, int, int64); | |||
| friend void juce_cameraCaptureSessionCaptureStarted (int64, bool, void*, void*, int64, int64); | |||
| friend void juce_deviceOrientationChanged (int64, int); | |||
| #endif | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDevice) | |||
| }; | |||
| @@ -59,13 +59,23 @@ | |||
| //============================================================================= | |||
| /** Config: JUCE_USE_CAMERA | |||
| Enables web-cam support using the CameraDevice class (Mac and Windows). | |||
| Enables camera support using the CameraDevice class (Mac, Windows, iOS, Android). | |||
| */ | |||
| #ifndef JUCE_USE_CAMERA | |||
| #define JUCE_USE_CAMERA 0 | |||
| #endif | |||
| #if ! (JUCE_MAC || JUCE_WINDOWS) | |||
| #ifndef JUCE_CAMERA_LOG_ENABLED | |||
| #define JUCE_CAMERA_LOG_ENABLED 0 | |||
| #endif | |||
| #if JUCE_CAMERA_LOG_ENABLED | |||
| #define JUCE_CAMERA_LOG(x) DBG(x) | |||
| #else | |||
| #define JUCE_CAMERA_LOG(x) {} | |||
| #endif | |||
| #if ! (JUCE_MAC || JUCE_WINDOWS || JUCE_IOS || JUCE_ANDROID) | |||
| #undef JUCE_USE_CAMERA | |||
| #endif | |||
| @@ -26,8 +26,9 @@ | |||
| struct CameraDevice::Pimpl | |||
| { | |||
| Pimpl (const String&, int /*index*/, int /*minWidth*/, int /*minHeight*/, | |||
| Pimpl (CameraDevice& ownerToUse, const String&, int /*index*/, int /*minWidth*/, int /*minHeight*/, | |||
| int /*maxWidth*/, int /*maxHeight*/, bool useHighQuality) | |||
| : owner (ownerToUse) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| @@ -42,11 +43,20 @@ struct CameraDevice::Pimpl | |||
| static DelegateClass cls; | |||
| callbackDelegate = (id<AVCaptureFileOutputRecordingDelegate>) [cls.createInstance() init]; | |||
| DelegateClass::setOwner (callbackDelegate, this); | |||
| SEL runtimeErrorSel = NSSelectorFromString (nsStringLiteral ("captureSessionRuntimeError:")); | |||
| [[NSNotificationCenter defaultCenter] addObserver: callbackDelegate | |||
| selector: runtimeErrorSel | |||
| name: AVCaptureSessionRuntimeErrorNotification | |||
| object: session]; | |||
| } | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| [[NSNotificationCenter defaultCenter] removeObserver: callbackDelegate]; | |||
| [session stopRunning]; | |||
| removeImageCapture(); | |||
| removeMovieCapture(); | |||
| @@ -113,6 +123,19 @@ struct CameraDevice::Pimpl | |||
| refreshConnections(); | |||
| } | |||
| void takeStillPicture (std::function<void (const Image&)> pictureTakenCallbackToUse) | |||
| { | |||
| if (pictureTakenCallbackToUse == nullptr) | |||
| { | |||
| jassertfalse; | |||
| return; | |||
| } | |||
| pictureTakenCallback = static_cast<std::function<void (const Image&)>&&> (pictureTakenCallbackToUse); | |||
| triggerImageCapture(); | |||
| } | |||
| void startRecordingToFile (const File& file, int /*quality*/) | |||
| { | |||
| stopRecording(); | |||
| @@ -150,21 +173,10 @@ struct CameraDevice::Pimpl | |||
| return nil; | |||
| } | |||
| void handleImageCapture (const void* data, size_t size) | |||
| void handleImageCapture (const Image& image) | |||
| { | |||
| auto image = ImageFileFormat::loadFrom (data, size); | |||
| const ScopedLock sl (listenerLock); | |||
| if (! listeners.isEmpty()) | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| if (auto* l = listeners[i]) | |||
| l->imageReceived (image); | |||
| if (! listeners.isEmpty()) | |||
| triggerImageCapture(); | |||
| } | |||
| if (pictureTakenCallback != nullptr) | |||
| pictureTakenCallback (image); | |||
| } | |||
| void triggerImageCapture() | |||
| @@ -174,31 +186,23 @@ struct CameraDevice::Pimpl | |||
| if (auto* videoConnection = getVideoConnection()) | |||
| { | |||
| [imageOutput captureStillImageAsynchronouslyFromConnection: videoConnection | |||
| completionHandler: ^(CMSampleBufferRef sampleBuffer, NSError*) | |||
| completionHandler: ^(CMSampleBufferRef sampleBuffer, NSError* error) | |||
| { | |||
| auto buffer = CMSampleBufferGetDataBuffer (sampleBuffer); | |||
| size_t size = CMBlockBufferGetDataLength (buffer); | |||
| jassert (CMBlockBufferIsRangeContiguous (buffer, 0, size)); // TODO: need to add code to handle this if it happens | |||
| char* data = nullptr; | |||
| CMBlockBufferGetDataPointer (buffer, 0, &size, nullptr, &data); | |||
| handleImageCapture (data, size); | |||
| }]; | |||
| } | |||
| } | |||
| if (error != nil) | |||
| { | |||
| JUCE_CAMERA_LOG ("Still picture capture failed, error: " + nsStringToJuce (error.localizedDescription)); | |||
| jassertfalse; | |||
| return; | |||
| } | |||
| void addListener (CameraDevice::Listener* listenerToAdd) | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| listeners.addIfNotAlreadyThere (listenerToAdd); | |||
| NSData* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: sampleBuffer]; | |||
| if (listeners.size() == 1) | |||
| triggerImageCapture(); | |||
| } | |||
| auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length); | |||
| void removeListener (CameraDevice::Listener* listenerToRemove) | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| listeners.removeFirstMatchingValue (listenerToRemove); | |||
| WeakReference<Pimpl> weakRef (this); | |||
| MessageManager::callAsync ([weakRef, image]() mutable { if (weakRef != nullptr) weakRef->handleImageCapture (image); }); | |||
| }]; | |||
| } | |||
| } | |||
| static StringArray getAvailableDevices() | |||
| @@ -208,6 +212,15 @@ struct CameraDevice::Pimpl | |||
| return results; | |||
| } | |||
| void cameraSessionRuntimeError (const String& error) | |||
| { | |||
| JUCE_CAMERA_LOG ("cameraSessionRuntimeError(), error = " + error); | |||
| if (owner.onErrorOccurred != nullptr) | |||
| owner.onErrorOccurred (error); | |||
| } | |||
| CameraDevice& owner; | |||
| AVCaptureView* captureView = nil; | |||
| AVCaptureSession* session = nil; | |||
| AVCaptureMovieFileOutput* fileOutput = nil; | |||
| @@ -218,8 +231,9 @@ struct CameraDevice::Pimpl | |||
| Time firstPresentationTime; | |||
| bool isRecording = false; | |||
| Array<CameraDevice::Listener*> listeners; | |||
| CriticalSection listenerLock; | |||
| std::function<void (const Image&)> pictureTakenCallback; | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl) | |||
| private: | |||
| //============================================================================== | |||
| @@ -235,17 +249,29 @@ private: | |||
| addMethod (@selector (captureOutput:didResumeRecordingToOutputFileAtURL: fromConnections:), didResumeRecordingToOutputFileAtURL, "v@:@@@"); | |||
| addMethod (@selector (captureOutput:willFinishRecordingToOutputFileAtURL:fromConnections:error:), willFinishRecordingToOutputFileAtURL, "v@:@@@@"); | |||
| SEL runtimeErrorSel = NSSelectorFromString (nsStringLiteral ("captureSessionRuntimeError:")); | |||
| addMethod (runtimeErrorSel, sessionRuntimeError, "v@:@"); | |||
| registerClass(); | |||
| } | |||
| static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); } | |||
| static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); } | |||
| static Pimpl& getOwner (id self) { return *getIvar<Pimpl*> (self, "owner"); } | |||
| private: | |||
| static void didStartRecordingToOutputFileAtURL (id, SEL, AVCaptureFileOutput*, NSURL*, NSArray*) {} | |||
| static void didPauseRecordingToOutputFileAtURL (id, SEL, AVCaptureFileOutput*, NSURL*, NSArray*) {} | |||
| static void didResumeRecordingToOutputFileAtURL (id, SEL, AVCaptureFileOutput*, NSURL*, NSArray*) {} | |||
| static void willFinishRecordingToOutputFileAtURL (id, SEL, AVCaptureFileOutput*, NSURL*, NSArray*, NSError*) {} | |||
| static void sessionRuntimeError (id self, SEL, NSNotification* notification) | |||
| { | |||
| JUCE_CAMERA_LOG (nsStringToJuce ([notification description])); | |||
| NSError* error = notification.userInfo[AVCaptureSessionErrorKey]; | |||
| auto errorString = error != nil ? nsStringToJuce (error.localizedDescription) : String(); | |||
| getOwner (self).cameraSessionRuntimeError (errorString); | |||
| } | |||
| }; | |||
| JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
| @@ -49,10 +49,11 @@ static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0 | |||
| struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| { | |||
| Pimpl (const String&, int index, | |||
| int minWidth, int minHeight, | |||
| int maxWidth, int maxHeight, bool /*highQuality*/) | |||
| : isRecording (false), | |||
| Pimpl (CameraDevice& ownerToUse, const String&, int index, | |||
| int minWidth, int minHeight, int maxWidth, int maxHeight, | |||
| bool /*highQuality*/) | |||
| : owner (ownerToUse), | |||
| isRecording (false), | |||
| openedSuccessfully (false), | |||
| imageNeedsFlipping (false), | |||
| width (0), height (0), | |||
| @@ -191,6 +192,22 @@ struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| bool openedOk() const noexcept { return openedSuccessfully; } | |||
| void takeStillPicture (std::function<void (const Image&)> pictureTakenCallbackToUse) | |||
| { | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| jassert (pictureTakenCallbackToUse != nullptr); | |||
| if (pictureTakenCallbackToUse == nullptr) | |||
| return; | |||
| pictureTakenCallback = static_cast<std::function<void (const Image&)>&&> (pictureTakenCallbackToUse); | |||
| } | |||
| addUser(); | |||
| } | |||
| void startRecordingToFile (const File& file, int quality) | |||
| { | |||
| addUser(); | |||
| @@ -212,32 +229,26 @@ struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| return firstRecordedTime; | |||
| } | |||
| void addListener (CameraDevice::Listener* listenerToAdd) | |||
| void notifyImageReceivedIfNeeded (const Image& image) | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| if (listeners.size() == 0) | |||
| addUser(); | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| listeners.addIfNotAlreadyThere (listenerToAdd); | |||
| } | |||
| if (pictureTakenCallback == nullptr) | |||
| return; | |||
| } | |||
| void removeListener (CameraDevice::Listener* listenerToRemove) | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| listeners.removeAllInstancesOf (listenerToRemove); | |||
| WeakReference<Pimpl> weakRef (this); | |||
| MessageManager::callAsync ([weakRef, image]() mutable | |||
| { | |||
| if (weakRef == nullptr) | |||
| return; | |||
| if (listeners.size() == 0) | |||
| removeUser(); | |||
| } | |||
| if (weakRef->pictureTakenCallback != nullptr) | |||
| weakRef->pictureTakenCallback (image); | |||
| void callListeners (const Image& image) | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| if (CameraDevice::Listener* const l = listeners[i]) | |||
| l->imageReceived (image); | |||
| weakRef->pictureTakenCallback = nullptr; | |||
| }); | |||
| } | |||
| void addUser() | |||
| @@ -294,8 +305,7 @@ struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| imageNeedsFlipping = true; | |||
| } | |||
| if (listeners.size() > 0) | |||
| callListeners (loadingImage); | |||
| notifyImageReceivedIfNeeded (loadingImage); | |||
| sendChangeMessage(); | |||
| } | |||
| @@ -520,9 +530,9 @@ struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| JUCE_DECLARE_NON_COPYABLE (GrabberCallback) | |||
| }; | |||
| CameraDevice& owner; | |||
| ComSmartPtr<GrabberCallback> callback; | |||
| Array<CameraDevice::Listener*> listeners; | |||
| CriticalSection listenerLock; | |||
| bool isRecording, openedSuccessfully; | |||
| int width, height; | |||
| @@ -547,6 +557,11 @@ struct CameraDevice::Pimpl : public ChangeBroadcaster | |||
| bool recordNextFrameTime; | |||
| int previewMaxFPS; | |||
| CriticalSection callbackLock; | |||
| std::function<void (const Image&)> pictureTakenCallback; | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl) | |||
| private: | |||
| void getVideoSizes (IAMStreamConfig* const streamConfig) | |||
| { | |||
| @@ -54,12 +54,12 @@ public: | |||
| //============================================================================== | |||
| /** Tries to load a video from a local file. | |||
| @returns am error if the file failed to be loaded correctly | |||
| @returns an error if the file failed to be loaded correctly | |||
| */ | |||
| Result load (const File& file); | |||
| /** Tries to load a video from a URL. | |||
| @returns am error if the file failed to be loaded correctly | |||
| @returns an error if the file failed to be loaded correctly | |||
| */ | |||
| Result load (const URL& url); | |||
| @@ -75,7 +75,7 @@ public: | |||
| File getCurrentVideoFile() const; | |||
| /** Returns the last URL that was loaded. | |||
| If nothing is open, or if it was a file rather than a URL, this will return File(). | |||
| If nothing is open, or if it was a file rather than a URL, this will return URL(). | |||
| */ | |||
| URL getCurrentVideoURL() const; | |||