Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create ImageService #2265

Merged
merged 14 commits into from
Dec 27, 2023
11 changes: 5 additions & 6 deletions lib/locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:talawa/services/comment_service.dart';
import 'package:talawa/services/database_mutation_functions.dart';
import 'package:talawa/services/event_service.dart';
import 'package:talawa/services/graphql_config.dart';
import 'package:talawa/services/image_service.dart';
import 'package:talawa/services/navigation_service.dart';
import 'package:talawa/services/org_service.dart';
import 'package:talawa/services/post_service.dart';
Expand Down Expand Up @@ -70,11 +71,8 @@ final connectivity = locator<Connectivity>();
///creating GetIt for OrganizationService.
final organizationService = locator<OrganizationService>();

///creating GetIt for ImageCropper.
final imageCropper = locator<ImageCropper>();

///creating GetIt for ImagePicker.
final imagePicker = locator<ImagePicker>();
///creating GetIt for ImageService.
final imageService = locator<ImageService>();

/// This function registers the widgets/objects in "GetIt".
///
Expand Down Expand Up @@ -104,8 +102,9 @@ void setupLocator() {
locator.registerLazySingleton(() => MultiMediaPickerService());
locator.registerLazySingleton(() => Connectivity());
locator.registerLazySingleton(() => ChatService());
locator.registerLazySingleton(() => ImageCropper());
locator.registerLazySingleton(() => ImageService());
locator.registerLazySingleton(() => ImagePicker());
locator.registerLazySingleton(() => ImageCropper());

//graphql
locator.registerSingleton(GraphqlConfig());
Expand Down
80 changes: 80 additions & 0 deletions lib/services/image_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:talawa/locator.dart';

/// ImageService class provides different functions as service in the context of Images.
///
/// Services include:
/// * `cropImage`
/// * `convertToBase64`
class ImageService {
/// Global instance of ImageCropper.
final ImageCropper _imageCropper = locator<ImageCropper>();

/// Crops the image selected by the user.
///
/// **params**:
/// * `imageFile`: the image file to be cropped.
///
/// **returns**:
/// * `Future<File?>`: the image after been cropped.
///
/// **throws**:
/// - `Exception`: If an error occurs during the image cropping process.
Future<File?> cropImage({required File imageFile}) async {
// try, to crop the image and returns a File with cropped image path.
try {
final CroppedFile? croppedImage = await _imageCropper.cropImage(
sourcePath: imageFile.path,
aspectRatioPresets: [
CropAspectRatioPreset.square,
CropAspectRatioPreset.original,
],
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Crop Image',
toolbarColor: const Color(0xff18191A),
toolbarWidgetColor: Colors.white,
backgroundColor: Colors.black,
cropGridColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
minimumAspectRatio: 1.0,
),
],
);

if (croppedImage != null) {
Azad99-9 marked this conversation as resolved.
Show resolved Hide resolved
return File(croppedImage.path);
}
} catch (e) {
throw Exception(
"ImageService : $e.",
);
}
Azad99-9 marked this conversation as resolved.
Show resolved Hide resolved

return null;
}

/// Converts the image into Base64 format.
///
/// **params**:
/// * `file`: Image as a File object.
///
/// **returns**:
/// * `Future<String?>`: image in string format
Future<String?> convertToBase64(File file) async {
try {
final List<int> bytes = await file.readAsBytes();
final String base64String = base64Encode(bytes);
return base64String;
} catch (error) {
return null;
}
}
}
95 changes: 37 additions & 58 deletions lib/services/third_party_service/multi_media_pick_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ Service usage: "add_post_view_model.dart"
import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:talawa/locator.dart';
import 'package:talawa/services/image_service.dart';
import 'package:talawa/services/navigation_service.dart';
import 'package:talawa/widgets/custom_alert_dialog.dart';

