Browse Source

[camera] Init plugin camera_aurora

camera
Vitaliy Zarubin 1 year ago
parent
commit
f41d9d9204
  1. 2
      example/aurora/desktop/ru.auroraos.flutter_example_packages.desktop
  2. 64
      example/lib/packages/camera/extension/camera_controller.dart
  3. 35
      example/lib/packages/camera/extension/camera_description.dart
  4. 7
      example/lib/packages/camera/extension/export.dart
  5. 21
      example/lib/packages/camera/extension/uint8_list.dart
  6. 11
      example/lib/packages/camera/model.dart
  7. 26
      example/lib/packages/camera/package.dart
  8. 162
      example/lib/packages/camera/page.dart
  9. 106
      example/lib/packages/camera/widgets/camera_body.dart
  10. 125
      example/lib/packages/camera/widgets/camera_control_panel.dart
  11. 96
      example/lib/packages/camera/widgets/cameras_loading.dart
  12. 83
      example/lib/packages/camera/widgets/cameras_select.dart
  13. 2
      example/lib/packages/packages.dart
  14. 12
      example/pubspec.yaml
  15. 30
      packages/camera/camera_aurora/.gitignore
  16. 30
      packages/camera/camera_aurora/README.md
  17. 4
      packages/camera/camera_aurora/analysis_options.yaml
  18. 36
      packages/camera/camera_aurora/aurora/CMakeLists.txt
  19. 273
      packages/camera/camera_aurora/aurora/camera_aurora_plugin.cpp
  20. 54
      packages/camera/camera_aurora/aurora/include/camera_aurora/camera_aurora_plugin.h
  21. 14
      packages/camera/camera_aurora/aurora/include/camera_aurora/globals.h
  22. 155
      packages/camera/camera_aurora/lib/camera_aurora.dart
  23. 167
      packages/camera/camera_aurora/lib/camera_aurora_method_channel.dart
  24. 72
      packages/camera/camera_aurora/lib/camera_aurora_platform_interface.dart
  25. 43
      packages/camera/camera_aurora/lib/type_conversion.dart
  26. 29
      packages/camera/camera_aurora/pubspec.yaml
  27. 14
      packages/xdga_directories/lib/xdga_directories.dart

2
example/aurora/desktop/ru.auroraos.flutter_example_packages.desktop

@ -7,6 +7,6 @@ Exec=/usr/bin/ru.auroraos.flutter_example_packages
X-Nemo-Application-Type=silica-qt5
[X-Application]
Permissions=DeviceInfo;UserDirs;Sensors
Permissions=DeviceInfo;UserDirs;Sensors;Camera
OrganizationName=ru.auroraos
ApplicationName=flutter_example_packages

