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

Adds photo support #126

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Harry Schiller (@waitingwittykitty)
- David Coker (@daoxve)
- Adrasteon (@AdrasteonDev)
- Alberto Salguero (@agsalguero)

## Testing & Feedback
- Augusto Vesco
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Then, after a couple months or even years:
## Features

- Record up to 10 seconds of video (1080p resolution)
- Pick videos from gallery
- Pick videos or photos from gallery
- Add or edit subtitles in the videos
- Add automatic or manual geotagging on top of the videos
- Choose the date format and color to show on top of the videos
Expand Down
3 changes: 3 additions & 0 deletions lib/lang/en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,7 @@ const Map<String, String> en = {
'useAlternativeCalendarColors': 'Use alternative calendar colors',
'useAlternativeCalendarColorsDescription':
'Changes green and red in calendar to blue and yellow. Useful for colorblind people.',
'useExtendedQuickCuts': 'Use extended quickcuts',
'useExtendedQuickCutsDescription':
'Add more duration values for cutting videos. Useful for cutting videos with more precision.',
};
5 changes: 4 additions & 1 deletion lib/lang/es.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,8 @@ const Map<String, String> es = {
'Cuando está activado, seleccionar fechas pasadas filtrará los vídeos por esa fecha. Si está desactivado, se mostrarán todos los vídeos. Funciona solo con el selector de archivos experimental.',
'useAlternativeCalendarColors': 'Use colores alternativos para el calendario',
'useAlternativeCalendarColorsDescription':
'Cambia el verde y el rojo en el calendario a azul y amarillo. Útil para personas con daltonismo.'
'Cambia el verde y el rojo en el calendario a azul y amarillo. Útil para personas con daltonismo.',
'useExtendedQuickCuts': 'Usar botones de corte extendidos',
'useExtendedQuickCutsDescription':
'Añade más botones de duración para cortar los vídeos. Útil para recortar los vídeos con mayor precisión.',
};
4 changes: 2 additions & 2 deletions lib/pages/home/calendar_editor/calendar_editor_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ class _CalendarEditorPageState extends State<CalendarEditorPage> {
context,
pickerConfig: AssetPickerConfig(
maxAssets: 1,
requestType: RequestType.video,
requestType: RequestType.common,
filterOptions: shouldIgnoreFilter ? null : filterOptionGroup,
sortPathsByModifiedDate: true,
specialItemPosition: SpecialItemPosition.prepend,
specialItemBuilder: (context, path, length) {
return Center(
child: Text(
shouldIgnoreFilter
? 'Latest\nvideos'
? 'Latest\nmedia'
: 'From\n${_selectedDate.toString().substring(0, 10).split('-').reversed.join('-')}\nonwards',
textAlign: TextAlign.center,
style: const TextStyle(
Expand Down
46 changes: 46 additions & 0 deletions lib/pages/home/settings/widgets/preferences_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class _PreferencesPageState extends State<PreferencesPage> {
late bool isPickerSwitchToggled;
late bool isPickerFilterSwitchToggled;
late bool isColorsSwitchToggled;
late bool isExtendedQuickCutsSwitchToggled;

@override
void initState() {
Expand All @@ -24,6 +25,7 @@ class _PreferencesPageState extends State<PreferencesPage> {
isPickerSwitchToggled = SharedPrefsUtil.getBool('useExperimentalPicker') ?? true;
isPickerFilterSwitchToggled = SharedPrefsUtil.getBool('useFilterInExperimentalPicker') ?? false;
isColorsSwitchToggled = SharedPrefsUtil.getBool('useAlternativeCalendarColors') ?? false;
isExtendedQuickCutsSwitchToggled = SharedPrefsUtil.getBool('useExtendedQuickCuts') ?? false;
}

@override
Expand Down Expand Up @@ -229,6 +231,50 @@ class _PreferencesPageState extends State<PreferencesPage> {
),
const SizedBox(height: 5.0),
Text('useAlternativeCalendarColorsDescription'.tr),
const Divider(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
'useExtendedQuickCuts'.tr,
style: TextStyle(
fontSize: MediaQuery.of(context).size.width * 0.045,
),
),
),
Switch(
value: isExtendedQuickCutsSwitchToggled,
onChanged: (value) async {
if (value) {
Utils.logInfo(
'[PREFERENCES] - Use extended quickCuts was enabled',
);

SharedPrefsUtil.putBool('useExtendedQuickCuts', true);
} else {
Utils.logInfo(
'[PREFERENCES] - Use extended quickCuts was disabled',
);

SharedPrefsUtil.putBool('useExtendedQuickCuts', false);
}

/// Update switch value
setState(() {
isExtendedQuickCutsSwitchToggled = !isExtendedQuickCutsSwitchToggled;
});
},
activeTrackColor: AppColors.mainColor.withOpacity(0.4),
activeColor: AppColors.mainColor,
),
],
),
),
const SizedBox(height: 5.0),
Text('useExtendedQuickCutsDescription'.tr),
],
),
),
Expand Down
147 changes: 113 additions & 34 deletions lib/pages/save_video/save_video_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:group_radio_button/group_radio_button.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:video_trimmer/video_trimmer.dart';

import '../../controllers/recording_settings_controller.dart';
Expand Down Expand Up @@ -64,6 +65,7 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
double _videoEndValue = 0.0;
bool _isVideoPlaying = false;
bool _isLocationProcessing = false;
bool _isImage = false;

late final bool isDarkTheme = ThemeService().isDarkTheme();
String selectedProfileName = Utils.getCurrentProfile();
Expand Down Expand Up @@ -294,9 +296,14 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
pickerColor = parseColorString(_recordingSettingsController.dateColor.value);
currentColor = pickerColor;
_tempVideoPath = routeArguments['videoPath'];
_isImage = isImage(_tempVideoPath);
isTextDate = _recordingSettingsController.dateFormatId.value == 1;
_initCorrectDates();
_initVideoPlayerController();
if(_isImage) {
_videoEndValue = 1.0;
} else {
_initVideoPlayerController();
}
if (isGeotaggingEnabled) {
setGeotagging();
}
Expand Down Expand Up @@ -367,17 +374,29 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
return ColoredBox(
color: AppColors.dark,
child: GestureDetector(
onTap: () => videoPlay(),
onTap: _isImage ? null : () => videoPlay(), // @todo animate effect for images
child: AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
children: [
VideoViewer(
trimmer: _trimmer,
),
if (_isImage)
SizedBox(
width: MediaQuery.of(context).size.width,
child: ColoredBox(
color: Colors.black,
child: Image.file(
File(_tempVideoPath),
fit: BoxFit.contain,
),
),
)
else
VideoViewer(
trimmer: _trimmer,
),
Center(
child: Opacity(
opacity: _isVideoPlaying ? 0.0 : 1.0,
opacity: _isVideoPlaying || _isImage ? 0.0 : 1.0, // @todo show play button for images
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
height: MediaQuery.of(context).size.width * 0.25,
Expand Down Expand Up @@ -465,6 +484,11 @@ class _SaveVideoPageState extends State<SaveVideoPage> {

@override
Widget build(BuildContext context) {
const editorProperties = TrimEditorProperties();
final List<double>quickCutNumbers = (SharedPrefsUtil.getBool('useExtendedQuickCuts') ?? false)
? [1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10]
: [1, 2, 3, 5, 10];

return PopScope(
canPop: false,
onPopInvoked: (_) async {
Expand Down Expand Up @@ -516,7 +540,6 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
),
child: SaveButton(
videoPath: _tempVideoPath,
videoController: _trimmer.videoPlayerController!,
dateColor: currentColor,
dateFormat: _dateFinalFormatValueForVideoEdit,
isTextDate: isTextDate,
Expand All @@ -526,13 +549,13 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
: customLocationTextController.text,
subtitles: _subtitles,
videoStartInMilliseconds: _videoStartValue,
videoEndInMilliseconds: getVideoEndInMilliseconds(),
videoDuration: _trimmer.videoPlayerController!.value.duration.inSeconds,
videoEndInMilliseconds: _isImage ? _videoEndValue * 1000 : getVideoEndInMilliseconds(),
isGeotaggingEnabled: isGeotaggingEnabled,
textOutlineColor: invert(currentColor),
textOutlineWidth: textOutlineStrokeWidth,
determinedDate: routeArguments['currentDate'],
isFromRecordingPage: routeArguments['isFromRecordingPage'],
isImage: _isImage,
),
),
body: Column(
Expand All @@ -546,31 +569,78 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: TrimViewer(
trimmer: _trimmer,
viewerHeight: 50.0,
type: ViewerType.fixed,
editorProperties: TrimEditorProperties(
borderWidth: 2.5,
circleSize: 6.0,
circleSizeOnDrag: 9.0,
circlePaintColor: isDarkTheme ? Colors.white : AppColors.mainColor,
borderPaintColor:
isDarkTheme ? AppColors.light : AppColors.mainColor.withOpacity(0.75),
quickCutBackgroundColor: isDarkTheme
? AppColors.light.withOpacity(0.15)
: AppColors.dark.withOpacity(0.40),
child: _isImage
? SingleChildScrollView( // add quick cut buttons for images
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
child: Row(
children: [
for (double i in quickCutNumbers)
Padding(
padding: const EdgeInsets.all(10.0),
child: TextButton.icon(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(
editorProperties.quickCutForegroundColor,
),
backgroundColor: MaterialStateProperty.all<Color>(
editorProperties.quickCutBackgroundColor,
),
overlayColor: MaterialStateProperty.all<Color>(
editorProperties.quickCutForegroundColor.withOpacity(0.25),
),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(35.0),
),
),
),
icon: Icon(
editorProperties.quickCutIcon,
size: editorProperties.quickCutIconSize,
),
onPressed: () {
_videoStartValue = 0;
_videoEndValue = i;
setState(() {});
},
label: Text(
(i == i.roundToDouble()?i.round():i).toString(),
style: TextStyle(color: (i == _videoEndValue)
? Colors.yellow
: editorProperties.quickCutTextColor),
),
),
),
],
),
)
: TrimViewer( // use video trimmer for videos
trimmer: _trimmer,
viewerHeight: 50.0,
type: ViewerType.fixed,
editorProperties: TrimEditorProperties(
borderWidth: 2.5,
circleSize: 6.0,
circleSizeOnDrag: 9.0,
circlePaintColor: isDarkTheme ? Colors.white : AppColors.mainColor,
borderPaintColor:
isDarkTheme ? AppColors.light : AppColors.mainColor.withOpacity(0.75),
quickCutBackgroundColor: isDarkTheme
? AppColors.light.withOpacity(0.15)
: AppColors.dark.withOpacity(0.40),
),
durationStyle: DurationStyle.FORMAT_SS_MS,
durationTextStyle: isDarkTheme
? const TextStyle(color: Colors.white)
: const TextStyle(color: Colors.black),
maxVideoLength: const Duration(milliseconds: 10000),
viewerWidth: MediaQuery.of(context).size.width,
onChangeStart: (value) => _videoStartValue = value,
onChangeEnd: (value) => _videoEndValue = value,
onChangePlaybackState: (value) => setState(() => _isVideoPlaying = value),
quickCutNumbers: quickCutNumbers,
),
durationStyle: DurationStyle.FORMAT_SS_MS,
durationTextStyle: isDarkTheme
? const TextStyle(color: Colors.white)
: const TextStyle(color: Colors.black),
maxVideoLength: const Duration(milliseconds: 10000),
viewerWidth: MediaQuery.of(context).size.width,
onChangeStart: (value) => _videoStartValue = value,
onChangeEnd: (value) => _videoEndValue = value,
onChangePlaybackState: (value) => setState(() => _isVideoPlaying = value),
),
),
),
],
Expand Down Expand Up @@ -1034,11 +1104,20 @@ class _SaveVideoPageState extends State<SaveVideoPage> {
}

double getVideoEndInMilliseconds() {
final double defaultEnd = _videoEndValue + 500;
final double defaultEnd = _videoEndValue;
final int videoDuration = _trimmer.videoPlayerController!.value.duration.inMilliseconds;
if (defaultEnd > videoDuration) {
return videoDuration.toDouble();
}
return defaultEnd;
}

bool isImage(String path) {
final String? mimeStr = lookupMimeType(path);
if (mimeStr == null) {
return false;
}
final fileType = mimeStr.split('/');
return fileType[0] == 'image';
}
}
Loading