Panel de conversión completamente funcional y con soporte a vídeo

parent 0d3fc775
......@@ -6,8 +6,9 @@ class Conversion{
Formato get formatoOriginal => _formatoOriginal;
Conversion({required Formato formatoOriginal, Formato? formatoDestino = null}):
_formatoOriginal = formatoOriginal, _formatoDestino = formatoDestino;
Conversion({required ClaseFormato formatoOriginal, Formato? formatoDestino = null}):
_formatoOriginal = Formato(extension: formatoOriginal),
_formatoDestino = formatoDestino;
void convertir(){
// TODO: <implement>
......
......@@ -3,17 +3,90 @@ class Formato{
Calidad _calidad;
ClaseFormato get claseFormato => _extension;
Calidad get calidad => _calidad;
set calidad(Calidad calidad) => _calidad = calidad;
bool get isVideo => _extension.tipoMultimedia == TipoMultimedia.VIDEO;
Formato({required ClaseFormato extension, Calidad calidad = Calidad.ORIGINAL}):
_extension = extension, _calidad = calidad;
Formato copiaSiNulo({ClaseFormato? formato, Calidad? calidad}){
return Formato(
extension: formato ?? this.claseFormato,
calidad: calidad ?? this._calidad
);
}
}
enum ClaseFormato{
PNG(".png", "Portable Network Graphics", "Calidad", "Nombre, Autor...", "Formato gráfico basado en un algoritmo de compresión sin pérdida para bitmaps no sujeto a patentes. Fue desarrollado en buena parte para solventar las deficiencias del formato GIF y permite almacenar imágenes con una mayor profundidad de contraste y otros datos importantes."),
JPG(".jpg", "Joint Photographic Experts Group", "Ligero", "Nombre, Autor...", "A pesar de ser un método de compresión, es a menudo considerado como un formato de archivo. Es el formato de imagen más común, utilizado por las cámaras fotográficas digitales y otros dispositivos de captura de imagen");
PNG('.png',
'Portable Network Graphics',
TipoMultimedia.IMAGEN,
'Calidad',
'Nombre, Autor...',
'Formato gráfico basado en un algoritmo de compresión sin pérdida para bitmaps no sujeto a patentes. Fue desarrollado en buena parte para solventar las deficiencias del formato GIF y permite almacenar imágenes con una mayor profundidad de contraste y otros datos importantes.'
),
JPG('.jpg',
'Joint Photographic Experts Group',
TipoMultimedia.IMAGEN,
'Ligero',
'Nombre, Autor...',
'A pesar de ser un método de compresión, es a menudo considerado como un formato de archivo. Es el formato de imagen más común, utilizado por las cámaras fotográficas digitales y otros dispositivos de captura de imagen'
),
TIF('.tif',
'Tagged Image File Format',
TipoMultimedia.IMAGEN,
'Calidad',
'Nombre, Autor...',
'Un formato de archivo informático para almacenar imágenes de mapa de bits. Es prevalente en la industria gráfica y en la fotografía profesional por su versatilidad y compresión no destructiva.'
),
MP3('.mp3',
'MPEG-1 Layer III',
TipoMultimedia.AUDIO,
'Calidad',
'Nombre, Autor...',
'Un formato de compresión de audio digital que usa un algoritmo con pérdida para conseguir un menor tamaño de archivo. Es un formato de audio común utilizado para música tanto en computadoras como en reproductores de audio portátil.'
),
OGG('.ogg',
'Xiph.org Ogg',
TipoMultimedia.AUDIO,
'Versátil',
'Nombre, Autor...',
'Un formato contenedor libre y abierto, desarrollado y mantenido por la Fundación Xiph.Org que no está restringido por las patentes de software, y está diseñado para proporcionar una difusión de flujo eficiente y manipulación de multimedios digitales de alta calidad.'
),
WAV('.wav',
'Waveform Audio File Format',
TipoMultimedia.AUDIO,
'Calidad',
'Nombre, Autor...',
'Un formato de audio digital con o sin compresión de datos desarrollado por Microsoft e IBM que se utiliza para almacenar flujos digitales de audio en el PC, mono y estéreo a diversas resoluciones y velocidades de muestreo.'
),
MP4('.mp4',
'MPEG-4 Parte 14',
TipoMultimedia.VIDEO,
'Calidad',
'Nombre, Autor...',
'Un formato contenedor especificado como parte del estándar internacional MPEG-4 de ISO/IEC. Es utilizado para almacenar los formatos audiovisuales especificados por ISO/IEC y el grupo MPEG (Moving Picture Experts Group) al igual que otros formatos audiovisuales disponibles.'
),
MKV('.mkv',
'Matroshka',
TipoMultimedia.VIDEO,
'Calidad',
'Nombre, Autor...',
'Un formato contenedor abierto que puede almacenar una cantidad muy grande de vídeo, audio, imagen o pistas de subtítulos dentro de un solo archivo. Su finalidad es la de servir como formato universal para el almacenamiento de contenidos audiovisuales y multimedia, como películas o programas de televisión, imágenes y textos.'
),
WMV('.wmv',
'Windows Media Video',
TipoMultimedia.VIDEO,
'Calidad',
'Nombre, Autor...',
'Un formato de vídeo desarrollado por Microsoft, que forma parte del framework Windows Media. No está contruida solo con tecnología interna de Microsoft. Desde la versión 7 (WMV1), Microsoft ha utilizado su propia versión no estandarizada de MPEG-4. El vídeo a menudo se combina con sonido en formato Windows Media Audio.'
);
final String _extension;
final String _nombre;
final TipoMultimedia _tipoMultimedia;
final String _descripcion;
final String _clasificacion;
final String _metadatos;
......@@ -21,14 +94,33 @@ enum ClaseFormato{
String get extension => _extension;
String get nombre => _nombre;
String get descripcion => _descripcion;
TipoMultimedia get tipoMultimedia => _tipoMultimedia;
const ClaseFormato(this._extension, this._nombre, this._tipoMultimedia, this._clasificacion, this._metadatos, this._descripcion);
static List<ClaseFormato> listadoFormatos({required TipoMultimedia tipo, ClaseFormato? excepcion}){
final toRet = <ClaseFormato>[];
for(var formato in ClaseFormato.values){
if(formato._tipoMultimedia == tipo && formato != excepcion) {
toRet.add(formato);
}
}
return toRet;
}
}
const ClaseFormato(this._extension, this._nombre, this._clasificacion, this._metadatos, this._descripcion);
enum TipoMultimedia{
VIDEO,
IMAGEN,
AUDIO
}
enum Calidad{
BAJA,
MEDIA,
ALTA,
MUY_ALTA,
Baja,
Media,
Alta,
Muy_alta,
ORIGINAL
}
\ No newline at end of file
......@@ -15,9 +15,18 @@ class PaginaConversion extends StatefulWidget {
State<PaginaConversion> createState() => _PaginaConversionState();
}
class _PaginaConversionState extends State<PaginaConversion> {
Formato? _formatoConvertido = Formato(extension: ClaseFormato.JPG);
int _calidad = 0;
class _PaginaConversionState extends State<PaginaConversion>
with SingleTickerProviderStateMixin {
Formato? _formatoConvertido;
late final _tabController;
int _tabElegida = 0;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
Widget build(BuildContext context) {
......@@ -40,18 +49,8 @@ class _PaginaConversionState extends State<PaginaConversion> {
_createConversionFormatBox()
],
),
SizedBox(height: 8.0),
// Calidad...
/*Slider(
value: _calidad.toDouble(),
min: 0,
max: 3,
onChanged: (valor) {
setState(() {
_calidad = valor.toInt();
});
}
)*/
SizedBox(height: 20.0),
_createCalidadSelectionPanel()
],
),
),
......@@ -111,7 +110,48 @@ class _PaginaConversionState extends State<PaginaConversion> {
);
}
Widget _createConversionSelectionPanel() {
Widget _createCalidadSelectionPanel() {
final listaChips = Calidad.values.map((elemento) {
return ChoiceChip(
selected: (_formatoConvertido != null)? _formatoConvertido!.calidad == elemento : false,
shape: StadiumBorder(),
label: Text(
elemento.name.replaceAll(RegExp(r'_'), ' ')
),
onSelected: (_formatoConvertido == null)? null : (selection) {
setState(() { if(_formatoConvertido != null) _formatoConvertido!.calidad = elemento; });
},
);
}).toList();
// Borramos la calidad "Original"
listaChips.removeLast();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Calidad:',
style: Theme.of(context).textTheme.titleSmall
),
SizedBox(height: 8.0),
Wrap(
spacing: 10.0,
children: listaChips,
)
],
);
}
Widget _createConversionSelectionPanel(){
if(widget._formatoOriginal.isVideo){
return _createTabVideos();
}
else{
return _createDefaultConversionPanel();
}
}
Widget _createDefaultConversionPanel() {
return Material(
clipBehavior: Clip.antiAlias,
shape: BeveledRectangleBorder(
......@@ -125,7 +165,7 @@ class _PaginaConversionState extends State<PaginaConversion> {
)
),
child: Container(
height: MediaQuery.of(context).size.height - 400,
height: MediaQuery.of(context).size.height - 380,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
child: Padding(
......@@ -134,44 +174,157 @@ class _PaginaConversionState extends State<PaginaConversion> {
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: List.generate(16, (index) {
return Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
children: _createGridBotonesConversion()
),
),
),
);
}
Widget _createTabVideos(){
return Material(
clipBehavior: Clip.antiAlias,
shape: BeveledRectangleBorder(
side: BorderSide(
color: Colors.black,
width: 1
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0)
)
),
child: Container(
height: MediaQuery.of(context).size.height - 380,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Container(
height: 68,
child: TabBar(
controller: _tabController,
tabs: <Widget>[
Tab(
icon: Icon(Icons.movie_creation_outlined),
text: 'Vídeo',
),
Tab(
icon: Icon(Icons.music_note_outlined),
text: 'Audio',
),
Tab(
icon: Icon(Icons.image_outlined),
text: 'Imagen',
)
]
),
color: Theme.of(context).cardColor,
child: InkWell(
onTap: () {},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black
),
borderRadius: BorderRadius.circular(20.0)
),
Container(
height: MediaQuery.of(context).size.height - 488,
child: Padding(
padding: const EdgeInsets.only(top: 16.0),
child: TabBarView(
controller: _tabController,
children: [
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: _createGridBotonesConversion(tipo: TipoMultimedia.VIDEO)
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'PNG',
style: Theme.of(context).textTheme.titleLarge,
textScaler: TextScaler.linear(1.5),
),
Container(
height: 15.0,
child: Icon(Icons.airplanemode_active),
)
],
)
)
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: _createGridBotonesConversion(tipo: TipoMultimedia.AUDIO)
),
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: _createGridBotonesConversion(tipo: TipoMultimedia.IMAGEN)
),
]
),
),
);
}),
),
],
),
),
),
);
}
List<Widget> _createGridBotonesConversion({TipoMultimedia? tipo}){
tipo ??= widget._formatoOriginal.claseFormato.tipoMultimedia;
final listaFormatos = ClaseFormato.listadoFormatos(
tipo: tipo,
excepcion: widget._formatoOriginal.claseFormato
);
return listaFormatos.map((elemento) {
return Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
color: (_formatoConvertido?.claseFormato == elemento)?
Theme.of(context).disabledColor : Theme.of(context).cardColor,
child: InkWell(
onTap: (_formatoConvertido?.claseFormato == elemento)? null : () {
setState(() {
if(_formatoConvertido == null){
_formatoConvertido = Formato(extension: elemento, calidad: Calidad.Media);
}
else{
_formatoConvertido = _formatoConvertido!.copiaSiNulo(formato: elemento);
}
});
},
onLongPress: () {
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
content: Text(elemento.descripcion),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cerrar'),
),
],
);
},
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black
),
borderRadius: BorderRadius.circular(20.0)
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
elemento.name,
style: Theme.of(context).textTheme.titleLarge,
textScaler: TextScaler.linear(1.5),
),
Container(
height: 15.0,
child: Icon(Icons.airplanemode_active),
)
],
)
)
),
),
);
}).toList();
}
}
......@@ -32,9 +32,7 @@ class _PaginaPrincipalState extends State<PaginaPrincipal> {
Widget build(BuildContext context) {
return PaginaConfiguracion(elementoAsociado: ArchivoIndividual(
id: '1', nombre: 'miArchivo', localizacion: 'C:\\',
conversion: Conversion(formatoOriginal: Formato(
extension: ClaseFormato.PNG
))
conversion: Conversion(formatoOriginal: ClaseFormato.PNG)
));
/*return PaginaConfiguracion(elementoAsociado: Carpeta(
id: '2', nombre: 'miCarpeta', localizacion: 'C:\\'
......
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