64
example/lib/packages/camera/extension/camera_controller.dart

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart' as provider;
import 'package:path_provider/path_provider.dart';
import 'uint8_list.dart';
extension ExtCameraController on CameraController {
/// Get photo
Future<File?> takeImageFile() async {
if (!value.isInitialized) {
return null;
}
if (value.isTakingPicture) {
return null;
}
try {
// Get image
final picture = await takePicture();
// Get bytes
final bytes = await picture.readAsBytes();
// Get path
final directory = await provider.getExternalStorageDirectories(
type: StorageDirectory.pictures);
// Save to file
final file = await bytes.writeToFile(directory![0], picture);
// Return saved file
return file;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
/// Get video
Future<File?> takeVideoFileAndStopRecording() async {
if (!value.isInitialized) {
return null;
}
if (!value.isRecordingVideo) {
return null;
}
try {
// Stop recording
final video = await stopVideoRecording();
// Get bytes
final bytes = await video.readAsBytes();
// Get path
final directory = await provider.getApplicationDocumentsDirectory();
// Save to file
final file = await bytes.writeToFile(directory, video);
// Return saved file
return file;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
}

35
example/lib/packages/camera/extension/camera_description.dart

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
extension ExtCameraDescription on CameraDescription {
/// Get [CameraController]
Future<CameraController?> getCameraController() async {
final cameraController = CameraController(
this,
ResolutionPreset.medium,
enableAudio: true,
imageFormatGroup: ImageFormatGroup.jpeg,
);
try {
await cameraController.initialize();
return cameraController;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
/// Get Icon by direction
IconData getIcon() {
switch (lensDirection) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
}
}
}

7
example/lib/packages/camera/extension/export.dart

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
library camera;
export './camera_controller.dart';
export './camera_description.dart';
export './uint8_list.dart';

21
example/lib/packages/camera/extension/uint8_list.dart

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:path/path.dart' as p;
extension ExtUint8List on Uint8List {
Future<File> writeToFile(Directory directory, XFile file) {
if (file.name.isEmpty) {
/// @todo XFile.fromData not work name file
return File(p.join(directory.path,
'${DateTime.now().millisecondsSinceEpoch}.${file.mimeType == 'video/mp4' ? 'mp4' : 'jpg'}'))
.writeAsBytes(this);
} else {
return File(p.join(directory.path, file.name)).writeAsBytes(this);
}
}
}

11
example/lib/packages/camera/model.dart

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/widgets.dart';
import 'package:scoped_model/scoped_model.dart';
/// Model for [CameraPage]
class CameraModel extends Model {
/// Get [ScopedModel]
static CameraModel of(BuildContext context) =>
ScopedModel.of<CameraModel>(context);
}

26
example/lib/packages/camera/package.dart

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter_example_packages/base/package/package_page.dart';
import 'package:get_it/get_it.dart';
import 'model.dart';
import 'page.dart';
/// Package values
final packageCamera = PackagePage(
key: 'camera',
descEN: '''
A Flutter plugin for Aurora, iOS, Android and Web allowing access
to the device cameras.
''',
descRU: '''
Плагин Flutter для Aurora, iOS, Android и Интернета, обеспечивающий доступ
к камерам устройства.
''',
version: '0.10.5+2',
isPlatformDependent: true,
page: () => CameraPage(),
init: () {
GetIt.instance.registerFactory<CameraModel>(() => CameraModel());
},
);

162
example/lib/packages/camera/page.dart

@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/base/di/app_di.dart';
import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/packages/camera/extension/export.dart';
import 'package:flutter_example_packages/packages/camera/widgets/camera_body.dart';
import 'package:flutter_example_packages/packages/camera/widgets/camera_control_panel.dart';
import 'package:flutter_example_packages/packages/camera/widgets/cameras_loading.dart';
import 'package:flutter_example_packages/packages/camera/widgets/cameras_select.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/layouts/block_layout.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'model.dart';
import 'package.dart';
class CameraPage extends AppStatefulWidget {
CameraPage({
super.key,
});
final Package package = packageCamera;
@override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends AppState<CameraPage> {
CameraController? _cameraController;
File? _photo;
File? _video;
bool _loading = false;
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return BlockLayout<CameraModel>(
model: getIt<CameraModel>(),
title: widget.package.key,
builder: (context, child, model) {
return Padding(
padding: const EdgeInsets.all(20),
child: CamerasLoading(
package: widget.package,
builder: (context, cameras) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 1,
child: Column(
children: [
Flexible(
flex: 0,
child: CamerasSelect(
disable: _loading,
cameras: cameras,
onChange: (controller) => setState(() {
_photo = null;
_cameraController = controller;
}),
),
),
const SizedBox(height: 5),
Flexible(
flex: 1,
child: CameraBody(
loading: _loading,
controller: _cameraController,
photo: _photo,
),
),
CameraControlPanel(
disable: _loading,
controller: _cameraController,
photo: _photo,
// Start record video
onRecordingStart: () => _cameraController
?.startVideoRecording()
.then((picture) {
if (mounted) {
setState(() {});
}
}),
// Pause record video if record already start
onRecordingPause: () => _cameraController
?.pauseVideoRecording()
.then((value) {
if (mounted) {
setState(() {});
}
}),
// Resume record video if record already start and will pause
onRecordingResume: () => _cameraController
?.resumeVideoRecording()
.then((value) {
if (mounted) {
setState(() {});
}
}),
// Clear photo
onClearPhoto: () {
if (mounted) {
setState(() {
_photo = null;
});
}
},
// Stop record video and save to file (custom extension)
onRecordingStop: () => _cameraController
?.takeVideoFileAndStopRecording()
.then((video) {
if (mounted) {
showMessage(video);
setState(() {
_video = video;
});
}
}),
// Take photo and save to file (custom extension)
onTakePhoto: () => setState(() {
_loading = true;
_cameraController?.takeImageFile().then((photo) {
if (mounted) {
showMessage(photo);
setState(() {
_loading = false;
_photo = photo;
});
}
});
}),
),
],
),
),
],
);
},
),
);
},
);
}
void showMessage(File? file) {
if (file != null) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("File save to: ${file.path}"),
backgroundColor: AppColors.secondary,
));
}
}
}

106
example/lib/packages/camera/widgets/camera_body.dart

@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/theme/radius.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/texts/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CameraBody extends AppStatefulWidget {
const CameraBody({
super.key,
required this.loading,
required this.controller,
required this.photo,
});
final bool loading;
final CameraController? controller;
final File? photo;
@override
State<CameraBody> createState() => _CameraBodyState();
}
class _CameraBodyState extends AppState<CameraBody> {
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return ClipRRect(
borderRadius: AppRadius.small,
child: Container(
color: Colors.black87,
child: Stack(
children: [
if (widget.loading)
const Center(
child: TextTitleLarge(
'Loading...',
color: Colors.white,
),
),
// Show info if not select camera
if (!widget.loading &&
widget.controller == null &&
widget.photo == null)
const Center(
child: TextTitleLarge(
'Select camera',
color: Colors.white,
),
),
// Show camera preview
if (!widget.loading &&
widget.controller != null &&
widget.controller!.value.isInitialized &&
widget.photo == null)
Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
child: CameraPreview(widget.controller!),
),
// Show dot when recording is active
if (!widget.loading &&
(widget.controller?.value.isRecordingVideo ?? false) &&
!(widget.controller?.value.isRecordingPaused ?? false))
Padding(
padding: const EdgeInsets.all(16),
child: ClipOval(
child: Container(
color: Colors.red,
width: 16,
height: 16,
),
),
),
// Show take phone
if (!widget.loading && widget.photo != null)
Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
child: RotationTransition(
turns: const AlwaysStoppedAnimation(90 / 360),
child: Image.file(
widget.photo!,
fit: BoxFit.fill,
filterQuality: FilterQuality.high,
),
),
),
],
),
),
);
}
}