Expand All @@ -26,15 +26,22 @@ class MultiMediaPickerService {
MultiMediaPickerService() {
_picker = locator<ImagePicker>();
_fileStream = _fileStreamController.stream.asBroadcastStream();
_imageService = imageService;
}

//Local Variables
/// Controller for handling the stream of selected files.
final StreamController<File> _fileStreamController = StreamController();

/// Stream of selected files.
late Stream<File> _fileStream;

/// [ImagePicker] used for selecting images or videos.
late ImagePicker _picker;
//Getters

/// This function returns the stream of files.
/// [ImageService] for additional image-related operations.
late ImageService _imageService;

/// Provides a stream of selected multimedia files.
///
/// params:
/// None.
Expand All @@ -43,11 +50,10 @@ class MultiMediaPickerService {
/// * `Stream<dynamic>`: Stream of files.
Stream get fileStream => _fileStream;

/// This function is used to pick the image from gallery or to click the image from user's camera.
///
/// The function first ask for the permission to access the camera, if denied then returns a message in.
/// Picks the image from gallery or to click the image from user's camera.
///
/// custom Dialog Box. This function returns a File type for which `camera` variable is false by default.
/// First ask for the permission to access the camera, if denied then returns a message in.
/// custom Dialog Box. Returns a File type for which `camera` variable is false by default.
///
/// **params**:
/// * `camera`: if true then open camera for image, else open gallery to select image.
Expand All @@ -63,73 +69,46 @@ class MultiMediaPickerService {
);
// if image is selected or not null, call the cropImage function that provide service to crop the selected image.
if (image != null) {
return await cropImage(imageFile: File(image.path));
return await _imageService.cropImage(
imageFile: File(image.path),
);
}
} catch (e) {
// if the permission denied or error occurs.
if (e is PlatformException && e.code == 'camera_access_denied') {
// push the dialog alert with the message.
locator<NavigationService>().pushDialog(
CustomAlertDialog(
success: () {
locator<NavigationService>().pop();
openAppSettings();
},
dialogTitle: 'Permission Denied',
successText: 'SETTINGS',
dialogSubTitle:
"Camera permission is required, to use this feature, give permission from app settings",
),
permissionDeniedDialog(),
);
}
print(
debugPrint(
"MultiMediaPickerService : Exception occurred while choosing photo from the gallery $e",
);
}

return null;
}

/// This function is used to crop the image selected by the user.
/// Generates a custom alert dialog for permission denial.
///
/// The function accepts a `File` type image and returns `File` type of cropped image.
/// When called, it creates and returns a `CustomAlertDialog` widget with pre-defined settings.
/// This dialog prompts the user to grant camera permissions from the app settings.
///
/// **params**:
/// * `imageFile`: the image file to be cropped.
/// None
///
/// **returns**:
/// * `Future<File?>`: the image after been cropped.
Future<File?> cropImage({required File imageFile}) async {
// try, to crop the image and returns a File with cropped image path.
try {
final CroppedFile? croppedImage = await locator<ImageCropper>().cropImage(
sourcePath: imageFile.path,
aspectRatioPresets: [
CropAspectRatioPreset.square,
CropAspectRatioPreset.original,
],
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Crop Image',
toolbarColor: const Color(0xff18191A),
toolbarWidgetColor: Colors.white,
backgroundColor: Colors.black,
cropGridColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
minimumAspectRatio: 1.0,
),
],
);
if (croppedImage != null) {
return File(croppedImage.path);
}
} catch (e) {
print(
"MultiMediaPickerService : Exception occurred while cropping Image",
);
}
return null;
/// * `CustomAlertDialog`: Custom Alert Dialog widget.
CustomAlertDialog permissionDeniedDialog() {
return CustomAlertDialog(
success: () {
locator<NavigationService>().pop();
openAppSettings();
},
dialogTitle: 'Permission Denied',
successText: 'SETTINGS',
dialogSubTitle:
"Camera permission is required, to use this feature, give permission from app settings",
);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
import 'package:talawa/models/organization/org_info.dart';
import 'package:talawa/services/database_mutation_functions.dart';
import 'package:talawa/services/image_service.dart';
import 'package:talawa/services/navigation_service.dart';
import 'package:talawa/services/third_party_service/multi_media_pick_service.dart';
import 'package:talawa/services/user_config.dart';
Expand All @@ -22,6 +22,7 @@ class AddPostViewModel extends BaseModel {
//Services
late MultiMediaPickerService _multiMediaPickerService;
late NavigationService _navigationService;
late ImageService _imageService;

// ignore: unused_field
late File? _imageFile;
Expand Down Expand Up @@ -65,7 +66,7 @@ class AddPostViewModel extends BaseModel {
/// **returns**:
/// * `Future<void>`: define_the_return
Future<void> setImageInBase64(File file) async {
_imageInBase64 = await convertToBase64(file);
_imageInBase64 = await _imageService.convertToBase64(file);
notifyListeners();
}

Expand Down Expand Up @@ -121,33 +122,15 @@ class AddPostViewModel extends BaseModel {
void initialise() {
_navigationService = locator<NavigationService>();
_imageFile = null;
_imageInBase64 = null;
_multiMediaPickerService = locator<MultiMediaPickerService>();
_imageService = locator<ImageService>();
if (!demoMode) {
_dbFunctions = locator<DataBaseMutationFunctions>();
_selectedOrg = locator<UserConfig>().currentOrg;
}
}

/// to convert the image in base64.
///
///
/// **params**:
/// * `file`: file of image clicked.
///
/// **returns**:
/// * `Future<String>`: Future string containing the base 64 format image
Future<String> convertToBase64(File file) async {
try {
final List<int> bytes = await file.readAsBytes();
final String base64String = base64Encode(bytes);
print(base64String);
_imageInBase64 = base64String;
return base64String;
} catch (error) {
return '';
}
}

/// This function is used to get the image from gallery.
///
/// The function uses the `_multiMediaPickerService` services.
Expand All @@ -164,7 +147,7 @@ class AddPostViewModel extends BaseModel {
if (image != null) {
_imageFile = image;
// convertImageToBase64(image.path);
convertToBase64(image);
_imageInBase64 = await _imageService.convertToBase64(image);
// print(_imageInBase64);
_navigationService.showTalawaErrorSnackBar(
"Image is added",
Expand Down
9 changes: 9 additions & 0 deletions test/helpers/test_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:talawa/services/comment_service.dart';
import 'package:talawa/services/database_mutation_functions.dart';
import 'package:talawa/services/event_service.dart';
import 'package:talawa/services/graphql_config.dart';
import 'package:talawa/services/image_service.dart';
import 'package:talawa/services/navigation_service.dart';
import 'package:talawa/services/org_service.dart';
import 'package:talawa/services/post_service.dart';
Expand Down Expand Up @@ -53,6 +54,7 @@ import 'package:talawa/view_model/theme_view_model.dart';
import 'package:talawa/view_model/widgets_view_models/custom_drawer_view_model.dart';
import 'package:talawa/view_model/widgets_view_models/like_button_view_model.dart';
import 'package:talawa/view_model/widgets_view_models/progress_dialog_view_model.dart';
import '../service_tests/image_service_test.dart';
import '../views/main_screen_test.dart';
import 'test_helpers.mocks.dart';

Expand Down Expand Up @@ -429,6 +431,13 @@ ImageCropper getAndRegisterImageCropper() {
return service;
}

ImageService getAndRegisterImageService() {
_removeRegistrationIfExists<ImageService>();
final service = MockImageService();
locator.registerLazySingleton<ImageService>(() => service);
return service;
}

ImagePicker getAndRegisterImagePicker() {
_removeRegistrationIfExists<ImagePicker>();
final service = MockImagePicker();
Expand Down
Loading
Loading