1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Add camera support for iOS and Android.

This commit is contained in:
Lukasz Kozakiewicz 2018-04-25 18:54:04 +02:00
parent bac6996d98
commit 772185f2b0
75 changed files with 6619 additions and 421 deletions

View file

@ -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.

View file

@ -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)

View file

@ -19,7 +19,7 @@ android {
}
defaultConfig {
applicationId "com.roli.juce.demorunner"
applicationId "com.juce.demorunner"
minSdkVersion 23
targetSdkVersion 23
externalNativeBuild {

View file

@ -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>

View file

@ -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)
{

View file

@ -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 = {

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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,

View file

@ -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>

View file

@ -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&#10;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"/>

View file

@ -156,6 +156,10 @@
//#define JUCE_PLUGINHOST_AU 0
#endif
#ifndef JUCE_PLUGINHOST_LADSPA
//#define JUCE_PLUGINHOST_LADSPA 0
#endif
//==============================================================================
// juce_audio_utils flags:

View file

@ -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

View file

@ -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()),

View file

@ -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());
cameraDevice->startRecordingToFile (file);
#if JUCE_ANDROID
// Android does not support taking pictures while recording video.
snapshotButton.setEnabled (false);
#endif
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);
if (! image.isValid())
return;
// 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();
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 handleAsyncUpdate() override
void errorOccurred (const String& error)
{
if (incomingImage.isValid())
lastSnapshot.setImage (incomingImage);
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 sharingFinished (bool success, bool isCapture)
{
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)

View file

@ -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)

View file

@ -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)
{

View file

@ -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>

View file

@ -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>

View file

@ -147,6 +147,10 @@
//#define JUCE_PLUGINHOST_AU 0
#endif
#ifndef JUCE_PLUGINHOST_LADSPA
//#define JUCE_PLUGINHOST_LADSPA 0
#endif
//==============================================================================
// juce_audio_utils flags:

View file

@ -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)

View file

@ -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)
{

View file

@ -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"/>

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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)

View file

@ -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)
{

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -150,6 +150,10 @@
//#define JUCE_PLUGINHOST_AU 0
#endif
#ifndef JUCE_PLUGINHOST_LADSPA
//#define JUCE_PLUGINHOST_LADSPA 0
#endif
//==============================================================================
// juce_audio_utils flags:

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);
String juceMidiCode, juceMidiImports, juceRuntimePermissionsCode;
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;
};
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 };
}
struct CameraCode
{
String imports;
String main;
};
CameraCode getCameraCode (const File& javaSourceFolder) const
{
String juceCameraImports, juceCameraCode;
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
juceCameraImports << "import android.hardware.camera2.*;" << newLine;
auto javaCameraFile = javaSourceFolder.getChildFile ("AndroidCamera.java");
auto juceCameraCodeAll = javaCameraFile.loadFileAsString();
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
{
MemoryOutputStream newFile;
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;
}
javaSourceLines = StringArray::fromLines (newFile.toString());
juceCameraCode << juceCameraCodeAll.fromFirstOccurrenceOf ("$$CameraApi21", false, false)
.upToFirstOccurrenceOf ("CameraApi21$$", false, false);
}
while (javaSourceLines.size() > 2
&& javaSourceLines[javaSourceLines.size() - 1].trim().isEmpty()
&& javaSourceLines[javaSourceLines.size() - 2].trim().isEmpty())
javaSourceLines.remove (javaSourceLines.size() - 1);
overwriteFileIfDifferentOrThrow (javaDestFile, javaSourceLines.joinIntoString (newLine));
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");

View file

@ -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();

View file

@ -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);

View file

@ -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"/>

View file

@ -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>

View file

@ -155,6 +155,10 @@
//#define JUCE_PLUGINHOST_AU 0
#endif
#ifndef JUCE_PLUGINHOST_LADSPA
//#define JUCE_PLUGINHOST_LADSPA 0
#endif
//==============================================================================
// juce_audio_utils flags:

View file

@ -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"/>

View file

@ -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>

View file

@ -150,6 +150,10 @@
//#define JUCE_PLUGINHOST_AU 0
#endif
#ifndef JUCE_PLUGINHOST_LADSPA
//#define JUCE_PLUGINHOST_LADSPA 0
#endif
//==============================================================================
// juce_audio_utils flags:

View file

@ -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;
};

View file

@ -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
};
//==============================================================================

View file

@ -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$$

View file

@ -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)
{

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -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)
{

View file

@ -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)

View file

@ -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

View file

@ -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)
};

View file

@ -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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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,33 +186,25 @@ 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;
}
NSData* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: sampleBuffer];
auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length);
WeakReference<Pimpl> weakRef (this);
MessageManager::callAsync ([weakRef, image]() mutable { if (weakRef != nullptr) weakRef->handleImageCapture (image); });
}];
}
}
void addListener (CameraDevice::Listener* listenerToAdd)
{
const ScopedLock sl (listenerLock);
listeners.addIfNotAlreadyThere (listenerToAdd);
if (listeners.size() == 1)
triggerImageCapture();
}
void removeListener (CameraDevice::Listener* listenerToRemove)
{
const ScopedLock sl (listenerLock);
listeners.removeFirstMatchingValue (listenerToRemove);
}
static StringArray getAvailableDevices()
{
StringArray results;
@ -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)

View file

@ -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);
{
const ScopedLock sl (callbackLock);
if (listeners.size() == 0)
addUser();
if (pictureTakenCallback == nullptr)
return;
}
listeners.addIfNotAlreadyThere (listenerToAdd);
}
WeakReference<Pimpl> weakRef (this);
MessageManager::callAsync ([weakRef, image]() mutable
{
if (weakRef == nullptr)
return;
void removeListener (CameraDevice::Listener* listenerToRemove)
{
const ScopedLock sl (listenerLock);
listeners.removeAllInstancesOf (listenerToRemove);
if (weakRef->pictureTakenCallback != nullptr)
weakRef->pictureTakenCallback (image);
if (listeners.size() == 0)
removeUser();
}
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)
{

View file

@ -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;