125
example/lib/packages/camera/widgets/camera_control_panel.dart

@ -0,0 +1,125 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CameraControlPanel extends AppStatefulWidget {
const CameraControlPanel({
super.key,
required this.disable,
required this.controller,
required this.photo,
required this.onRecordingStart,
required this.onRecordingStop,
required this.onRecordingPause,
required this.onRecordingResume,
required this.onTakePhoto,
required this.onClearPhoto,
});
final bool disable;
final CameraController? controller;
final File? photo;
final void Function() onRecordingStart;
final void Function() onRecordingStop;
final void Function() onRecordingPause;
final void Function() onRecordingResume;
final void Function() onTakePhoto;
final void Function() onClearPhoto;
@override
State<CameraControlPanel> createState() => _CameraControlPanelState();
}
class _CameraControlPanelState extends AppState<CameraControlPanel> {
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
final isPhoto = widget.photo != null;
final isRecordingVideo = widget.controller?.value.isRecordingVideo ?? false;
final isRecordingPaused =
widget.controller?.value.isRecordingPaused ?? false;
return Visibility(
visible: widget.controller != null,
child: Column(
children: [
const SizedBox(height: 10),
Row(
children: [
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isRecordingVideo ? Icons.stop_circle : Icons.videocam,
color: AppColors.primary
.withOpacity(isPhoto || widget.disable ? 0.5 : 1),
),
onPressed: isPhoto || widget.disable
? null
: () {
if (isRecordingVideo) {
widget.onRecordingStop.call();
} else {
widget.onRecordingStart.call();
}
},
),
),
),
if (isRecordingVideo)
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isRecordingPaused
? Icons.play_circle
: Icons.pause_circle,
color: AppColors.primary,
),
onPressed: () {
if (isRecordingPaused) {
widget.onRecordingResume.call();
} else {
widget.onRecordingPause.call();
}
},
),
),
),
const Spacer(),
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isPhoto ? Icons.image_not_supported : Icons.photo_camera,
color: AppColors.primary.withOpacity(
isRecordingVideo || widget.disable ? 0.5 : 1),
),
onPressed: isRecordingVideo || widget.disable
? null
: () {
if (isPhoto) {
widget.onClearPhoto.call();
} else {
widget.onTakePhoto.call();
}
},
),
),
),
],
),
],
),
);
}
}

96
example/lib/packages/camera/widgets/cameras_loading.dart

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/blocks/block_alert.dart';
import 'package:flutter_example_packages/widgets/blocks/block_info_package.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CamerasLoading extends AppStatefulWidget {
const CamerasLoading({
super.key,
required this.package,
required this.builder,
});
final Package package;
final Widget Function(BuildContext context, List<CameraDescription> cameras)
builder;
@override
State<CamerasLoading> createState() => _CamerasLoadingState();
}
class _CamerasLoadingState extends AppState<CamerasLoading> {
List<CameraDescription>? _cameras;
String? _error;
@override
void initState() {
super.initState();
// Get list cameras
try {
availableCameras().then((cameras) {
if (mounted) {
setState(() {
_cameras = cameras;
});
}
}).catchError((e) {
setState(() {
_error = e.toString();
});
});
} catch (e) {
setState(() {
_error = e.toString();
});
}
}
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 0,
child: Column(
children: [
BlockInfoPackage(
widget.package,
),
],
),
),
Visibility(
visible: _cameras == null && _error == null,
child: const Flexible(
flex: 1,
child: Center(
child: CircularProgressIndicator(),
),
),
),
Visibility(
visible: _cameras?.isEmpty ?? false || _error != null,
child: Flexible(
flex: 0,
child: BlockAlert(_error ?? 'No camera found.'),
),
),
if (_cameras?.isNotEmpty ?? false)
Flexible(
flex: 1,
child: widget.builder.call(context, _cameras!),
),
],
);
}
}

83
example/lib/packages/camera/widgets/cameras_select.dart

@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/packages/camera/extension/export.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/texts/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CamerasSelect extends AppStatefulWidget {
const CamerasSelect({
super.key,
required this.disable,
required this.cameras,
required this.onChange,
});
final bool disable;
final List<CameraDescription> cameras;
final void Function(CameraController controller) onChange;
@override
State<CamerasSelect> createState() => _CamerasSelectState();
}
class _CamerasSelectState extends AppState<CamerasSelect> {
CameraController? _cameraController;
Future<void> initCamera(CameraDescription? camera) async {
if (camera != null) {
if (_cameraController != null) {
// Check and stop if need image stream
if (_cameraController!.value.isStreamingImages) {
await _cameraController!.stopImageStream();
}
// Check and stop if need video recording
if (_cameraController!.value.isRecordingVideo) {
await _cameraController!.stopVideoRecording();
}
// Change camera
await _cameraController!.setDescription(camera);
} else {
_cameraController = await camera.getCameraController();
}
// Send signal about change camera
if (mounted) {
widget.onChange(_cameraController!);
}
}
}
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return Row(
children: [
const TextTitleLarge('Cameras:'),
const SizedBox(width: 8),
for (final CameraDescription camera in widget.cameras)
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
camera.getIcon(),
color: _cameraController?.description == camera
? AppColors.secondary
: AppColors.primary,
),
onPressed:
_cameraController?.description == camera || widget.disable
? null
: () => initCamera(camera),
),
),
),
],
);
}
}

2
example/lib/packages/packages.dart

@ -4,6 +4,7 @@ import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/packages/battery_plus/package.dart';
import 'package:flutter_example_packages/packages/build_runner/package.dart';
import 'package:flutter_example_packages/packages/cached_network_image/package.dart';
import 'package:flutter_example_packages/packages/camera/package.dart';
import 'package:flutter_example_packages/packages/crypto/package.dart';
import 'package:flutter_example_packages/packages/cupertino_icons/package.dart';
import 'package:flutter_example_packages/packages/dartz/package.dart';
@ -41,6 +42,7 @@ final packages = <Package>[
packageBatteryPlus,
packageBuildRunner,
packageCachedNetworkImage,
packageCamera,
packageCrypto,
packageCupertinoIcons,
packageDartz,

12
example/pubspec.yaml

@ -173,6 +173,18 @@ dependencies:
# ref: master
# path: packages/sensors_plus/sensors_plus_aurora
## https://pub.dev/packages/camera
camera: ^0.10.5+5
## https://pub.dev/packages/camera_android
camera_android: ^0.10.8+13
## https://os-git.omprussia.ru/non-oss/flutter/flutter-plugins/-/tree/master/packages/camera/camera_aurora
camera_aurora:
path: ../packages/camera/camera_aurora
# git:
# url: https://gitlab.com/omprussia/flutter/flutter-plugins.git
# ref: master
# path: packages/camera/camera_aurora
dev_dependencies:
flutter_test:
sdk:

30
packages/camera/camera_aurora/.gitignore vendored

@ -0,0 +1,30 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

30
packages/camera/camera_aurora/README.md

@ -0,0 +1,30 @@
# camera_aurora
The Aurora implementation of [camera](https://pub.dev/packages/camera).
## Usage
This package is not an _endorsed_ implementation of `camera`.
Therefore, you have to include `camera_aurora` alongside `camera` as dependencies in your `pubspec.yaml` file.
***.desktop**
```desktop
Permissions=Sensors
```
***.spec**
```spec
BuildRequires: pkgconfig(streamcamera)
```
**pubspec.yaml**
```yaml
dependencies:
camera: ^0.10.5+5
camera_aurora:
git:
url: https://gitlab.com/omprussia/flutter/flutter-plugins.git
ref: master
path: packages/device_info_plus/device_info_plus_aurora
```

4
packages/camera/camera_aurora/analysis_options.yaml

@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
include: package:flutter_lints/flutter.yaml

36
packages/camera/camera_aurora/aurora/CMakeLists.txt

@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME camera_aurora)
set(PLUGIN_NAME camera_aurora_platform_plugin)
project(${PROJECT_NAME} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-psabi")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
find_package(PkgConfig REQUIRED)
find_package(Qt5 COMPONENTS Core Multimedia REQUIRED)
pkg_check_modules(StreamCamera REQUIRED IMPORTED_TARGET streamcamera)
pkg_check_modules(FlutterEmbedder REQUIRED IMPORTED_TARGET flutter-embedder)
add_library(${PLUGIN_NAME} SHARED
camera_aurora_plugin.cpp
)
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden AUTOMOC ON)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::FlutterEmbedder)
target_link_libraries(${PLUGIN_NAME} PUBLIC PkgConfig::StreamCamera)
target_link_libraries(${PLUGIN_NAME} PUBLIC Qt5::Core Qt5::Multimedia)
target_include_directories(${PLUGIN_NAME} PRIVATE ${FLUTTER_DIR})
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME})
target_compile_definitions(${PLUGIN_NAME} PRIVATE PLUGIN_IMPL)

273
packages/camera/camera_aurora/aurora/camera_aurora_plugin.cpp

@ -0,0 +1,273 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <camera_aurora/camera_aurora_plugin.h>
#include <flutter/method-channel.h>
#include <flutter/platform-methods.h>
#include <QtCore>
#include <QBuffer>
#include <QCamera>
#include <QCameraInfo>
#include <QMediaRecorder>
#include <QCameraImageCapture>
#include <unistd.h>
namespace CameraAuroraMethods
{
constexpr auto PluginKey = "camera_aurora";
constexpr auto AvailableCameras = "availableCameras";
constexpr auto CreateCamera = "createCamera";
constexpr auto Dispose = "dispose";
constexpr auto InitializeCamera = "initializeCamera";
constexpr auto TakePicture = "takePicture";
constexpr auto StartVideoRecording = "startVideoRecording";
constexpr auto StopVideoRecording = "stopVideoRecording";
constexpr auto PauseVideoRecording = "pauseVideoRecording";
constexpr auto ResumeVideoRecording = "resumeVideoRecording";
}
namespace CameraAuroraEvents
{
constexpr auto ReadyForCapture = "cameraAuroraReadyForCapture";
constexpr auto ImageSaved = "cameraAuroraImageSaved";
constexpr auto StreamedFrame = "cameraAuroraStreamedFrame";
}
void CameraAuroraPlugin::RegisterWithRegistrar(PluginRegistrar &registrar)
{
RegisterMethods(registrar);
RegisterEvents(registrar);
}
void CameraAuroraPlugin::RegisterMethods(PluginRegistrar &registrar)
{
auto methods = [this](const MethodCall &call)
{
const auto &method = call.GetMethod();
if (method == CameraAuroraMethods::AvailableCameras)
{
onAvailableCameras(call);
return;
}
if (method == CameraAuroraMethods::CreateCamera)
{
onCreateCamera(call);
return;
}
if (method == CameraAuroraMethods::Dispose)
{
onDispose(call);
return;
}
if (method == CameraAuroraMethods::InitializeCamera)
{
onInitializeCamera(call);
return;
}
if (method == CameraAuroraMethods::TakePicture)
{
onTakePicture(call);
return;
}
if (method == CameraAuroraMethods::StartVideoRecording)
{
onStartVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::StopVideoRecording)
{
onStopVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::PauseVideoRecording)
{
onPauseVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::ResumeVideoRecording)
{
onResumeVideoRecording(call);
return;
}
unimplemented(call);
};
registrar.RegisterMethodChannel(
CameraAuroraMethods::PluginKey,
MethodCodecType::Standard,
methods);
}
void CameraAuroraPlugin::RegisterEvents(PluginRegistrar &registrar)
{
registrar.RegisterEventChannel(
CameraAuroraEvents::ReadyForCapture, MethodCodecType::Standard,
[this](const Encodable &)
{ return EventResponse(); },
[this](const Encodable &)
{ return EventResponse(); });
registrar.RegisterEventChannel(
CameraAuroraEvents::ImageSaved, MethodCodecType::Standard,
[this](const Encodable &)
{ return EventResponse(); },
[this](const Encodable &)
{ return EventResponse(); });
registrar.RegisterEventChannel(
CameraAuroraEvents::StreamedFrame, MethodCodecType::Standard,
[this](const Encodable &)
{ return EventResponse(); },
[this](const Encodable &)
{ return EventResponse(); });
}
/**
* Methods
*/
void CameraAuroraPlugin::onAvailableCameras(const MethodCall &call)
{
std::vector<Encodable> list;
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo &cameraInfo : cameras)
{
list.push_back(std::map<Encodable, Encodable>{
{"deviceName", cameraInfo.deviceName().toStdString()},
{"position", static_cast<int>(cameraInfo.position())},
{"orientation", cameraInfo.orientation()},
});
}
call.SendSuccessResponse(list);
}
void CameraAuroraPlugin::onCreateCamera(const MethodCall &call)
{
QCameraInfo cameraInfo;
const auto cameraName = call.GetArgument<Encodable::String>("cameraName");
qDebug() << "onCreateCamera";
for (const QCameraInfo &item : QCameraInfo::availableCameras())
{
if (item.deviceName().toStdString() == cameraName)
{
cameraInfo = item;
break;
}
}
m_camera.reset(new QCamera(cameraInfo));
m_camera->setCaptureMode(QCamera::CaptureStillImage);
m_camera->start();
m_imageCapture.reset(new QCameraImageCapture(m_camera.data()));
connect(m_imageCapture.data(), &QCameraImageCapture::readyForCaptureChanged, this, &CameraAuroraPlugin::readyForCapture);
connect(m_imageCapture.data(), &QCameraImageCapture::imageSaved, this, &CameraAuroraPlugin::imageSaved);
call.SendSuccessResponse(stoi(cameraName));
qDebug() << "onCreateCamera";
}
void CameraAuroraPlugin::onDispose(const MethodCall &call)
{
const auto cameraId = call.GetArgument<Encodable::Int>("cameraId");
if (m_camera) {
qDebug() << "onDispose";
}
unimplemented(call);
}
void CameraAuroraPlugin::onInitializeCamera(const MethodCall &call)
{
qDebug() << "onInitializeCamera";
unimplemented(call);
}
void CameraAuroraPlugin::onTakePicture(const MethodCall &call)
{
if (m_imageCapture->isReadyForCapture()) {
m_imageCapture->capture();
call.SendSuccessResponse(true);
} else {
call.SendSuccessResponse(false);
}
}
void CameraAuroraPlugin::onStartVideoRecording(const MethodCall &call)
{
qDebug() << "onStartVideoRecording";
unimplemented(call);
}
void CameraAuroraPlugin::onStopVideoRecording(const MethodCall &call)
{
qDebug() << "onStopVideoRecording";
unimplemented(call);
}
void CameraAuroraPlugin::onPauseVideoRecording(const MethodCall &call)
{
qDebug() << "onPauseVideoRecording";
unimplemented(call);
}
void CameraAuroraPlugin::onResumeVideoRecording(const MethodCall &call)
{
qDebug() << "onResumeVideoRecording";
unimplemented(call);
}
/**
* Slots
*/
void CameraAuroraPlugin::readyForCapture(bool ready)
{
qDebug() << "readyForCapture";
EventChannel(
CameraAuroraEvents::ReadyForCapture,
MethodCodecType::Standard)
.SendEvent(ready);
}
void CameraAuroraPlugin::imageSaved(int id, const QString &fileName)
{
qDebug() << "imageSaved";
EventChannel(
CameraAuroraEvents::ImageSaved,
MethodCodecType::Standard)
.SendEvent(std::vector<Encodable>{
id,
fileName.toStdString()});
}
void CameraAuroraPlugin::unimplemented(const MethodCall &call)
{
call.SendSuccessResponse(nullptr);
}
#include "moc_camera_aurora_plugin.cpp"

