Comentando código y otros cambios

parent ea0518c7
......@@ -15,7 +15,6 @@ abstract class Conversor {
ReturnCode? returnCode;
String nuevoPath = "$pathSalida/${archivo.nombre}.${archivo.formatoDestino!.name}";
// TODO: REVISAR EL SACAR FOTOGRAMA. NO PARECE SALIR NADA
bool conversionVideoAOtro =
archivo.formatoOriginal.tipoMultimedia == TipoMultimedia.video
&& archivo.formatoDestino?.tipoMultimedia != TipoMultimedia.video;
......@@ -55,7 +54,6 @@ abstract class Conversor {
continue;
}
var campos = linea.split("=");
// TODO: Chequear metadatos que falten
try {
metadatos.add(
Metadato(
......
......@@ -59,7 +59,6 @@ class Enlace extends Convertible {
nombre = nombre.substring(1, nombre.length-1);
}
// filename* encoding, habría que parsear en UTF8
// TODO: parsear en el caso de filename*
else {
nombre = nombre.substring(7);
}
......
......@@ -6,13 +6,17 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:prueba_multimedia/modelo/provider_ajustes.dart';
import 'package:prueba_multimedia/widgets/action_button.dart';
/// Página de Ajustes de la aplicación
class PaginaAjustes extends StatefulWidget {
/// Las diferentes opciones del modo de conversión
static const opcionesModoConversion = <String>[
"Preguntar siempre", "Copiar", "Comprimir"
];
/// Las diferentes opciones de la inclusión de subcarpetas
static const opcionesIncluirSubcarpetas = <String>[
"Sí", "No"
];
/// Las diferentes opciones de la selección de carpeta de salida
// Si hemos elegido una carpeta entonces se tendría que mostrar
static const opcionesCarpetaSalida = <String>[
"Elegir", "Preguntar siempre"
......
......@@ -23,8 +23,6 @@ class PaginaConfiguracionCarpeta extends StatefulWidget {
State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState();
}
// TODO: Se muestran formatos repetidos porque se consideran distintos
// al ser un de subcarpeta y otro no
class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> {
final Map<TipoMultimedia, List<Formato>> _formatos = {};
final Map<TipoMultimedia, bool?> _allOfType = {};
......
......@@ -136,7 +136,6 @@ class _PaginaConversionState extends State<PaginaConversion>
selected: elemento == _calidadActual,
shape: StadiumBorder(),
label: Text(elemento.texto),
// TODO: CAMBIAR CALIDAD FORMATO
onSelected: (_formatoConvertido != null && _formatoConvertido!.listCalidades.contains(elemento))?
(selected) { setState((){_calidadActual = elemento;}); } : null,
);
......
......@@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:prueba_multimedia/modelo/modelo.dart';
/// Clase que representa los botones de acción de la página principal
class ActionButton extends StatelessWidget {
final ActionButtonTypes tipoBoton;
final ListaSeleccionables manager;
......@@ -174,7 +175,6 @@ class ActionButton extends StatelessWidget {
}
/// Igual que copiar pero guarda el resultado en un .zip
/// TODO (RAFA): CREAR LOS ARCHIVOS TEMPORALES EN OTRO SITIO
void comprimirAction() async {
if(await _comprobacionesPreviasConversion(context)) {
// Averiguamos donde colocar los archivos de salida
......@@ -216,7 +216,6 @@ class ActionButton extends StatelessWidget {
else if (sel is Convertible){
sel.convertir(directorioSalida);
File tempFile = await File(
// TODO: Esto da bug con los enlaces por el nombre
"$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}"
).create();
resultsZip.add(
......@@ -272,7 +271,6 @@ class ActionButton extends StatelessWidget {
}
/// Comprueba si tenemos permisos suficientes
// TODO: Añadir aquí una notificación para avisar al usuario de si quiere realmente convertir una carpeta muy grande
Future<bool> _comprobacionesPreviasConversion(BuildContext context) async {
if(!await comprobacionPermisoArchivos(context)){
return false;
......
......@@ -8,7 +8,9 @@ import 'dart:math';
import 'package:uuid/uuid.dart';
import 'package:uuid/v1.dart';
/// Widget para mostrar carpetas en la lista de archivos de la página principal
class CarpetaWidget extends StatefulWidget {
/// Índice de la carpeta en cuestión en la lista de seleccionables
final int indice;
final Carpeta carpeta;
final ListaSeleccionables lista;
......@@ -24,16 +26,22 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
with SingleTickerProviderStateMixin
{
static const double maxHeight = 50;
/// Controlador para la animación de apertura y cierre
late final AnimationController _controller;
/// Archivos (o carpetas si es recursivo) que el usuario no ha eliminado
/// de la lista y que coinciden con los formatos elegidos
List<InfoFormato> seleccionados = [];
/// Controla si el widget está abierto o cerrado
bool open = false;
/// Controla la rotación del icono de desplegar / colapsar
double rotAngle = 0;
/// Controla la altura de las entradas de formato
double height = 0;
/// Inicializa el controlador
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this
......@@ -44,7 +52,13 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
height = maxHeight*_controller.value;
});
});
super.initState();
}
/// Libera los recursos del controlador
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
......@@ -74,6 +88,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
);
}
/// Construye la entrada de la carpeta
Widget buildMainContainer(){
Carpeta carpeta = widget.carpeta;
// Para el plural indicando los formato(s) seleccionado(s)
......@@ -164,6 +179,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
);
}
/// Construye las entradas de los formatos
List<Widget> buildSecondaryContainers(){
Carpeta carpeta = widget.carpeta;
int index = 0;
......@@ -295,6 +311,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
}
}
/// Clase encargada de cortar las entradas de los formatos durante la animación
class _MyRectClipper extends CustomClipper<Rect> {
final AnimationController _controller;
......
......@@ -11,9 +11,14 @@ import 'package:prueba_multimedia/paginas/pagina_configuracion.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion_carpeta.dart';
import 'package:prueba_multimedia/widgets/widgets.dart';
/// Widget que representa la barra inferior de botones de acción de la pantalla
/// principal
class ConVertexFabBar extends StatefulWidget {
/// Si se permite o no convertir (o sea, si hay o no seleccionables en la lista)
final bool allowConversion;
/// Acción que deben realizar los botones de conversión al finalizar
final VoidCallback onConvertSuccess;
/// Provider con los ajustes de la aplicación
final ProviderAjustes providerAjustes;
const ConVertexFabBar({
......@@ -28,17 +33,22 @@ class ConVertexFabBar extends StatefulWidget {
}
class _ConVertexFabBarState extends State<ConVertexFabBar> {
bool _convertirOpen = false;
/// Si el botón de Agregar está expandido
bool _agregarOpen = false;
/// Si el botón de Convertir está expandido
bool _convertirOpen = false;
/// Clave que se utiliza para abrir y cerrar el botón de Agregar
final _agregarKey = GlobalKey<ExpandableFabState>();
/// Clave que se utiliza para abrir y cerrar el botón de Convertir
final _convertirKey = GlobalKey<ExpandableFabState>();
/// Los botones de acción dentro del botón expansible Agregar
static const agregarButtonTypes = <ActionButtonTypes>[
ActionButtonTypes.archivo,
ActionButtonTypes.carpeta,
ActionButtonTypes.enlace
];
/// Los botones de acción dentro del botón expansible Convertir
static const convertirButtonTypes= <ActionButtonTypes>[
ActionButtonTypes.copiar,
ActionButtonTypes.comprimir
......@@ -78,21 +88,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
);
}
List<Widget> _loadConvertirActionButtons (BuildContext context,
ListaSeleccionables manager)
{
return convertirButtonTypes.map((type) {
return ActionButton(
tipoBoton: type,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && type.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
);
}).toList();
}
/// Construye los FABs expansibles
List<Widget> _buildFABs(ListaSeleccionables manager) {
final toRet = <Widget>[];
......@@ -141,23 +137,13 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
disabledElevation: 0,
onPressed: (!widget.allowConversion)? null :
() {
// Rafa: Esto es un poco un 'hack'
final tipoAccion = widget.providerAjustes.modoConversion == 1
? ActionButtonTypes.copiar
: ActionButtonTypes.comprimir;
final callback = ActionButton(
tipoBoton: tipoAccion,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && tipoAccion.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
).getCallback();
if (callback != null) {
callback();
switch(widget.providerAjustes.modoConversion){
case 1:
convertirCopiar(manager);
break;
case 2:
convertirComprimir(manager);
break;
}
},
));
......@@ -195,6 +181,23 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
return toRet;
}
/// Construye los botones de acción dentro del botón expansible Convertir
List<Widget> _loadConvertirActionButtons (BuildContext context,
ListaSeleccionables manager)
{
return convertirButtonTypes.map((type) {
return ActionButton(
tipoBoton: type,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && type.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
);
}).toList();
}
/// Construye los botones de acción dentro del botón expansible Agregar
List<Widget> _loadAgregarActionButtons(BuildContext context,
ListaSeleccionables manager)
{
......@@ -242,6 +245,110 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
}).toList();
}
/// Acción que realiza el botón de convertir cuando el modo Copiar está
/// seleccionado en Ajustes
Future<void> convertirCopiar(ListaSeleccionables manager) async {
if(await ActionButton.comprobacionPermisoArchivos(context)){
String? directorioSalida;
if(widget.providerAjustes.carpetaSalida.isNotEmpty){
directorioSalida = widget.providerAjustes.carpetaSalida;
}
else{
directorioSalida = await FilePicker.platform.getDirectoryPath();
}
if(directorioSalida != null){
listenerActualizar(manager);
// Convertimos los archivos como tal
List<Archivo> archivos = manager.seleccionables.whereType<Archivo>().toList();
while(manager.seleccionables.isNotEmpty){
ElementoSeleccionable sel = manager.seleccionables.first;
if(sel is Carpeta){
for (var archivo in sel.elementosSeleccionados) {
Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
Conversor.convertir(archivo, d.path);
}
}
else if (sel is Archivo){
Conversor.convertir(sel, directorioSalida);
}
manager.borraSeleccionable(0);
}
}
}
}
/// Acción que realiza el botón de convertir cuando el modo Comprimir está
/// seleccionado en Ajustes
Future<void> convertirComprimir(ListaSeleccionables manager) async {
if(await ActionButton.comprobacionPermisoArchivos(context)){
// Averiguamos donde colocar los archivos de salida
String? directorioSalida = widget.providerAjustes.carpetaSalida.isNotEmpty
? widget.providerAjustes.carpetaSalida
: await FilePicker.platform.getDirectoryPath();
if(directorioSalida != null) {
listenerActualizar(manager);
final now = DateTime.now();
final nombreZip = "ConVertex_"
"${now.day}-${now.month}-${now.year}_"
"${now.hour}-${now.minute}-${now.second}";
final zipFileEncoder = ZipFileEncoder();
zipFileEncoder.create("$directorioSalida/$nombreZip.zip");
final resultsZip = <Future<void>>[];
while(manager.seleccionables.isNotEmpty){
ElementoSeleccionable sel = manager.seleccionables.first;
// Conversion de carpetas
if(sel is Carpeta){
Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
final resultsConversion = <Future<ReturnCode?>>[];
for (var archivo in sel.elementosSeleccionados) {
resultsConversion.add(Conversor.convertir(archivo, d.path));
}
// Esperamos a la conversión y añadimos a zip
for (final result in resultsConversion) {
await result;
}
resultsZip.add(
zipFileEncoder.addDirectory(d)..then((_) => d.delete(recursive: true))
);
}
// Conversion de archivos
else if (sel is Archivo){
// Esperamos a la conversión y añadimos a zip
await Conversor.convertir(sel, directorioSalida);
File tempFile = await File(
"$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}"
).create();
resultsZip.add(
zipFileEncoder.addFile(tempFile)..then((_) => tempFile.delete())
);
}
// Esperamos a que la compresión se termine y se borren los
// archivos temporales
if (manager.seleccionables.length == 1) {
for (final result in resultsZip) {
await result;
}
zipFileEncoder.closeSync();
}
// Esto hace que la barra de progreso suba
manager.borraSeleccionable(0);
}
}
}
}
/// Listener que se encarga de actualizar el progreso conforme se convierten
/// los archivos
Future<void> listenerActualizar(ListaSeleccionables manager) async {
manager.iniciarConversion();
int initialSize = manager.seleccionables.length;
......
import 'package:flutter/material.dart';
/// Un pequeño Widget que contiene la barra de progreso de la conversión
class ConvertexProgressBar extends StatelessWidget {
/// Progreso de la conversión
final int progress;
/// Alto y ancho de la barra
final double? width;
final double? height;
/// Colores de fondo, borde y texto de la barra de progreso
final Color? background;
final Color? completedBackground;
final Color? borderColor;
......
......@@ -4,12 +4,14 @@ import 'package:prueba_multimedia/modelo/provider_ajustes.dart';
import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart';
/// Clase que representa la aplicación de ConVertex
class ConvertexPrototipoApp extends StatelessWidget {
const ConvertexPrototipoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ConVertex',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)
),
......
......@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart';
/// Widget para mostrar archivos y enlaces en la lista de archivos de la página principal
class ConvertibleWidget extends StatelessWidget {
/// Índice del archivo o enlace en cuestión en la lista de seleccionables
final int indice;
final Convertible convertible;
final ListaSeleccionables lista;
......@@ -10,7 +12,6 @@ class ConvertibleWidget extends StatelessWidget {
const ConvertibleWidget({super.key,
required this.indice, required this.convertible, required this.lista});
// TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS
@override
Widget build(BuildContext context) {
final String nombre = convertible.nombre;
......
import 'dart:math';
import 'package:flutter/material.dart';
/// Widget que contiene un botón expansible, como los usados en la página
/// principal. Inspirados en [https://docs.flutter.dev/cookbook/effects/expandable-fab]
@immutable
class ExpandableFab extends StatefulWidget {
final bool? initialOpen;
/// Si es true, se alinea a la izquierda
final bool invert;
/// Icono del FAB
final Icon icon;
/// Texto del FAB
final String? label;
/// Distancia a la que se crean los hijos del FAB
final double distance;
/// Lista de hijos del FAB
final List<Widget> children;
const ExpandableFab({
......@@ -26,8 +33,10 @@ class ExpandableFab extends StatefulWidget {
class ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
/// Controladores de la animación
late final AnimationController _controller;
late final Animation<double> _expandAnimation;
/// Si está abierto
bool _open = false;
@override
......@@ -52,16 +61,19 @@ class ExpandableFabState extends State<ExpandableFab>
super.dispose();
}
/// Simula una pulsación sobre el botón
void tap(){
_toggle();
}
/// Simula el cierre del botón
void close(){
if(_open){
_toggle();
}
}
/// Alterna el estado abierto-cerrado
void _toggle(){
setState(() {
_open = !_open;
......@@ -95,6 +107,7 @@ class ExpandableFabState extends State<ExpandableFab>
);
}
/// Construye la X que contrae el FAB
Widget _buildTapToCloseFab() {
return SizedBox(
width: 56,
......@@ -118,6 +131,7 @@ class ExpandableFabState extends State<ExpandableFab>
);
}
/// Construye el contenedor que expande las opciones
Widget _buildTapToOpenFab() {
return IgnorePointer(
ignoring: _open,
......@@ -140,6 +154,7 @@ class ExpandableFabState extends State<ExpandableFab>
);
}
/// Construye el botón del FAB que expande las opciones
Widget _loadFloatingActionButton(){
if(widget.label == null){
return FloatingActionButton(
......@@ -158,6 +173,7 @@ class ExpandableFabState extends State<ExpandableFab>
);
}
/// Construye las opciones del FAB
List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
......@@ -178,6 +194,7 @@ class ExpandableFabState extends State<ExpandableFab>
return children;
}
/// Obtiene la anchura que debería tener el botón
double _computeWidth() {
if(_open){
if(widget.label != null) return 170.0;
......@@ -190,6 +207,7 @@ class ExpandableFabState extends State<ExpandableFab>
}
}
/// Clase que se utiliza para construir la animación del FAB abriéndose y cerrándose
class _ExpandingActionButton extends StatelessWidget {
final bool invert;
final double directionInDegrees;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment