bug fixed

This commit is contained in:
nutchayut
2025-01-07 15:02:31 +07:00
parent f16e5f491a
commit 4f344e5e00
2634 changed files with 46314 additions and 9884 deletions

View File

@@ -0,0 +1,32 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import Foundation;
#import <Flutter/Flutter.h>
typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *);
/// Requests camera access permission.
///
/// If it is the first time requesting camera access, a permission dialog will show up on the
/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the
/// user will have to update the choice in Settings app.
///
/// @param handler if access permission is (or was previously) granted, completion handler will be
/// called without error; Otherwise completion handler will be called with error. Handler can be
/// called on an arbitrary dispatch queue.
extern void FLTRequestCameraPermissionWithCompletionHandler(
FLTCameraPermissionRequestCompletionHandler handler);
/// Requests audio access permission.
///
/// If it is the first time requesting audio access, a permission dialog will show up on the
/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the
/// user will have to update the choice in Settings app.
///
/// @param handler if access permission is (or was previously) granted, completion handler will be
/// called without error; Otherwise completion handler will be called with error. Handler can be
/// called on an arbitrary dispatch queue.
extern void FLTRequestAudioPermissionWithCompletionHandler(
FLTCameraPermissionRequestCompletionHandler handler);

View File

@@ -0,0 +1,10 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Flutter/Flutter.h>
#import "messages.g.h"
@interface CameraPlugin : NSObject <FlutterPlugin, FCPCameraApi>
@end

View File

@@ -0,0 +1,46 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This header is available in the Test module. Import via "@import camera_avfoundation.Test;"
#import "CameraPlugin.h"
#import "FLTCam.h"
#import "messages.g.h"
/// APIs exposed for unit testing.
@interface CameraPlugin ()
/// All FLTCam's state access and capture session related operations should be on run on this queue.
@property(nonatomic, strong) dispatch_queue_t captureSessionQueue;
/// An internal camera object that manages camera's state and performs camera operations.
@property(nonatomic, strong) FLTCam *camera;
/// Inject @p FlutterTextureRegistry and @p FlutterBinaryMessenger for unit testing.
- (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
messenger:(NSObject<FlutterBinaryMessenger> *)messenger;
/// Inject @p FlutterTextureRegistry, @p FlutterBinaryMessenger, and Pigeon callback handler for
/// unit testing.
- (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
messenger:(NSObject<FlutterBinaryMessenger> *)messenger
globalAPI:(FCPCameraGlobalEventApi *)globalAPI NS_DESIGNATED_INITIALIZER;
/// Hide the default public constructor.
- (instancetype)init NS_UNAVAILABLE;
/// Called by the @c NSNotificationManager each time the device's orientation is changed.
///
/// @param notification @c NSNotification instance containing a reference to the `UIDevice` object
/// that triggered the orientation change.
- (void)orientationChanged:(NSNotification *)notification;
/// Creates FLTCam on session queue and reports the creation result.
/// @param name the name of the camera.
/// @param settings the creation settings.
/// @param completion the callback to inform the Dart side of the plugin of creation.
- (void)createCameraOnSessionQueueWithName:(NSString *)name
settings:(FCPPlatformMediaSettings *)settings
completion:(void (^)(NSNumber *, FlutterError *))completion;
@end

View File

@@ -0,0 +1,28 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import AVFoundation;
@import Foundation;
@import UIKit;
#import "messages.g.h"
NS_ASSUME_NONNULL_BEGIN
/// Gets AVCaptureFlashMode from FLTFlashMode.
/// @param mode flash mode.
extern AVCaptureFlashMode FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode mode);
/// Gets UIDeviceOrientation from its Pigeon representation.
extern UIDeviceOrientation FCPGetUIDeviceOrientationForPigeonDeviceOrientation(
FCPPlatformDeviceOrientation orientation);
/// Gets a Pigeon representation of UIDeviceOrientation.
extern FCPPlatformDeviceOrientation FCPGetPigeonDeviceOrientationForOrientation(
UIDeviceOrientation orientation);
/// Gets VideoFormat from its Pigeon representation.
extern OSType FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup imageFormat);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,120 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import AVFoundation;
@import Foundation;
@import Flutter;
#import "CameraProperties.h"
#import "FLTCamMediaSettingsAVWrapper.h"
#import "messages.g.h"
NS_ASSUME_NONNULL_BEGIN
/// A class that manages camera's state and performs camera operations.
@interface FLTCam : NSObject <FlutterTexture>
@property(readonly, nonatomic) AVCaptureDevice *captureDevice;
@property(readonly, nonatomic) CGSize previewSize;
@property(assign, nonatomic) BOOL isPreviewPaused;
@property(nonatomic, copy) void (^onFrameAvailable)(void);
/// The API instance used to communicate with the Dart side of the plugin. Once initially set, this
/// should only ever be accessed on the main thread.
@property(nonatomic) FCPCameraEventApi *dartAPI;
@property(assign, nonatomic) FCPPlatformExposureMode exposureMode;
@property(assign, nonatomic) FCPPlatformFocusMode focusMode;
@property(assign, nonatomic) FCPPlatformFlashMode flashMode;
// Format used for video and image streaming.
@property(assign, nonatomic) FourCharCode videoFormat;
@property(assign, nonatomic) FCPPlatformImageFileFormat fileFormat;
@property(assign, nonatomic) CGFloat minimumAvailableZoomFactor;
@property(assign, nonatomic) CGFloat maximumAvailableZoomFactor;
/// Initializes an `FLTCam` instance.
/// @param cameraName a name used to uniquely identify the camera.
/// @param mediaSettings the media settings configuration parameters
/// @param mediaSettingsAVWrapper AVFoundation wrapper to perform media settings related operations
/// (for dependency injection in unit tests).
/// @param orientation the orientation of camera
/// @param captureSessionQueue the queue on which camera's capture session operations happen.
/// @param error report to the caller if any error happened creating the camera.
- (instancetype)initWithCameraName:(NSString *)cameraName
mediaSettings:(FCPPlatformMediaSettings *)mediaSettings
mediaSettingsAVWrapper:(FLTCamMediaSettingsAVWrapper *)mediaSettingsAVWrapper
orientation:(UIDeviceOrientation)orientation
captureSessionQueue:(dispatch_queue_t)captureSessionQueue
error:(NSError **)error;
/// Informs the Dart side of the plugin of the current camera state and capabilities.
- (void)reportInitializationState;
- (void)start;
- (void)stop;
- (void)setDeviceOrientation:(UIDeviceOrientation)orientation;
- (void)captureToFileWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
- (void)close;
- (void)setImageFileFormat:(FCPPlatformImageFileFormat)fileFormat;
/// Starts recording a video with an optional streaming messenger.
/// If the messenger is non-nil then it will be called for each
/// captured frame, allowing streaming concurrently with recording.
///
/// @param messenger Nullable messenger for capturing each frame.
- (void)startVideoRecordingWithCompletion:(void (^)(FlutterError *_Nullable))completion
messengerForStreaming:(nullable NSObject<FlutterBinaryMessenger> *)messenger;
- (void)stopVideoRecordingWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
- (void)pauseVideoRecording;
- (void)resumeVideoRecording;
- (void)lockCaptureOrientation:(FCPPlatformDeviceOrientation)orientation;
- (void)unlockCaptureOrientation;
- (void)setFlashMode:(FCPPlatformFlashMode)mode
withCompletion:(void (^)(FlutterError *_Nullable))completion;
- (void)setExposureMode:(FCPPlatformExposureMode)mode;
- (void)setFocusMode:(FCPPlatformFocusMode)mode;
- (void)applyFocusMode;
/// Acknowledges the receipt of one image stream frame.
///
/// This should be called each time a frame is received. Failing to call it may
/// cause later frames to be dropped instead of streamed.
- (void)receivedImageStreamData;
/// Applies FocusMode on the AVCaptureDevice.
///
/// If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use
/// AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to
/// AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor
/// AVCaptureFocusModeAutoFocus are supported focus mode will not be set.
/// If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use
/// AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not
/// be set.
///
/// @param focusMode The focus mode that should be applied to the @captureDevice instance.
/// @param captureDevice The AVCaptureDevice to which the @focusMode will be applied.
- (void)applyFocusMode:(FCPPlatformFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
- (void)pausePreview;
- (void)resumePreview;
- (void)setDescriptionWhileRecording:(NSString *)cameraName
withCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Sets the exposure point, in a (0,1) coordinate system.
///
/// If @c point is nil, the exposure point will reset to the center.
- (void)setExposurePoint:(nullable FCPPlatformPoint *)point
withCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Sets the focus point, in a (0,1) coordinate system.
///
/// If @c point is nil, the focus point will reset to the center.
- (void)setFocusPoint:(nullable FCPPlatformPoint *)point
withCompletion:(void (^)(FlutterError *_Nullable))completion;
- (void)setExposureOffset:(double)offset;
- (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger;
- (void)stopImageStream;
- (void)setZoomLevel:(CGFloat)zoom withCompletion:(void (^)(FlutterError *_Nullable))completion;
- (void)setUpCaptureSessionForAudio;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,112 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import AVFoundation;
@import Foundation;
NS_ASSUME_NONNULL_BEGIN
/**
* @interface FLTCamMediaSettingsAVWrapper
* @abstract An interface for performing media settings operations.
*
* @discussion
* xctest-expectation-checking implementation (`TestMediaSettingsAVWrapper`) of this interface can
* be injected into `camera-avfoundation` plugin allowing to run media-settings tests without any
* additional mocking of AVFoundation classes.
*/
@interface FLTCamMediaSettingsAVWrapper : NSObject
/**
* @method lockDevice:error:
* @abstract Requests exclusive access to configure device hardware properties.
* @param captureDevice The capture device.
* @param outError The optional error.
* @result A BOOL indicating whether the device was successfully locked for configuration.
*/
- (BOOL)lockDevice:(AVCaptureDevice *)captureDevice error:(NSError *_Nullable *_Nullable)outError;
/**
* @method unlockDevice:
* @abstract Release exclusive control over device hardware properties.
* @param captureDevice The capture device.
*/
- (void)unlockDevice:(AVCaptureDevice *)captureDevice;
/**
* @method beginConfigurationForSession:
* @abstract When paired with commitConfiguration, allows a client to batch multiple configuration
* operations on a running session into atomic updates.
* @param videoCaptureSession The video capture session.
*/
- (void)beginConfigurationForSession:(AVCaptureSession *)videoCaptureSession;
/**
* @method commitConfigurationForSession:
* @abstract When preceded by beginConfiguration, allows a client to batch multiple configuration
* operations on a running session into atomic updates.
* @param videoCaptureSession The video capture session.
*/
- (void)commitConfigurationForSession:(AVCaptureSession *)videoCaptureSession;
/**
* @method setMinFrameDuration:onDevice:
* @abstract Set receiver's current active minimum frame duration (the reciprocal of its max frame
* rate).
* @param duration The frame duration.
* @param captureDevice The capture device
*/
- (void)setMinFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice;
/**
* @method setMaxFrameDuration:onDevice:
* @abstract Set receiver's current active maximum frame duration (the reciprocal of its min frame
* rate).
* @param duration The frame duration.
* @param captureDevice The capture device
*/
- (void)setMaxFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice;
/**
* @method assetWriterAudioInputWithOutputSettings:
* @abstract Creates a new input of the audio media type to receive sample buffers for writing to
* the output file.
* @param outputSettings The settings used for encoding the audio appended to the output.
* @result An instance of `AVAssetWriterInput`.
*/
- (AVAssetWriterInput *)assetWriterAudioInputWithOutputSettings:
(nullable NSDictionary<NSString *, id> *)outputSettings;
/**
* @method assetWriterVideoInputWithOutputSettings:
* @abstract Creates a new input of the video media type to receive sample buffers for writing to
* the output file.
* @param outputSettings The settings used for encoding the video appended to the output.
* @result An instance of `AVAssetWriterInput`.
*/
- (AVAssetWriterInput *)assetWriterVideoInputWithOutputSettings:
(nullable NSDictionary<NSString *, id> *)outputSettings;
/**
* @method addInput:toAssetWriter:
* @abstract Adds an input to the asset writer.
* @param writerInput The `AVAssetWriterInput` object to be added.
* @param writer The `AVAssetWriter` object.
*/
- (void)addInput:(AVAssetWriterInput *)writerInput toAssetWriter:(AVAssetWriter *)writer;
/**
* @method recommendedVideoSettingsForAssetWriterWithFileType:forOutput:
* @abstract Specifies the recommended video settings for `AVCaptureVideoDataOutput`.
* @param fileType Specifies the UTI of the file type to be written (see AVMediaFormat.h for a list
* of file format UTIs).
* @param output The `AVCaptureVideoDataOutput` instance.
* @result A fully populated dictionary of keys and values that are compatible with AVAssetWriter.
*/
- (nullable NSDictionary<NSString *, id> *)
recommendedVideoSettingsForAssetWriterWithFileType:(AVFileType)fileType
forOutput:(AVCaptureVideoDataOutput *)output;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "FLTCam.h"
#import "FLTSavePhotoDelegate.h"
/// Determines the video dimensions (width and height) for a given capture device format.
/// Used in tests to mock CMVideoFormatDescriptionGetDimensions.
typedef CMVideoDimensions (^VideoDimensionsForFormat)(AVCaptureDeviceFormat *);
/// Factory block returning an AVCaptureDevice.
/// Used in tests to inject a device into FLTCam.
typedef AVCaptureDevice * (^CaptureDeviceFactory)(void);
@interface FLTImageStreamHandler : NSObject <FlutterStreamHandler>
/// The queue on which `eventSink` property should be accessed.
@property(nonatomic, strong) dispatch_queue_t captureSessionQueue;
/// The event sink to stream camera events to Dart.
///
/// The property should only be accessed on `captureSessionQueue`.
/// The block itself should be invoked on the main queue.
@property FlutterEventSink eventSink;
@end
// APIs exposed for unit testing.
@interface FLTCam ()
/// The output for video capturing.
@property(readonly, nonatomic) AVCaptureVideoDataOutput *captureVideoOutput;
/// The output for photo capturing. Exposed setter for unit tests.
@property(strong, nonatomic) AVCapturePhotoOutput *capturePhotoOutput;
/// True when images from the camera are being streamed.
@property(assign, nonatomic) BOOL isStreamingImages;
/// A dictionary to retain all in-progress FLTSavePhotoDelegates. The key of the dictionary is the
/// AVCapturePhotoSettings's uniqueID for each photo capture operation, and the value is the
/// FLTSavePhotoDelegate that handles the result of each photo capture operation. Note that photo
/// capture operations may overlap, so FLTCam has to keep track of multiple delegates in progress,
/// instead of just a single delegate reference.
@property(readonly, nonatomic)
NSMutableDictionary<NSNumber *, FLTSavePhotoDelegate *> *inProgressSavePhotoDelegates;
/// Delegate callback when receiving a new video or audio sample.
/// Exposed for unit tests.
- (void)captureOutput:(AVCaptureOutput *)output
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection;
/// Initializes a camera instance.
/// Allows for injecting dependencies that are usually internal.
- (instancetype)initWithCameraName:(NSString *)cameraName
mediaSettings:(FCPPlatformMediaSettings *)mediaSettings
mediaSettingsAVWrapper:(FLTCamMediaSettingsAVWrapper *)mediaSettingsAVWrapper
orientation:(UIDeviceOrientation)orientation
videoCaptureSession:(AVCaptureSession *)videoCaptureSession
audioCaptureSession:(AVCaptureSession *)audioCaptureSession
captureSessionQueue:(dispatch_queue_t)captureSessionQueue
error:(NSError **)error;
/// Initializes a camera instance.
/// Allows for testing with specified resolution, audio preference, orientation,
/// and direct access to capture sessions and blocks.
- (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings
mediaSettingsAVWrapper:(FLTCamMediaSettingsAVWrapper *)mediaSettingsAVWrapper
orientation:(UIDeviceOrientation)orientation
videoCaptureSession:(AVCaptureSession *)videoCaptureSession
audioCaptureSession:(AVCaptureSession *)audioCaptureSession
captureSessionQueue:(dispatch_queue_t)captureSessionQueue
captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory
videoDimensionsForFormat:(VideoDimensionsForFormat)videoDimensionsForFormat
error:(NSError **)error;
/// Start streaming images.
- (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger
imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler;
@end

View File

@@ -0,0 +1,33 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@import AVFoundation;
@import Flutter;
@import Foundation;
NS_ASSUME_NONNULL_BEGIN
/// The completion handler block for save photo operations.
/// Can be called from either main queue or IO queue.
/// If success, `error` will be present and `path` will be nil. Otherewise, `error` will be nil and
/// `path` will be present.
/// @param path the path for successfully saved photo file.
/// @param error photo capture error or IO error.
typedef void (^FLTSavePhotoDelegateCompletionHandler)(NSString *_Nullable path,
NSError *_Nullable error);
/// Delegate object that handles photo capture results.
@interface FLTSavePhotoDelegate : NSObject <AVCapturePhotoCaptureDelegate>
/// Initialize a photo capture delegate.
/// @param path the path for captured photo file.
/// @param ioQueue the queue on which captured photos are written to disk.
/// @param completionHandler The completion handler block for save photo operations. Can
/// be called from either main queue or IO queue.
- (instancetype)initWithPath:(NSString *)path
ioQueue:(dispatch_queue_t)ioQueue
completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "FLTSavePhotoDelegate.h"
/// API exposed for unit tests.
@interface FLTSavePhotoDelegate ()
/// The completion handler block for capture and save photo operations.
/// Can be called from either main queue or IO queue.
/// Exposed for unit tests to manually trigger the completion.
@property(readonly, nonatomic) FLTSavePhotoDelegateCompletionHandler completionHandler;
/// The path for captured photo file.
/// Exposed for unit tests to verify the captured photo file path.
@property(readwrite, nonatomic) NSString *filePath;
/// Handler to write captured photo data into a file.
/// @param error the capture error.
/// @param photoDataProvider a closure that provides photo data.
- (void)handlePhotoCaptureResultWithError:(NSError *)error
photoDataProvider:(NSData * (^)(void))photoDataProvider;
@end

View File

@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
/// A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching
/// its underlying engine calls to the main thread.
@interface FLTThreadSafeEventChannel : NSObject
/// Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object.
/// @param channel The FlutterEventChannel object to be wrapped.
- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel;
/// Registers a handler on the main thread for stream setup requests from the Flutter side.
/// The completion block runs on the main thread.
- (void)setStreamHandler:(nullable NSObject<FlutterStreamHandler> *)handler
completion:(void (^)(void))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,19 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Queue-specific context data to be associated with the capture session queue.
extern const char* FLTCaptureSessionQueueSpecific;
/// Ensures the given block to be run on the main queue.
/// If caller site is already on the main queue, the block will be run
/// synchronously. Otherwise, the block will be dispatched asynchronously to the
/// main queue.
/// @param block the block to be run on the main queue.
extern void FLTEnsureToRunOnMainQueue(dispatch_block_t block);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,9 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Foundation/Foundation.h>
#import <camera_avfoundation/CameraPlugin.h>
FOUNDATION_EXPORT double cameraVersionNumber;
FOUNDATION_EXPORT const unsigned char cameraVersionString[];

View File

@@ -0,0 +1,320 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v18.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#import <Foundation/Foundation.h>
@protocol FlutterBinaryMessenger;
@protocol FlutterMessageCodec;
@class FlutterError;
@class FlutterStandardTypedData;
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, FCPPlatformCameraLensDirection) {
/// Front facing camera (a user looking at the screen is seen by the camera).
FCPPlatformCameraLensDirectionFront = 0,
/// Back facing camera (a user looking at the screen is not seen by the camera).
FCPPlatformCameraLensDirectionBack = 1,
/// External camera which may not be mounted to the device.
FCPPlatformCameraLensDirectionExternal = 2,
};
/// Wrapper for FCPPlatformCameraLensDirection to allow for nullability.
@interface FCPPlatformCameraLensDirectionBox : NSObject
@property(nonatomic, assign) FCPPlatformCameraLensDirection value;
- (instancetype)initWithValue:(FCPPlatformCameraLensDirection)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformDeviceOrientation) {
FCPPlatformDeviceOrientationPortraitUp = 0,
FCPPlatformDeviceOrientationLandscapeLeft = 1,
FCPPlatformDeviceOrientationPortraitDown = 2,
FCPPlatformDeviceOrientationLandscapeRight = 3,
};
/// Wrapper for FCPPlatformDeviceOrientation to allow for nullability.
@interface FCPPlatformDeviceOrientationBox : NSObject
@property(nonatomic, assign) FCPPlatformDeviceOrientation value;
- (instancetype)initWithValue:(FCPPlatformDeviceOrientation)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformExposureMode) {
FCPPlatformExposureModeAuto = 0,
FCPPlatformExposureModeLocked = 1,
};
/// Wrapper for FCPPlatformExposureMode to allow for nullability.
@interface FCPPlatformExposureModeBox : NSObject
@property(nonatomic, assign) FCPPlatformExposureMode value;
- (instancetype)initWithValue:(FCPPlatformExposureMode)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformFlashMode) {
FCPPlatformFlashModeOff = 0,
FCPPlatformFlashModeAuto = 1,
FCPPlatformFlashModeAlways = 2,
FCPPlatformFlashModeTorch = 3,
};
/// Wrapper for FCPPlatformFlashMode to allow for nullability.
@interface FCPPlatformFlashModeBox : NSObject
@property(nonatomic, assign) FCPPlatformFlashMode value;
- (instancetype)initWithValue:(FCPPlatformFlashMode)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformFocusMode) {
FCPPlatformFocusModeAuto = 0,
FCPPlatformFocusModeLocked = 1,
};
/// Wrapper for FCPPlatformFocusMode to allow for nullability.
@interface FCPPlatformFocusModeBox : NSObject
@property(nonatomic, assign) FCPPlatformFocusMode value;
- (instancetype)initWithValue:(FCPPlatformFocusMode)value;
@end
/// Pigeon version of ImageFileFormat.
typedef NS_ENUM(NSUInteger, FCPPlatformImageFileFormat) {
FCPPlatformImageFileFormatJpeg = 0,
FCPPlatformImageFileFormatHeif = 1,
};
/// Wrapper for FCPPlatformImageFileFormat to allow for nullability.
@interface FCPPlatformImageFileFormatBox : NSObject
@property(nonatomic, assign) FCPPlatformImageFileFormat value;
- (instancetype)initWithValue:(FCPPlatformImageFileFormat)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformImageFormatGroup) {
FCPPlatformImageFormatGroupBgra8888 = 0,
FCPPlatformImageFormatGroupYuv420 = 1,
};
/// Wrapper for FCPPlatformImageFormatGroup to allow for nullability.
@interface FCPPlatformImageFormatGroupBox : NSObject
@property(nonatomic, assign) FCPPlatformImageFormatGroup value;
- (instancetype)initWithValue:(FCPPlatformImageFormatGroup)value;
@end
typedef NS_ENUM(NSUInteger, FCPPlatformResolutionPreset) {
FCPPlatformResolutionPresetLow = 0,
FCPPlatformResolutionPresetMedium = 1,
FCPPlatformResolutionPresetHigh = 2,
FCPPlatformResolutionPresetVeryHigh = 3,
FCPPlatformResolutionPresetUltraHigh = 4,
FCPPlatformResolutionPresetMax = 5,
};
/// Wrapper for FCPPlatformResolutionPreset to allow for nullability.
@interface FCPPlatformResolutionPresetBox : NSObject
@property(nonatomic, assign) FCPPlatformResolutionPreset value;
- (instancetype)initWithValue:(FCPPlatformResolutionPreset)value;
@end
@class FCPPlatformCameraDescription;
@class FCPPlatformCameraState;
@class FCPPlatformMediaSettings;
@class FCPPlatformPoint;
@class FCPPlatformSize;
@interface FCPPlatformCameraDescription : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithName:(NSString *)name
lensDirection:(FCPPlatformCameraLensDirection)lensDirection;
/// The name of the camera device.
@property(nonatomic, copy) NSString *name;
/// The direction the camera is facing.
@property(nonatomic, assign) FCPPlatformCameraLensDirection lensDirection;
@end
@interface FCPPlatformCameraState : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithPreviewSize:(FCPPlatformSize *)previewSize
exposureMode:(FCPPlatformExposureMode)exposureMode
focusMode:(FCPPlatformFocusMode)focusMode
exposurePointSupported:(BOOL)exposurePointSupported
focusPointSupported:(BOOL)focusPointSupported;
/// The size of the preview, in pixels.
@property(nonatomic, strong) FCPPlatformSize *previewSize;
/// The default exposure mode
@property(nonatomic, assign) FCPPlatformExposureMode exposureMode;
/// The default focus mode
@property(nonatomic, assign) FCPPlatformFocusMode focusMode;
/// Whether setting exposure points is supported.
@property(nonatomic, assign) BOOL exposurePointSupported;
/// Whether setting focus points is supported.
@property(nonatomic, assign) BOOL focusPointSupported;
@end
@interface FCPPlatformMediaSettings : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithResolutionPreset:(FCPPlatformResolutionPreset)resolutionPreset
framesPerSecond:(nullable NSNumber *)framesPerSecond
videoBitrate:(nullable NSNumber *)videoBitrate
audioBitrate:(nullable NSNumber *)audioBitrate
enableAudio:(BOOL)enableAudio;
@property(nonatomic, assign) FCPPlatformResolutionPreset resolutionPreset;
@property(nonatomic, strong, nullable) NSNumber *framesPerSecond;
@property(nonatomic, strong, nullable) NSNumber *videoBitrate;
@property(nonatomic, strong, nullable) NSNumber *audioBitrate;
@property(nonatomic, assign) BOOL enableAudio;
@end
@interface FCPPlatformPoint : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithX:(double)x y:(double)y;
@property(nonatomic, assign) double x;
@property(nonatomic, assign) double y;
@end
@interface FCPPlatformSize : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithWidth:(double)width height:(double)height;
@property(nonatomic, assign) double width;
@property(nonatomic, assign) double height;
@end
/// The codec used by FCPCameraApi.
NSObject<FlutterMessageCodec> *FCPCameraApiGetCodec(void);
@protocol FCPCameraApi
/// Returns the list of available cameras.
- (void)availableCamerasWithCompletion:(void (^)(NSArray<FCPPlatformCameraDescription *> *_Nullable,
FlutterError *_Nullable))completion;
/// Create a new camera with the given settings, and returns its ID.
- (void)createCameraWithName:(NSString *)cameraName
settings:(FCPPlatformMediaSettings *)settings
completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Initializes the camera with the given ID.
- (void)initializeCamera:(NSInteger)cameraId
withImageFormat:(FCPPlatformImageFormatGroup)imageFormat
completion:(void (^)(FlutterError *_Nullable))completion;
/// Begins streaming frames from the camera.
- (void)startImageStreamWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Stops streaming frames from the camera.
- (void)stopImageStreamWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Called by the Dart side of the plugin when it has received the last image
/// frame sent.
///
/// This is used to throttle sending frames across the channel.
- (void)receivedImageStreamDataWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Indicates that the given camera is no longer being used on the Dart side,
/// and any associated resources can be cleaned up.
- (void)disposeCamera:(NSInteger)cameraId completion:(void (^)(FlutterError *_Nullable))completion;
/// Locks the camera capture to the current device orientation.
- (void)lockCaptureOrientation:(FCPPlatformDeviceOrientation)orientation
completion:(void (^)(FlutterError *_Nullable))completion;
/// Unlocks camera capture orientation, allowing it to automatically adapt to
/// device orientation.
- (void)unlockCaptureOrientationWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Takes a picture with the current settings, and returns the path to the
/// resulting file.
- (void)takePictureWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
/// Does any preprocessing necessary before beginning to record video.
- (void)prepareForVideoRecordingWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Begins recording video, optionally enabling streaming to Dart at the same
/// time.
- (void)startVideoRecordingWithStreaming:(BOOL)enableStream
completion:(void (^)(FlutterError *_Nullable))completion;
/// Stops recording video, and results the path to the resulting file.
- (void)stopVideoRecordingWithCompletion:(void (^)(NSString *_Nullable,
FlutterError *_Nullable))completion;
/// Pauses video recording.
- (void)pauseVideoRecordingWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Resumes a previously paused video recording.
- (void)resumeVideoRecordingWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Switches the camera to the given flash mode.
- (void)setFlashMode:(FCPPlatformFlashMode)mode
completion:(void (^)(FlutterError *_Nullable))completion;
/// Switches the camera to the given exposure mode.
- (void)setExposureMode:(FCPPlatformExposureMode)mode
completion:(void (^)(FlutterError *_Nullable))completion;
/// Anchors auto-exposure to the given point in (0,1) coordinate space.
///
/// A null value resets to the default exposure point.
- (void)setExposurePoint:(nullable FCPPlatformPoint *)point
completion:(void (^)(FlutterError *_Nullable))completion;
/// Returns the minimum exposure offset supported by the camera.
- (void)getMinimumExposureOffset:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Returns the maximum exposure offset supported by the camera.
- (void)getMaximumExposureOffset:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Sets the exposure offset manually to the given value.
- (void)setExposureOffset:(double)offset completion:(void (^)(FlutterError *_Nullable))completion;
/// Switches the camera to the given focus mode.
- (void)setFocusMode:(FCPPlatformFocusMode)mode
completion:(void (^)(FlutterError *_Nullable))completion;
/// Anchors auto-focus to the given point in (0,1) coordinate space.
///
/// A null value resets to the default focus point.
- (void)setFocusPoint:(nullable FCPPlatformPoint *)point
completion:(void (^)(FlutterError *_Nullable))completion;
/// Returns the minimum zoom level supported by the camera.
- (void)getMinimumZoomLevel:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Returns the maximum zoom level supported by the camera.
- (void)getMaximumZoomLevel:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
/// Sets the zoom factor.
- (void)setZoomLevel:(double)zoom completion:(void (^)(FlutterError *_Nullable))completion;
/// Pauses streaming of preview frames.
- (void)pausePreviewWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Resumes a previously paused preview stream.
- (void)resumePreviewWithCompletion:(void (^)(FlutterError *_Nullable))completion;
/// Changes the camera used while recording video.
///
/// This should only be called while video recording is active.
- (void)updateDescriptionWhileRecordingCameraName:(NSString *)cameraName
completion:(void (^)(FlutterError *_Nullable))completion;
/// Sets the file format used for taking pictures.
- (void)setImageFileFormat:(FCPPlatformImageFileFormat)format
completion:(void (^)(FlutterError *_Nullable))completion;
@end
extern void SetUpFCPCameraApi(id<FlutterBinaryMessenger> binaryMessenger,
NSObject<FCPCameraApi> *_Nullable api);
extern void SetUpFCPCameraApiWithSuffix(id<FlutterBinaryMessenger> binaryMessenger,
NSObject<FCPCameraApi> *_Nullable api,
NSString *messageChannelSuffix);
/// The codec used by FCPCameraGlobalEventApi.
NSObject<FlutterMessageCodec> *FCPCameraGlobalEventApiGetCodec(void);
/// Handler for native callbacks that are not tied to a specific camera ID.
@interface FCPCameraGlobalEventApi : NSObject
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
messageChannelSuffix:(nullable NSString *)messageChannelSuffix;
/// Called when the device's physical orientation changes.
- (void)deviceOrientationChangedOrientation:(FCPPlatformDeviceOrientation)orientation
completion:(void (^)(FlutterError *_Nullable))completion;
@end
/// The codec used by FCPCameraEventApi.
NSObject<FlutterMessageCodec> *FCPCameraEventApiGetCodec(void);
/// Handler for native callbacks that are tied to a specific camera ID.
///
/// This is intended to be initialized with the camera ID as a suffix.
@interface FCPCameraEventApi : NSObject
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
messageChannelSuffix:(nullable NSString *)messageChannelSuffix;
/// Called when the camera is inialitized for use.
- (void)initializedWithState:(FCPPlatformCameraState *)initialState
completion:(void (^)(FlutterError *_Nullable))completion;
/// Called when an error occurs in the camera.
///
/// This should be used for errors that occur outside of the context of
/// handling a specific HostApi call, such as during streaming.
- (void)reportError:(NSString *)message completion:(void (^)(FlutterError *_Nullable))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,17 @@
framework module camera_avfoundation {
umbrella header "camera_avfoundation-umbrella.h"
export *
module * { export * }
explicit module Test {
header "CameraPlugin_Test.h"
header "CameraPermissionUtils.h"
header "CameraProperties.h"
header "FLTCam.h"
header "FLTCam_Test.h"
header "FLTSavePhotoDelegate_Test.h"
header "FLTThreadSafeEventChannel.h"
header "QueueUtils.h"
}
}