54
packages/camera/camera_aurora/aurora/include/camera_aurora/camera_aurora_plugin.h

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H
#define FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H
#include <flutter/plugin-interface.h>
#include <camera_aurora/globals.h>
#include <streamcamera/streamcamera.h>
#include <streamcamera/streamcamera-codec.h>
#include <QtCore>
#include <QCamera>
#include <QCameraInfo>
#include <QMediaRecorder>
#include <QCameraImageCapture>
class PLUGIN_EXPORT CameraAuroraPlugin final
: public QObject,
public PluginInterface
{
Q_OBJECT
public:
void RegisterWithRegistrar(PluginRegistrar &registrar) override;
public slots:
void readyForCapture(bool ready);
void imageSaved(int id, const QString &fileName);
private:
void RegisterMethods(PluginRegistrar &registrar);
void RegisterEvents(PluginRegistrar &registrar);
void onAvailableCameras(const MethodCall &call);
void onCreateCamera(const MethodCall &call);
void onDispose(const MethodCall &call);
void onInitializeCamera(const MethodCall &call);
void onTakePicture(const MethodCall &call);
void onStartVideoRecording(const MethodCall &call);
void onStopVideoRecording(const MethodCall &call);
void onPauseVideoRecording(const MethodCall &call);
void onResumeVideoRecording(const MethodCall &call);
void unimplemented(const MethodCall &call);
private:
QScopedPointer<QCamera> m_camera;
QScopedPointer<QCameraImageCapture> m_imageCapture;
};
#endif /* FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H */

14
packages/camera/camera_aurora/aurora/include/camera_aurora/globals.h

@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H
#define FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H
#ifdef PLUGIN_IMPL
#define PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define PLUGIN_EXPORT
#endif
#endif /* FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H */

155
packages/camera/camera_aurora/lib/camera_aurora.dart

@ -0,0 +1,155 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:async';
import 'dart:convert';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'camera_aurora_method_channel.dart';
import 'camera_aurora_platform_interface.dart';
class CameraAurora extends CameraPlatform {
/// Registers this class as the default instance of [CameraPlatform].
static void registerWith() {
CameraPlatform.instance = CameraAurora();
}
// The stream for vending frames to platform interface clients.
StreamController<CameraImageData>? _frameStreamController;
/// Completes with a list of available cameras.
///
/// This method returns an empty list when no cameras are available.
@override
Future<List<CameraDescription>> availableCameras() =>
CameraAuroraPlatform.instance.availableCameras();
/// Creates an uninitialized camera instance and returns the cameraId.
@override
Future<int> createCamera(
CameraDescription cameraDescription,
ResolutionPreset? resolutionPreset, {
bool enableAudio = false,
}) {
EventChannel(CameraAuroraEvents.cameraAuroraStreamedFrame.name)
.receiveBroadcastStream()
.listen((event) {
debugPrint(event);
});
return CameraAuroraPlatform.instance.createCamera(cameraDescription.name);
}
/// Initializes the camera on the device.
///
/// [imageFormatGroup] is used to specify the image formatting used.
/// On Android this defaults to ImageFormat.YUV_420_888 and applies only to the imageStream.
/// On iOS this defaults to kCVPixelFormatType_32BGRA.
/// On Web this parameter is currently not supported.
@override
Future<void> initializeCamera(
int cameraId, {
ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown,
}) async {
// init
}
/// Releases the resources of this camera.
@override
Future<void> dispose(int cameraId) {
return CameraAuroraPlatform.instance.dispose(cameraId);
}
/// Captures an image and returns the file where it was saved.
@override
Future<XFile> takePicture(int cameraId) =>
CameraAuroraPlatform.instance.takePicture(cameraId);
/// Starts a video recording.
///
/// The length of the recording can be limited by specifying the [maxVideoDuration].
/// By default no maximum duration is specified,
/// meaning the recording will continue until manually stopped.
/// With [maxVideoDuration] set the video is returned in a [VideoRecordedEvent]
/// through the [onVideoRecordedEvent] stream when the set duration is reached.
///
/// This method is deprecated in favour of [startVideoCapturing].
@override
Future<void> startVideoRecording(int cameraId,
{Duration? maxVideoDuration}) =>
CameraAuroraPlatform.instance.startVideoRecording(cameraId);
/// Stops the video recording and returns the file where it was saved.
@override
Future<XFile> stopVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.stopVideoRecording(cameraId);
/// Pause video recording.
@override
Future<void> pauseVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.pauseVideoRecording(cameraId);
/// Resume video recording after pausing.
@override
Future<void> resumeVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.resumeVideoRecording(cameraId);
/// The ui orientation changed.
///
/// Implementations for this:
/// - Should support all 4 orientations.
@override
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() async* {
yield const DeviceOrientationChangedEvent(DeviceOrientation.portraitUp);
}
/// The camera has been initialized.
@override
Stream<CameraInitializedEvent> onCameraInitialized(int cameraId) async* {
yield CameraInitializedEvent(
cameraId,
// previewWidth
400,
// previewHeight
400,
// exposureMode
ExposureMode.auto,
// exposurePointSupported
true,
// focusMode
FocusMode.auto,
// focusPointSupported
true,
);
}
@override
Stream<CameraImageData> onStreamedFrameAvailable(
int cameraId, {
CameraImageStreamOptions? options,
}) {
_frameStreamController = StreamController<CameraImageData>(
onListen: () =>
CameraAuroraPlatform.instance.streamedFrame(cameraId).listen((data) {
_frameStreamController!.add(data);
}),
onPause: () => {},
onResume: () => {},
onCancel: () => {},
);
return _frameStreamController!.stream;
}
/// Returns a widget showing a live camera preview.
@override
Widget buildPreview(int cameraId) {
return Center(
child: Text(
'Camera: $cameraId',
style:
const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
);
}
}

167
packages/camera/camera_aurora/lib/camera_aurora_method_channel.dart

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:convert';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'camera_aurora_platform_interface.dart';
enum CameraAuroraMethods {
availableCameras,
createCamera,
dispose,
initializeCamera,
takePicture,
startVideoRecording,
stopVideoRecording,
pauseVideoRecording,
resumeVideoRecording,
}
enum CameraAuroraEvents {
cameraAuroraReadyForCapture,
cameraAuroraImageSaved,
cameraAuroraStreamedFrame,
}
/// An implementation of [CameraAuroraPlatform] that uses method channels.
class MethodChannelCameraAurora extends CameraAuroraPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodsChannel = const MethodChannel('camera_aurora');
@override
Future<List<CameraDescription>> availableCameras() async {
final List<CameraDescription> result = [];
final cameras = await methodsChannel.invokeMethod<List<dynamic>?>(
CameraAuroraMethods.availableCameras.name) ??
[];
for (int i = 0; i < cameras.length; i++) {
final camera = cameras[i] as Map<dynamic, dynamic>;
final pos = camera['position'];
final lensDirection = pos == 1
? CameraLensDirection.back
: (pos == 2
? CameraLensDirection.front
: CameraLensDirection.external);
result.add(CameraDescription(
name: camera['deviceName'],
lensDirection: lensDirection,
sensorOrientation: camera['orientation'],
));
}
return result;
}
@override
Future<int> createCamera(String cameraName) async {
return await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.createCamera.name, {
'cameraName': cameraName,
}) as int;
}
@override
Future<void> dispose(int cameraId) {
return methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.dispose.name, {
'cameraId': cameraId,
});
}
@override
Future<void> initializeCamera(
int cameraId, {
ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown,
}) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.initializeCamera.name, {
'cameraId': cameraId,
}) as int;
}
@override
Future<XFile> takePicture(int cameraId) async {
final result = await methodsChannel.invokeMethod<Object?>(
CameraAuroraMethods.takePicture.name,
{'cameraId': cameraId},
);
if (result == true) {
await for (final data
in EventChannel(CameraAuroraEvents.cameraAuroraImageSaved.name)
.receiveBroadcastStream()) {
final response =
(data as List<Object?>).map((e) => e.toString()).toList();
return XFile(response[1], mimeType: 'image/jpeg');
}
}
throw "Error take picture";
}
@override
Future<void> startVideoRecording(int cameraId,
{Duration? maxVideoDuration}) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.startVideoRecording.name, {
'cameraId': cameraId,
});
}
@override
Future<XFile> stopVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.stopVideoRecording.name, {
'cameraId': cameraId,
});
// @todo Save empty data
return XFile.fromData(
Uint8List(0),
name: 'file_name.mp4',
/// @todo not set
mimeType: 'video/mp4',
length: 0,
);
}
@override
Future<void> pauseVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.pauseVideoRecording.name, {
'cameraId': cameraId,
});
}
@override
Future<void> resumeVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.resumeVideoRecording.name, {
'cameraId': cameraId,
});
}
@override
Stream<CameraImageData> streamedFrame(int cameraId) async* {
await for (final data
in EventChannel(CameraAuroraEvents.cameraAuroraStreamedFrame.name)
.receiveBroadcastStream()) {
debugPrint(data);
yield const CameraImageData(
format: CameraImageFormat(ImageFormatGroup.yuv420, raw: 0),
planes: [],
height: 100,
width: 100,
);
}
}
}