View File

@@ -0,0 +1,278 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/CameraPermissionUtils.h</key>
<data>
bFSPpi/SPZl6kA1A2dNV1EJvO+Y=
</data>
<key>Headers/CameraPlugin.h</key>
<data>
moy9n83mCZ9Thp89wrPlQsti1f0=
</data>
<key>Headers/CameraPlugin_Test.h</key>
<data>
buWCKC/yAT0yuy20UEAmdJKT7Dg=
</data>
<key>Headers/CameraProperties.h</key>
<data>
kc4LZE3Ysx1FpGXVLokcjLeHU+s=
</data>
<key>Headers/FLTCam.h</key>
<data>
nQaj9wTYXoluHJ/++ny6Z2Gm7kU=
</data>
<key>Headers/FLTCamMediaSettingsAVWrapper.h</key>
<data>
gVa7pDil4JZpdreVozcYd/H7pAk=
</data>
<key>Headers/FLTCam_Test.h</key>
<data>
/hQKoqT7ra03ZLV08gUcJD633qA=
</data>
<key>Headers/FLTSavePhotoDelegate.h</key>
<data>
4K02d/P8OgweYt88zJ7wZc5RHOA=
</data>
<key>Headers/FLTSavePhotoDelegate_Test.h</key>
<data>
dwaaSNI3j3QhvTQRmq7pTKuZXfs=
</data>
<key>Headers/FLTThreadSafeEventChannel.h</key>
<data>
JM7zBtB98TNyn1pqdThT7Omqdbc=
</data>
<key>Headers/QueueUtils.h</key>
<data>
r72smLzzu9OEiD/1aQQCwCCq0Rc=
</data>
<key>Headers/camera_avfoundation-umbrella.h</key>
<data>
se8+u2UIVRmcLVsp/Gko5ZCDEAI=
</data>
<key>Headers/messages.g.h</key>
<data>
8n/v0k+Rh9dtIGNPmZq3DvIQAa4=
</data>
<key>Info.plist</key>
<data>
y4Go6X7w3rM+7w0C6yuuMRr98Vc=
</data>
<key>Modules/module.modulemap</key>
<data>
J+3LjGHW8sBp+j3WW4oQ93GhBms=
</data>
<key>camera_avfoundation_privacy.bundle/Info.plist</key>
<data>
xaWexJ41pDAzHmys2HzzSORiLXU=
</data>
<key>camera_avfoundation_privacy.bundle/PrivacyInfo.xcprivacy</key>
<data>
/LX0ZlwxwIAIhjZaDB8EiH5KpXA=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/CameraPermissionUtils.h</key>
<dict>
<key>hash2</key>
<data>
tr6hym1tYEc709pdU+TPU2wo5/BhCTqq24C9jGijIIw=
</data>
</dict>
<key>Headers/CameraPlugin.h</key>
<dict>
<key>hash2</key>
<data>
9gHBAEhLyULUgRJkL8/kMiTkUrmxSi8YeLtWYCnXwuY=
</data>
</dict>
<key>Headers/CameraPlugin_Test.h</key>
<dict>
<key>hash2</key>
<data>
sq+ccl87Bzh5DBI1+z111jXyM9y/UGPndxYYPNtT2m8=
</data>
</dict>
<key>Headers/CameraProperties.h</key>
<dict>
<key>hash2</key>
<data>
2thGS/DnpvNNfVdAuIDm/wJxe8fFuezwBqzDVBf5eQY=
</data>
</dict>
<key>Headers/FLTCam.h</key>
<dict>
<key>hash2</key>
<data>
vr5lDDrICtcdPYuGO0OXv9geUgRBWM0XEN1revHVehI=
</data>
</dict>
<key>Headers/FLTCamMediaSettingsAVWrapper.h</key>
<dict>
<key>hash2</key>
<data>
g66uG4D56hT6hlg7F1tZsbL0OIFnPLSeuVpOFJcU+3k=
</data>
</dict>
<key>Headers/FLTCam_Test.h</key>
<dict>
<key>hash2</key>
<data>
c8FKVrP7hO+3iZltVhTFd9KE321AfjGKc0BMu+bs7qU=
</data>
</dict>
<key>Headers/FLTSavePhotoDelegate.h</key>
<dict>
<key>hash2</key>
<data>
23ZBSIif8zJWnyYHf7ttYIlU1ehN/Px1RRL0iDOvCK4=
</data>
</dict>
<key>Headers/FLTSavePhotoDelegate_Test.h</key>
<dict>
<key>hash2</key>
<data>
th1ztnrpJ1h14P5D9so+cuD1WkV4OV5e+A6bkn15eTE=
</data>
</dict>
<key>Headers/FLTThreadSafeEventChannel.h</key>
<dict>
<key>hash2</key>
<data>
RmA9acMqu0bn+A0QUeg5ucjg4mKTlRMhtQ48IPtdAq4=
</data>
</dict>
<key>Headers/QueueUtils.h</key>
<dict>
<key>hash2</key>
<data>
Q4Rp0ZE4xlEbXxb0qYLcc0KpjGZ88Y6LjAFIQNr84rs=
</data>
</dict>
<key>Headers/camera_avfoundation-umbrella.h</key>
<dict>
<key>hash2</key>
<data>
MyNmNNncpW1xJzLtL43VDBA8xg0f5YKwRZoQmZvjMF0=
</data>
</dict>
<key>Headers/messages.g.h</key>
<dict>
<key>hash2</key>
<data>
cHkoQoXbpbqOeXmu3gsLzbNTy0XF6alvPRyqLhTQHW8=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash2</key>
<data>
Sw4B5vQDdQbwPNU2k69TyzgrgK/S8ZgB1/LyGozoNuI=
</data>
</dict>
<key>camera_avfoundation_privacy.bundle/Info.plist</key>
<dict>
<key>hash2</key>
<data>
WeS92PGGt0QIvGPrpPCH/w6sPJndljeyoEWA1DUy/pA=
</data>
</dict>
<key>camera_avfoundation_privacy.bundle/PrivacyInfo.xcprivacy</key>
<dict>
<key>hash2</key>
<data>
bS2g2NkwIn1CjB2TY7CtbjoS4sm2jFzilxWKdBL8jDE=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>