72
packages/camera/camera_aurora/lib/camera_aurora_platform_interface.dart

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'camera_aurora_method_channel.dart';
abstract class CameraAuroraPlatform extends PlatformInterface {
/// Constructs a CameraAuroraPlatform.
CameraAuroraPlatform() : super(token: _token);
static final Object _token = Object();
static CameraAuroraPlatform _instance = MethodChannelCameraAurora();
/// The default instance of [CameraAuroraPlatform] to use.
///
/// Defaults to [MethodChannelCameraAurora].
static CameraAuroraPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [CameraAuroraPlatform] when
/// they register themselves.
static set instance(CameraAuroraPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<List<CameraDescription>> availableCameras() {
throw UnimplementedError('availableCameras() has not been implemented.');
}
Future<int> createCamera(String cameraName) {
throw UnimplementedError('createCamera() has not been implemented.');
}
Future<void> dispose(int cameraId) {
throw UnimplementedError('dispose() has not been implemented.');
}
Future<void> initializeCamera(
int cameraId, {
ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown,
}) {
throw UnimplementedError('initializeCamera() has not been implemented.');
}
Future<XFile> takePicture(int cameraId) {
throw UnimplementedError('takePicture() has not been implemented.');
}
Future<void> startVideoRecording(int cameraId) {
throw UnimplementedError('startVideoRecording() has not been implemented.');
}
Future<XFile> stopVideoRecording(int cameraId) {
throw UnimplementedError('stopVideoRecording() has not been implemented.');
}
Future<void> pauseVideoRecording(int cameraId) {
throw UnimplementedError('pauseVideoRecording() has not been implemented.');
}
Future<void> resumeVideoRecording(int cameraId) {
throw UnimplementedError(
'resumeVideoRecording() has not been implemented.');
}
Stream<CameraImageData> streamedFrame(int cameraId) {
throw UnimplementedError('streamedFrame() has not been implemented.');
}
}

43
packages/camera/camera_aurora/lib/type_conversion.dart

@ -0,0 +1,43 @@
import 'dart:typed_data';
import 'package:camera_platform_interface/camera_platform_interface.dart';
CameraImageData cameraImageFromPlatformData(Map<dynamic, dynamic> data) {
return CameraImageData(
format: _cameraImageFormatFromPlatformData(data['format']),
height: data['height'] as int,
width: data['width'] as int,
lensAperture: data['lensAperture'] as double?,
sensorExposureTime: data['sensorExposureTime'] as int?,
sensorSensitivity: data['sensorSensitivity'] as double?,
planes: List<CameraImagePlane>.unmodifiable(
(data['planes'] as List<dynamic>).map<CameraImagePlane>(
(dynamic planeData) => _cameraImagePlaneFromPlatformData(
planeData as Map<dynamic, dynamic>))));
}
CameraImageFormat _cameraImageFormatFromPlatformData(dynamic data) {
return CameraImageFormat(_imageFormatGroupFromPlatformData(data), raw: data);
}
ImageFormatGroup _imageFormatGroupFromPlatformData(dynamic data) {
switch (data) {
case 35: // android.graphics.ImageFormat.YUV_420_888
return ImageFormatGroup.yuv420;
case 256: // android.graphics.ImageFormat.JPEG
return ImageFormatGroup.jpeg;
case 17: // android.graphics.ImageFormat.NV21
return ImageFormatGroup.nv21;
}
return ImageFormatGroup.unknown;
}
CameraImagePlane _cameraImagePlaneFromPlatformData(Map<dynamic, dynamic> data) {
return CameraImagePlane(
bytes: data['bytes'] as Uint8List,
bytesPerPixel: data['bytesPerPixel'] as int?,
bytesPerRow: data['bytesPerRow'] as int,
height: data['height'] as int?,
width: data['width'] as int?);
}

29
packages/camera/camera_aurora/pubspec.yaml

@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
name: camera_aurora
description: Aurora implementation of the camera plugin.
version: 0.0.1
environment:
sdk: '>=2.18.6 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
camera_platform_interface: ^2.6.0
image: ^4.1.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
plugin:
platforms:
aurora:
pluginClass: CameraAuroraPlugin
dartPluginClass: CameraAurora

14
packages/xdga_directories/lib/xdga_directories.dart

@ -13,12 +13,18 @@ final DynamicLibrary _dylib = () {
final XdgaDirectoriesBindings _bindings = XdgaDirectoriesBindings(_dylib);
/// QStandardPaths::CacheLocation
String getCacheLocation() =>
_bindings.getCacheLocation().cast<Utf8>().toDartString();
String getCacheLocation() => _bindings
.getCacheLocation()
.cast<Utf8>()
.toDartString()
.replaceAll("/qsource", "");
/// QStandardPaths::AppDataLocation
String getAppDataLocation() =>
_bindings.getAppDataLocation().cast<Utf8>().toDartString();
String getAppDataLocation() => _bindings
.getAppDataLocation()
.cast<Utf8>()
.toDartString()
.replaceAll("/qsource", "");
/// QStandardPaths::DocumentsLocation
String getDocumentsLocation() =>

Loading…
Cancel
Save