diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 31de08e..953a07c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
-->
+
diff --git a/lib/config/application.dart b/lib/config/application.dart
index 3fbb01d..f389f65 100644
--- a/lib/config/application.dart
+++ b/lib/config/application.dart
@@ -9,30 +9,43 @@ import 'package:sqflite/sqflite.dart';
import '../models/Category.dart';
import '../models/CaptureModel.dart';
import '../models/Series.dart';
+import '../models/Settings.dart';
class Application {
static Router router;
static Database db;
static bool _dbIsOpened = false;
static String _databaseName = 'cantonfair.db';
- static Map cache;
+ static Map cache;
static String databasePath = "";
static String appDir;
+ static String mainDir;
static String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
static Future backupDatabase() async {
await closeDatabase();
File f = new File(join(databasePath, _databaseName));
- String newPath = join(databasePath, '${timestamp()}.db');
+ var backup = join(appDir, "Backup");
+ String newPath = join(backup, '${timestamp()}.db');
+ await Directory(backup).create(recursive: true);
print("Coping to $newPath");
await f.copy(newPath);
await openDB();
}
- static Future initDatabase() async {
- Directory extPath = await getExternalStorageDirectory();
- appDir = join(extPath.path, "CantonFair");
+ static Future forceDelete() async {
+ await closeDatabase();
+ File f = new File(join(databasePath, _databaseName));
+ if (await f.exists()) await f.delete();
+ await openDB();
+ }
+
+ static Future initDatabase() async {
+ Directory extDir = await getExternalStorageDirectory();
+ mainDir = extDir.path;
+ appDir = join(mainDir, "CantonFair");
+ await Directory(appDir).create(recursive: true);
databasePath = join(appDir, "Database");
await openDB();
}
@@ -47,15 +60,9 @@ class Application {
print("Database opened !");
_dbIsOpened = true;
- // only on development
- // print("Droping ${Category.tableName} ...");
- // await db.execute("DROP TABLE ${Category.tableName};");
-
- // print("Droping ${Series.tableName} ...");
- // await db.execute("DROP TABLE ${Series.tableName};");
-
- // print("Droping ${CaptureModel.tableName} ...");
- // await db.execute("DROP TABLE ${CaptureModel.tableName};");
+ print("Creating ${Settings.tableName} ...");
+ await db.execute(Settings.dbOnCreate);
+ Settings.db = db;
print("Creating ${CaptureModel.tableName} ...");
await db.execute(CaptureModel.dbOnCreate);
@@ -71,11 +78,6 @@ class Application {
},
onCreate: (Database db, int version) async {
// only on production
- print("Creating ${Category.tableName} ...");
- await db.execute(Category.dbOnCreate);
-
- print("Creating ${Series.tableName} ...");
- await db.execute(Series.dbOnCreate);
},
);
}
diff --git a/lib/models/Series.dart b/lib/models/Series.dart
index 2e7731a..2a75f34 100644
--- a/lib/models/Series.dart
+++ b/lib/models/Series.dart
@@ -77,6 +77,7 @@ class Series {
static Future getCategoryOfSeriesUUID(uuid,
{inv: 'desc', order: 'created_at'}) async {
var series = await getSelectedSeriesByUUID(uuid);
+ if (series == null) return null;
var result = await db.rawQuery(
'SELECT * FROM ${Category.tableName} WHERE ${Category.dbUUID} = "${series.categoryUUID}" ORDER BY "$order $inv";');
List categories = [];
diff --git a/lib/models/Settings.dart b/lib/models/Settings.dart
new file mode 100644
index 0000000..e67a1e8
--- /dev/null
+++ b/lib/models/Settings.dart
@@ -0,0 +1,53 @@
+import 'dart:async';
+import 'package:sqflite/sqflite.dart';
+
+class Settings {
+ static Database db;
+
+ static final String dbKey = "key";
+ static final String dbValue = "value";
+
+ static final String tableName = "settings";
+ static final String dbOnCreate = "CREATE TABLE IF NOT EXISTS $tableName ("
+ "${Settings.dbKey} STRING PRIMARY KEY,"
+ "${Settings.dbValue} Text"
+ ")";
+
+ String key;
+ String value;
+
+ Settings({this.key, this.value});
+
+ Settings.fromMap(Map map) {
+ this.key = map[dbKey];
+ this.value = map[dbValue];
+ }
+
+ Map toMap() {
+ return {
+ dbValue: value,
+ dbKey: key,
+ };
+ }
+
+ static Future fetch(String key) async {
+ var result = await db.rawQuery(
+ 'SELECT * FROM $tableName WHERE $dbKey = "$key";');
+ List items = [];
+ for (Map item in result) {
+ items.add(new Settings.fromMap(item));
+ }
+ return items.length > 0 ? items[0] : null;
+ }
+
+ static Future save(Settings item) async {
+ await db.rawInsert(
+ 'INSERT OR REPLACE INTO '
+ '$tableName(${Settings.dbKey}, ${Settings.dbValue})'
+ ' VALUES(?, ?)',
+ [
+ item.key,
+ item.value,
+ ]);
+ }
+}
diff --git a/lib/pages/capture.dart b/lib/pages/capture.dart
index cd135d1..2672f06 100644
--- a/lib/pages/capture.dart
+++ b/lib/pages/capture.dart
@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:audio_recorder/audio_recorder.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
+import 'package:image_picker/image_picker.dart';
import '../config/application.dart';
import '../models/CaptureModel.dart';
@@ -32,13 +33,22 @@ class Choice {
const Choice({this.title, this.icon, this.camera});
}
-enum Options { video, videoRecording, photo, audio, audioRecording }
+enum Options {
+ video,
+ videoRecording,
+ photo,
+ audio,
+ audioRecording,
+ hqVideo,
+ hqPhoto,
+}
class _CaptureRoute extends State {
final GlobalKey _scaffoldKey = GlobalKey();
CameraController controller;
CaptureModel model;
+ String openTime;
var _state = Options.photo;
bool _initializing = false;
@@ -47,7 +57,9 @@ class _CaptureRoute extends State {
bool _loading = false;
final String uuid;
- _CaptureRoute({this.uuid});
+ _CaptureRoute({this.uuid}) {
+ openTime = timestamp();
+ }
@override
Widget build(BuildContext context) {
@@ -62,13 +74,6 @@ class _CaptureRoute extends State {
);
}
- @override
- void initState() {
- print("init state");
- _prepare();
- super.initState();
- }
-
@override
void dispose() {
print("capture dispose");
@@ -76,16 +81,18 @@ class _CaptureRoute extends State {
this.controller?.dispose();
}
+ @override
+ void initState() {
+ _prepare();
+ super.initState();
+ }
+
void onAudioRecordButtonPressed() {
- setState(() {
- _loading = true;
- });
+ setState(() => _loading = true);
startAudioRecord().then((String filePath) {
_state = Options.audioRecording;
if (mounted) {
- setState(() {
- _loading = false;
- });
+ setState(() => _loading = false);
if (filePath != null) {
model = CaptureModel(
filePath: filePath,
@@ -128,29 +135,90 @@ class _CaptureRoute extends State {
}
}
+ void onHQTakePhotoButtonPressed() {
+ setState(() => _loading = true);
+ hqTakePhoto().then((String filePath) {
+ if (mounted) {
+ _state = Options.hqPhoto;
+ setState(() => _loading = false);
+ if (filePath != null) {
+ model = CaptureModel(
+ filePath: filePath,
+ seriesUUID: uuid,
+ captureMode: CaptureMode.picture);
+ CaptureModel.updateItem(model);
+ }
+ setState(() {});
+ }
+ });
+ }
+
+ void onHQVideoRecordButtonPressed() {
+ setState(() => _loading = true);
+ hqVideoRecord().then((String filePath) {
+ if (mounted) {
+ _state = Options.hqVideo;
+ setState(() => _loading = false);
+ if (filePath != null) {
+ model = CaptureModel(
+ filePath: filePath,
+ seriesUUID: uuid,
+ captureMode: CaptureMode.video);
+ CaptureModel.updateItem(model);
+ }
+ setState(() {});
+ }
+ });
+ }
+
+ Future hqTakePhoto() async {
+ var image = await ImagePicker.pickImage(source: ImageSource.camera);
+ if (image == null) return null;
+ final String dirPath =
+ '${Application.appDir}/Categories/${_category.name}/${openTime}_$uuid/Photos';
+ await Directory(dirPath).create(recursive: true);
+ final String filePath = '$dirPath/${timestamp()}.jpg';
+ await image.copy(filePath);
+ await image.delete();
+ return filePath;
+ }
+
+ Future hqVideoRecord() async {
+ var video = await ImagePicker.pickVideo(source: ImageSource.camera);
+ if (video == null) return null;
+ final String dirPath =
+ '${Application.appDir}/Categories/${_category.name}/${openTime}_$uuid/Movies';
+ await Directory(dirPath).create(recursive: true);
+ final String filePath = '$dirPath/${timestamp()}.mp4';
+ await video.copy(filePath);
+ await video.delete();
+ return filePath;
+ }
+
void onTakePictureButtonPressed() {
- _loading = true;
+ setState(() => _loading = true);
takePicture().then((String filePath) {
if (mounted) {
- _loading = false;
+ _state = Options.photo;
+ setState(() => _loading = false);
if (filePath != null) {
model = CaptureModel(
filePath: filePath,
seriesUUID: uuid,
captureMode: CaptureMode.picture);
CaptureModel.updateItem(model);
- }
+ }
setState(() {});
}
});
}
void onVideoRecordButtonPressed() {
- _loading = true;
+ setState(() => _loading = true);
startVideoRecording().then((String filePath) {
if (mounted) {
_state = Options.videoRecording;
- _loading = false;
+ setState(() => _loading = false);
if (filePath != null) {
model = CaptureModel(
filePath: filePath,
@@ -170,9 +238,20 @@ class _CaptureRoute extends State {
});
}
+ void showInSnackBar(String message) {
+ _scaffoldKey.currentState.showSnackBar(
+ SnackBar(
+ content: Text(message),
+ action: SnackBarAction(
+ label: 'HIDE',
+ onPressed: _scaffoldKey.currentState.hideCurrentSnackBar),
+ ),
+ );
+ }
+
Future startAudioRecord() async {
final String dirPath =
- '${Application.appDir}/Categories/${_category.name}/$uuid/Audios';
+ '${Application.appDir}/Categories/${_category.name}/${openTime}_$uuid/Audios';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.aac';
@@ -186,17 +265,6 @@ class _CaptureRoute extends State {
return filePath;
}
- void showInSnackBar(String message) {
- _scaffoldKey.currentState.showSnackBar(
- SnackBar(
- content: Text(message),
- action: SnackBarAction(
- label: 'HIDE',
- onPressed: _scaffoldKey.currentState.hideCurrentSnackBar),
- ),
- );
- }
-
Future startVideoRecording() async {
if (!controller.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
@@ -204,7 +272,7 @@ class _CaptureRoute extends State {
}
final String dirPath =
- '${Application.appDir}/Categories/${_category.name}/$uuid/Movies';
+ '${Application.appDir}/Categories/${_category.name}/${openTime}_$uuid/Movies';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.mp4';
@@ -247,7 +315,7 @@ class _CaptureRoute extends State {
return null;
}
final String dirPath =
- '${Application.appDir}/Categories/${_category.name}/$uuid/Pictures';
+ '${Application.appDir}/Categories/${_category.name}/${openTime}_$uuid/Pictures';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
@@ -294,6 +362,18 @@ class _CaptureRoute extends State {
Options.audio,
),
),
+ IconButton(
+ color: whiteColor,
+ icon: Icon(Icons.ondemand_video),
+ onPressed:
+ _changeIfState(_state == Options.hqVideo, Options.hqVideo),
+ ),
+ IconButton(
+ color: whiteColor,
+ icon: Icon(Icons.camera_enhance),
+ onPressed:
+ _changeIfState(_state == Options.hqPhoto, Options.hqPhoto),
+ ),
],
),
),
@@ -310,7 +390,7 @@ class _CaptureRoute extends State {
}
Function _captureButtonHandler() {
- if (_loading) return null;
+ if (_loading || _initializing) return null;
return () {
if (_state == Options.photo) {
onTakePictureButtonPressed();
@@ -322,6 +402,10 @@ class _CaptureRoute extends State {
onAudioRecordButtonPressed();
} else if (_state == Options.audioRecording) {
onAudioStopButtonPressed();
+ } else if (_state == Options.hqVideo) {
+ onHQVideoRecordButtonPressed();
+ } else if (_state == Options.hqPhoto) {
+ onHQTakePhotoButtonPressed();
}
};
}
@@ -338,15 +422,17 @@ class _CaptureRoute extends State {
Widget _floatingButton() {
return FloatingActionButton(
- backgroundColor: secondaryColor,
- foregroundColor: whiteColor,
+ backgroundColor:
+ (_state == Options.audioRecording || _state == Options.videoRecording)
+ ? whiteColor
+ : secondaryColor,
child: _floatingButtonChild(),
onPressed: _captureButtonHandler(),
);
}
Widget _floatingButtonChild() {
- if (_loading) {
+ if (_loading || _initializing) {
return Padding(
padding: EdgeInsets.all(15.0),
child: CircularProgressIndicator(
@@ -357,8 +443,8 @@ class _CaptureRoute extends State {
if (_state == Options.audioRecording || _state == Options.videoRecording) {
return Icon(
Icons.stop,
- size: 50.0,
- color: Colors.black.withRed(50),
+ size: 35.0,
+ color: Colors.redAccent,
);
}
return Icon(Icons.archive);
@@ -373,12 +459,16 @@ class _CaptureRoute extends State {
}
void _prepare() async {
- _initializing = true;
+ setState(() => _initializing = true);
+ await Future.delayed(Duration(milliseconds: 250));
_category = await Series.getCategoryOfSeriesUUID(uuid);
+ if (_category == null) {
+ showInSnackBar("Category not found !");
+ return;
+ }
await onNewCameraSelected(choices[0].camera);
- await Future.delayed(Duration(milliseconds: 500));
- _initializing = false;
- setState(() {});
+ await Future.delayed(Duration(milliseconds: 250));
+ setState(() => _initializing = false);
}
void _showCameraException(CameraException e) {
diff --git a/lib/pages/home.dart b/lib/pages/home.dart
index 69fc36f..b10153c 100644
--- a/lib/pages/home.dart
+++ b/lib/pages/home.dart
@@ -204,6 +204,9 @@ class _HomeRoute extends State {
),
),
ListTile(
+ onLongPress: () {
+ Application.router.navigateTo(context, '/categories');
+ },
title: Text("Category"),
leading: Icon(Icons.list),
subtitle:
diff --git a/lib/pages/item-view.dart b/lib/pages/item-view.dart
index 23a2d1e..1e32957 100644
--- a/lib/pages/item-view.dart
+++ b/lib/pages/item-view.dart
@@ -1,5 +1,11 @@
+import 'dart:io';
+import 'dart:async';
+
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
+import 'package:audioplayers/audioplayers.dart';
+import 'package:video_player/video_player.dart';
+import 'package:video_launcher/video_launcher.dart';
import '../utils/ui.dart';
import '../models/CaptureModel.dart';
@@ -20,9 +26,17 @@ class _ItemViewRouteRoute extends State {
int index;
String tag;
SwiperController controller = new SwiperController();
+ AudioPlayer audioPlayer = new AudioPlayer();
+ VideoPlayerController videoController;
_ItemViewRouteRoute({this.tag, this.list, this.index});
+ @override
+ void dispose() {
+ super.dispose();
+ videoController?.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Hero(
@@ -31,6 +45,13 @@ class _ItemViewRouteRoute extends State {
body: Container(
child: Swiper(
onIndexChanged: (current) {
+ audioPlayer?.stop();
+ if (videoController != null) {
+ if (videoController.value.isPlaying) {
+ videoController?.seekTo(Duration(seconds: 0));
+ videoController?.pause();
+ }
+ }
setState(() {
tag = list[current].uuid;
index = current;
@@ -38,7 +59,8 @@ class _ItemViewRouteRoute extends State {
},
controller: controller,
pagination: new SwiperPagination(
- margin: new EdgeInsets.all(5.0),
+ alignment: Alignment.topCenter,
+ margin: new EdgeInsets.all(35.0),
),
control: new SwiperControl(
color: whiteColor,
@@ -46,17 +68,12 @@ class _ItemViewRouteRoute extends State {
index: index,
itemBuilder: (BuildContext context, int index) {
CaptureMode type = list[index].captureMode;
- if (type == CaptureMode.video) return Container();
- if (type == CaptureMode.audio) return Container();
+ if (type == CaptureMode.video)
+ return VideoPlayerWidget(videoController, list[index].filePath);
+ if (type == CaptureMode.audio)
+ return AudioPlayerWidget(audioPlayer, list[index].filePath);
if (type == CaptureMode.picture)
- return Container(
- decoration: BoxDecoration(
- color: Colors.black.withOpacity(0.5),
- image: DecorationImage(
- image: AssetImage(list[index].filePath),
- fit: BoxFit.cover,
- )),
- );
+ return ImageViewWidget(list[index].filePath);
},
itemCount: list.length,
),
@@ -70,3 +87,278 @@ class _ItemViewRouteRoute extends State {
super.initState();
}
}
+
+class ImageViewWidget extends StatelessWidget {
+ final String filePath;
+ ImageViewWidget(this.filePath);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ child: Column(
+ children: [
+ new Expanded(
+ child: new Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ image: DecorationImage(
+ image: AssetImage(filePath),
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ new Container(
+ color: primaryColor,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ FlatButton(
+ child: Text("Image Viewer", style: TextStyle(color: whiteColor)),
+ onPressed: null,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class VideoPlayerWidget extends StatefulWidget {
+ final String filePath;
+ final VideoPlayerController controller;
+ VideoPlayerWidget(this.controller, this.filePath);
+
+ @override
+ _VideoPlayerWidget createState() =>
+ new _VideoPlayerWidget(this.controller, this.filePath);
+}
+
+class _VideoPlayerWidget extends State {
+ VideoPlayerController controller;
+ final String filePath;
+ bool _isPlaying = false;
+ _VideoPlayerWidget(this.controller, this.filePath);
+
+ @override
+ void initState() {
+ super.initState();
+ controller = VideoPlayerController.file(File(filePath))
+ ..addListener(() {
+ final bool isPlaying = controller.value.isPlaying;
+ if (isPlaying != _isPlaying) {
+ if (mounted)
+ setState(() {
+ _isPlaying = isPlaying;
+ });
+ }
+ })
+ ..initialize().then((_) {
+ if (mounted) setState(() {});
+ });
+ }
+
+ Future _launchVideo(path) async {
+ if (await canLaunchVideo(path, isLocal: true)) {
+ await launchVideo(path, isLocal: true);
+ return true;
+ }
+ return false;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ child: Column(
+ children: [
+ new Expanded(
+ child: controller.value.initialized
+ ? Center(
+ child: Align(
+ child: VideoPlayer(controller),
+ ),
+ )
+ : Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ image: DecorationImage(
+ image: AssetImage("assets/purple-materials.jpg"),
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ new Container(
+ color: primaryColor,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ FlatButton(
+ child: Icon(
+ controller.value.isPlaying
+ ? Icons.pause
+ : Icons.play_arrow,
+ color: whiteColor),
+ onPressed: () {
+ if (controller.value.isPlaying)
+ controller.pause();
+ else
+ controller.play();
+ },
+ ),
+ FlatButton(
+ child: Icon(Icons.stop, color: whiteColor),
+ onPressed: () {
+ if (controller.value.isPlaying) {
+ controller.seekTo(Duration(seconds: 0));
+ controller.pause();
+ }
+ },
+ ),
+ FlatButton(
+ child: Icon(Icons.open_with, color: whiteColor),
+ onPressed: () {
+ _launchVideo(filePath).then((result) {
+ print("Error on launch video !");
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class AudioPlayerWidget extends StatefulWidget {
+ final String filePath;
+ final AudioPlayer player;
+ AudioPlayerWidget(this.player, this.filePath);
+
+ @override
+ _AudioPlayerWidget createState() =>
+ new _AudioPlayerWidget(this.player, this.filePath);
+}
+
+class _AudioPlayerWidget extends State {
+ final String filePath;
+ _AudioPlayerWidget(this.audioPlayer, this.filePath);
+ int state = 0;
+ AudioPlayer audioPlayer;
+ Duration duration = Duration(seconds: 0), position = Duration(seconds: 0);
+
+ Function _playHandler() {
+ if (state == 0) {
+ return () {
+ audioPlayer.play(filePath, isLocal: true).then((result) {
+ setState(() {
+ state = 1;
+ });
+ });
+ };
+ }
+ if (state == 2) {
+ return () {
+ audioPlayer.resume().then((result) {
+ setState(() {
+ state = 1;
+ });
+ });
+ };
+ }
+ return null;
+ }
+
+ Function _stopHandler() {
+ if (state != 0) {
+ return () {
+ audioPlayer.stop().then((result) {
+ setState(() {
+ state = 0;
+ duration = Duration(seconds: 0);
+ position = Duration(seconds: 0);
+ });
+ });
+ };
+ }
+ return null;
+ }
+
+ Function _pauseHandler() {
+ if (state == 1) {
+ return () {
+ audioPlayer.pause().then((result) {
+ setState(() {
+ state = 2;
+ });
+ });
+ };
+ }
+ return null;
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ audioPlayer.positionHandler = (Duration p) {
+ setState(() => position = p);
+ };
+ audioPlayer.durationHandler = (Duration d) {
+ setState(() => duration = d);
+ };
+ audioPlayer.completionHandler = () {
+ setState(() {
+ state = 0;
+ position = duration;
+ });
+ };
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ child: Column(
+ children: [
+ new Expanded(
+ child: new Container(
+ decoration: BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
+ image: DecorationImage(
+ image: AssetImage("assets/purple-materials.jpg"),
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ new LinearProgressIndicator(
+ value: (position.inSeconds / (duration.inSeconds * 1.0)),
+ ),
+ new Container(
+ color: primaryColor,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ FlatButton(
+ child: Icon(Icons.play_arrow, color: whiteColor),
+ onPressed: _playHandler(),
+ ),
+ FlatButton(
+ child: Icon(Icons.pause, color: whiteColor),
+ onPressed: _pauseHandler(),
+ ),
+ FlatButton(
+ child: Icon(Icons.stop, color: whiteColor),
+ onPressed: _stopHandler(),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/pages/selected-series.dart b/lib/pages/selected-series.dart
index 28e681f..6486f89 100644
--- a/lib/pages/selected-series.dart
+++ b/lib/pages/selected-series.dart
@@ -153,6 +153,11 @@ class _SelectedSeriesRoute extends State
subtitle: Text(_makeTitle(_selectedSeries.description)),
leading: Icon(Icons.description),
),
+ ListTile(
+ title: Text("UUID:"),
+ subtitle: Text(_selectedSeries.uuid),
+ leading: Icon(Icons.code),
+ ),
ListTile(
title: Text("Created at:"),
subtitle: Text(formatter.format(_selectedSeries.createdAt)),
@@ -231,6 +236,17 @@ class _SelectedSeriesRoute extends State
title: Text(
_makeTitle(_selectedSeries.title),
),
+ // actions: [
+ // FlatButton(
+ // child: Text(
+ // "Open as",
+ // style: TextStyle(
+ // color: whiteColor,
+ // ),
+ // ),
+ // onPressed: _openIntent,
+ // )
+ // ],
),
body: _items.length > 0 ? _tabBarView() : _listView(),
bottomNavigationBar: _items.length > 0 ? _bottomBar() : null,
diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart
index df75799..260b296 100644
--- a/lib/pages/settings.dart
+++ b/lib/pages/settings.dart
@@ -1,8 +1,13 @@
+import 'dart:io';
+import 'package:archive/archive_io.dart';
+import 'dart:async';
import 'package:flutter/material.dart';
import '../utils/ui.dart';
import '../config/application.dart';
+String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
+
class SettingsRoute extends StatefulWidget {
@override
_SettingsRoute createState() => new _SettingsRoute();
@@ -34,26 +39,46 @@ class _SettingsRoute extends State {
? _showLoading()
: ListView(
children: [
- // ListTile(
- // title: Text("Clear database"),
- // leading: Icon(Icons.storage),
- // onTap: () {
- // confirmDialog(context, "Are you sure ?", () {});
- // },
- // ),
+ ListTile(
+ title: Text("Backup from contents"),
+ leading: Icon(Icons.archive),
+ onTap: () {
+ confirmDialog(context,
+ "This may take long, do you want to continue ?", () {
+ setState(() => _loading = true);
+ Future.delayed(Duration(seconds: 1)).then((_) {
+ var encoder = new ZipFileEncoder();
+ encoder.create(
+ '${Application.mainDir}/CantonFair_${timestamp()}.zip');
+ encoder.addDirectory(new Directory(Application.appDir));
+ encoder.close();
+ setState(() => _loading = false);
+ });
+ });
+ },
+ ),
+ Divider(),
+ ListTile(
+ title: Text("Clear database"),
+ leading: Icon(Icons.storage),
+ onTap: () {
+ confirmDialog(context, "Are you sure ?", () {
+ setState(() => _loading = true);
+ Application.forceDelete().then((_) {
+ Navigator.pushReplacementNamed(context, "/");
+ });
+ });
+ },
+ ),
ListTile(
title: Text("Backup from database"),
leading: Icon(Icons.backup),
onTap: () {
confirmDialog(
context, "Do you want to backup your database ?", () {
- setState(() {
- _loading = true;
- });
+ setState(() => _loading = true);
Application.backupDatabase().then((_) {
- setState(() {
- _loading = false;
- });
+ setState(() => _loading = false);
});
});
},
diff --git a/lib/pages/splash-screen.dart b/lib/pages/splash-screen.dart
index aea75f5..8b2986a 100644
--- a/lib/pages/splash-screen.dart
+++ b/lib/pages/splash-screen.dart
@@ -72,11 +72,16 @@ class _SplashScreenRoute extends State {
Future requestPermissions() async {
final writeAccess = await SimplePermissions.requestPermission(
Permission.WriteExternalStorage);
+ final readAccess = await SimplePermissions.requestPermission(
+ Permission.ReadExternalStorage);
final cameraAccess =
await SimplePermissions.requestPermission(Permission.Camera);
final recordAudio =
await SimplePermissions.requestPermission(Permission.RecordAudio);
+ if (readAccess != PermissionStatus.authorized) {
+ return false;
+ }
if (writeAccess != PermissionStatus.authorized) {
return false;
}
@@ -93,7 +98,6 @@ class _SplashScreenRoute extends State {
@override
void initState() {
super.initState();
-
requestPermissions().then((ok) {
if (ok) {
_handleStart();
@@ -104,7 +108,7 @@ class _SplashScreenRoute extends State {
}
_handleStart() {
- Application.cache = new Map();
+ Application.cache = new Map();
try {
availableCameras().then((cameras) {
@@ -119,10 +123,12 @@ class _SplashScreenRoute extends State {
);
}
- Application.closeDatabase().then((result) {
- Application.initDatabase().then((data) {
- Category.getCategories().then((categories) {
- Application.cache["categories"] = categories;
+ Application.closeDatabase().then((_) {
+ Application.initDatabase().then((_) {
+ Future.wait([
+ Category.getCategories(),
+ ]).then((results) {
+ Application.cache["categories"] = results[0];
_startApp();
});
});
diff --git a/lib/utils/card.dart b/lib/utils/card.dart
index dde40e0..4f82a59 100644
--- a/lib/utils/card.dart
+++ b/lib/utils/card.dart
@@ -1,6 +1,4 @@
-import 'dart:io';
import 'package:flutter/painting.dart';
-import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
import '../models/CaptureModel.dart';
@@ -123,36 +121,13 @@ class _VideoAppCardState extends State {
this.loadingImage = loadingImage;
}
- void dispose() {
- print("video dispose !");
- super.dispose();
- }
-
- VideoPlayerController _controller;
- bool _isPlaying = false;
-
@override
- void initState() {
- super.initState();
- _controller = VideoPlayerController.file(
- File(filepath),
- )
- ..addListener(() {
- final bool isPlaying = _controller.value.isPlaying;
- if (isPlaying != _isPlaying) {
- setState(() {
- _isPlaying = isPlaying;
- });
- }
- })
- ..initialize().then((_) {
- setState(() {});
- });
- }
-
- Widget _loading() {
+ Widget build(BuildContext context) {
+ print("Building video card ...");
return Container(
+ child: _overlay(context, CaptureMode.video),
decoration: new BoxDecoration(
+ color: Colors.black.withOpacity(0.5),
image: new DecorationImage(
image: new AssetImage(loadingImage),
fit: BoxFit.cover,
@@ -160,20 +135,4 @@ class _VideoAppCardState extends State {
),
);
}
-
- Widget _player() {
- // FUCCKK THISS SHITT !
- // flutter has long way to become a perfect mobile framework
- // cause of texture does not support border radius
- return Container(
- color: Colors.black.withOpacity(0.5),
- child: VideoPlayer(_controller),
- );
- }
-
- @override
- Widget build(BuildContext context) {
- print("Building video card ...");
- return _controller.value.initialized ? _player() : _loading();
- }
}
diff --git a/packages/flutter_video_launcher/.gitignore b/packages/flutter_video_launcher/.gitignore
new file mode 100644
index 0000000..14c7d4c
--- /dev/null
+++ b/packages/flutter_video_launcher/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+.atom/
+.idea
+.packages
+.pub/
+build/
+ios/.generated/
+packages
+pubspec.lock
diff --git a/packages/flutter_video_launcher/CHANGELOG.md b/packages/flutter_video_launcher/CHANGELOG.md
new file mode 100644
index 0000000..2aeee0f
--- /dev/null
+++ b/packages/flutter_video_launcher/CHANGELOG.md
@@ -0,0 +1,23 @@
+# Changelog
+
+## 0.3.0
+
+- fix the local video playing on android
+- fix the example
+
+## 0.2.2
+
+- changed Flutter dependency in pubspec.yaml to an SDK dependency
+
+## 0.2.1
+
+- fix the iOS local playing
+- add download/play local in the example
+
+## 0.2.0
+
+- android
+
+## 0.1.0
+
+- iOS
\ No newline at end of file
diff --git a/packages/flutter_video_launcher/LICENSE b/packages/flutter_video_launcher/LICENSE
new file mode 100644
index 0000000..86928f6
--- /dev/null
+++ b/packages/flutter_video_launcher/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2017 Your Company. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Your Company nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/flutter_video_launcher/README.md b/packages/flutter_video_launcher/README.md
new file mode 100644
index 0000000..0f687b9
--- /dev/null
+++ b/packages/flutter_video_launcher/README.md
@@ -0,0 +1,96 @@
+# VideoLauncher
+
+## Example
+
+```bash
+cd flutter_video_launcher/example
+flutter packages get
+flutter run
+```
+
+![screenshot](https://github.com/rxlabz/flutter_video_launcher/blob/master/screenshot.png)
+
+## Usage
+
+To use this plugin, add video_launcher as a dependency in your pubspec.yaml file.
+
+After importing 'package:video_launcher/video_launcher.dart' the directories can be queried as follows
+
+## Remote video
+
+To launch a video :
+
+```dart
+Future _launch(String url) async {
+ if (await canLaunchVideo(url)) {
+ await launchVideo(url);
+ } else {
+ throw 'Could not launch $url';
+ }
+ }
+```
+
+## Local video
+
+To play a local video file :
+
+```dart
+Future _launch(String url) async {
+ if (await canLaunchVideo(url, isLocal:true)) {
+ await launchVideo(url, isLocal:true);
+ } else {
+ throw 'Could not launch $url';
+ }
+ }
+```
+
+## Embedded asset
+
+To play video embedded assets, you can copy them to the app storage folder, and the play them "locally"
+
+```dart
+void _loadOrLaunchLocalAsset() =>
+ localAssetPath != null ? _launchLocalAsset() : copyLocalAsset();
+
+Future copyLocalAsset() async {
+ final dir = await getApplicationDocumentsDirectory();
+ final file = new File("${dir.path}/video_asset.mp4");
+ final videoData = await rootBundle.load("assets/video.mp4");
+ final bytes = videoData.buffer.asUint8List();
+ file.writeAsBytes(bytes, flush: true);
+ setState(() {
+ localAssetPath = file.path;
+ _launchLocalAsset();
+});
+}
+
+void _launchLocalAsset() =>
+ setState(() => _launched = _launchVideo(localAssetPath, isLocal: true));
+```
+
+Please see the example app of this plugin for a full example.
+
+
+## :warning: iOS App Transport Security
+
+By default iOS forbids loading from non-https url. To cancel this restriction edit your .plist and add :
+
+```xml
+NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+```
+
+cf. [Configuring App Transport Security Exceptions in iOS](https://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/)
+
+## Troubleshooting
+
+If you get a MissingPluginException, try to `flutter build apk` on Android, or `flutter build ios`
+
+## Getting Started
+
+For help getting started with Flutter, view our online
+[documentation](http://flutter.io/).
+
\ No newline at end of file
diff --git a/packages/flutter_video_launcher/android/.gitignore b/packages/flutter_video_launcher/android/.gitignore
new file mode 100644
index 0000000..5c4ef82
--- /dev/null
+++ b/packages/flutter_video_launcher/android/.gitignore
@@ -0,0 +1,12 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+
+/gradle
+/gradlew
+/gradlew.bat
diff --git a/packages/flutter_video_launcher/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/packages/flutter_video_launcher/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
new file mode 100644
index 0000000..00c52dd
--- /dev/null
+++ b/packages/flutter_video_launcher/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
@@ -0,0 +1,25 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+import bz.rxla.video_launcher.VideoLauncherPlugin;
+
+/**
+ * Generated file. Do not edit.
+ */
+public final class GeneratedPluginRegistrant {
+ public static void registerWith(PluginRegistry registry) {
+ if (alreadyRegisteredWith(registry)) {
+ return;
+ }
+ VideoLauncherPlugin.registerWith(registry.registrarFor("bz.rxla.video_launcher.VideoLauncherPlugin"));
+ }
+
+ private static boolean alreadyRegisteredWith(PluginRegistry registry) {
+ final String key = GeneratedPluginRegistrant.class.getCanonicalName();
+ if (registry.hasPlugin(key)) {
+ return true;
+ }
+ registry.registrarFor(key);
+ return false;
+ }
+}
diff --git a/packages/flutter_video_launcher/android/build.gradle b/packages/flutter_video_launcher/android/build.gradle
new file mode 100644
index 0000000..f39be84
--- /dev/null
+++ b/packages/flutter_video_launcher/android/build.gradle
@@ -0,0 +1,32 @@
+group 'bz.rxla.video_launcher'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.3'
+
+ defaultConfig {
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+}
diff --git a/packages/flutter_video_launcher/android/gradle.properties b/packages/flutter_video_launcher/android/gradle.properties
new file mode 100644
index 0000000..8bd86f6
--- /dev/null
+++ b/packages/flutter_video_launcher/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/packages/flutter_video_launcher/android/settings.gradle b/packages/flutter_video_launcher/android/settings.gradle
new file mode 100644
index 0000000..1305243
--- /dev/null
+++ b/packages/flutter_video_launcher/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'video_launcher'
diff --git a/packages/flutter_video_launcher/android/src/main/AndroidManifest.xml b/packages/flutter_video_launcher/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..02d94d5
--- /dev/null
+++ b/packages/flutter_video_launcher/android/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/packages/flutter_video_launcher/android/src/main/java/bz/rxla/video_launcher/VideoLauncherPlugin.java b/packages/flutter_video_launcher/android/src/main/java/bz/rxla/video_launcher/VideoLauncherPlugin.java
new file mode 100644
index 0000000..3fdf6ba
--- /dev/null
+++ b/packages/flutter_video_launcher/android/src/main/java/bz/rxla/video_launcher/VideoLauncherPlugin.java
@@ -0,0 +1,85 @@
+package bz.rxla.video_launcher;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.PluginRegistry;
+
+import java.io.File;
+import java.util.HashMap;
+
+
+/**
+ * VideoLauncherPlugin
+ */
+public class VideoLauncherPlugin implements MethodChannel.MethodCallHandler {
+ private final Activity activity;
+
+ public static void registerWith(PluginRegistry.Registrar registrar) {
+ MethodChannel channel =
+ new MethodChannel(registrar.messenger(), "bz.rxla.flutter/video_launcher");
+ VideoLauncherPlugin instance = new VideoLauncherPlugin(registrar.activity());
+ channel.setMethodCallHandler(instance);
+ }
+
+ private VideoLauncherPlugin(Activity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
+ String url = ((HashMap) call.arguments()).get("url").toString();
+ Boolean isLocal = ((HashMap) call.arguments()).get("isLocal").toString() == "1";
+ if (call.method.equals("canLaunchVideo")) {
+ canLaunchVideo(url, result, isLocal);
+ } else if (call.method.equals("launchVideo")) {
+ launchURLVideo(url, result, isLocal);
+ } else {
+ result.notImplemented();
+ }
+ }
+
+ private void launchURLVideo(String url, MethodChannel.Result result, Boolean isLocal) {
+ if( isLocal ){
+ File f = new File(url);
+ f.setReadable(true, false);
+ Uri videoUri = Uri.fromFile(f);
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+ launchIntent.setDataAndType(Uri.parse(videoUri.toString()), "video/mp4");
+ activity.startActivity(launchIntent);
+ result.success(null);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.setDataAndType(Uri.parse(url), "video/mp4");
+ activity.startActivity(intent);
+ result.success(null);
+ }
+ }
+
+ private void canLaunchVideo(String url, MethodChannel.Result result, Boolean isLocal) {
+ boolean canLaunch;
+
+ if (isLocal) {
+ File f = new File(url);
+ f.setReadable(true, false);
+ Uri videoUri = Uri.fromFile(f);
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+ launchIntent.setDataAndType(Uri.parse(videoUri.toString()), "video/mp4");
+ ComponentName componentName = launchIntent.resolveActivity(activity.getPackageManager());
+ canLaunch = componentName != null
+ && !"{com.android.fallback/com.android.fallback.Fallback}"
+ .equals(componentName.toShortString());
+ } else {
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+ launchIntent.setDataAndType(Uri.parse(url), "video/mp4");
+ ComponentName componentName = launchIntent.resolveActivity(activity.getPackageManager());
+ canLaunch = componentName != null
+ && !"{com.android.fallback/com.android.fallback.Fallback}"
+ .equals(componentName.toShortString());
+ }
+ result.success(canLaunch);
+ }
+}
diff --git a/packages/flutter_video_launcher/ios/.gitignore b/packages/flutter_video_launcher/ios/.gitignore
new file mode 100644
index 0000000..956c87f
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/.gitignore
@@ -0,0 +1,31 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
diff --git a/packages/flutter_video_launcher/ios/Assets/.gitkeep b/packages/flutter_video_launcher/ios/Assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.h b/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.h
new file mode 100644
index 0000000..a25746c
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface VideoLauncherPlugin : NSObject
+@end
diff --git a/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.m b/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.m
new file mode 100644
index 0000000..ad1e59a
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/Classes/VideoLauncherPlugin.m
@@ -0,0 +1,74 @@
+#import "VideoLauncherPlugin.h"
+#import
+#import
+
+@implementation VideoLauncherPlugin
+
++ (void)registerWithRegistrar:(NSObject *)registrar {
+ FlutterMethodChannel *channel =
+ [FlutterMethodChannel methodChannelWithName:@"bz.rxla.flutter/video_launcher"
+ binaryMessenger:registrar.messenger];
+ [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
+ NSString *url = call.arguments[@"url"];
+ BOOL isLocal = call.arguments[@"isLocal"] == @1;
+
+ if ([@"canLaunchVideo" isEqualToString:call.method]) {
+ result(isLocal ? @([self fileExists:url]) : @([self canLaunchURLVideo:url]));
+ } else if ([@"launchVideo" isEqualToString:call.method]) {
+ [self launchURLVideo:url result:result isLocal:isLocal];
+ } else {
+ result(FlutterMethodNotImplemented);
+ }
+ }];
+}
+
++ (BOOL)fileExists:(NSString *)pathString {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ return [fileManager fileExistsAtPath:pathString];
+}
+
++ (BOOL)canLaunchURLVideo:(NSString *)urlString {
+ NSURL *url = [NSURL URLWithString:urlString];
+ UIApplication *application = [UIApplication sharedApplication];
+ return [application canOpenURL:url];
+}
+
++ (void)launchURLVideo:(NSString *)urlString result:(FlutterResult)result isLocal:(BOOL)isLocal {
+ NSURL *url = isLocal ? [NSURL fileURLWithPath:urlString] : [NSURL URLWithString:urlString];
+
+ AVPlayer *player = [AVPlayer playerWithURL:url];
+
+ AVPlayerViewController *playerViewController = [AVPlayerViewController new];
+ playerViewController.showsPlaybackControls = YES;
+ playerViewController.player = player;
+
+ UIViewController *viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
+ [viewController.view addSubview:playerViewController.view];
+ [viewController presentViewController:playerViewController animated:YES completion:nil];
+
+ [player play];
+
+// Using ifdef as workaround to support running with Xcode 7.0 and sdk version 9
+// where the dynamic check fails.
+#if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_9_0
+ [application openURL:url
+ options:@{}
+ completionHandler:^(BOOL success) {
+ [self sendResult:success result:result url:url];
+ }];
+#else
+ [self sendResult:YES result:result url:url];
+#endif
+}
+
++ (void)sendResult:(BOOL)success result:(FlutterResult)result url:(NSURL *)url {
+ if (success) {
+ result(nil);
+ } else {
+ result([FlutterError errorWithCode:@"Error"
+ message:[NSString stringWithFormat:@"Error while launching %@", url]
+ details:nil]);
+ }
+}
+
+@end
diff --git a/packages/flutter_video_launcher/ios/Flutter/Generated.xcconfig b/packages/flutter_video_launcher/ios/Flutter/Generated.xcconfig
new file mode 100644
index 0000000..f5aa7a3
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/Flutter/Generated.xcconfig
@@ -0,0 +1,9 @@
+// This is a generated file; do not edit or check into version control.
+FLUTTER_ROOT=/home/aiden/flutter/flutter
+FLUTTER_APPLICATION_PATH=/home/aiden/flutter/projects/cantonfair/packages/flutter_video_launcher
+FLUTTER_TARGET=lib/main.dart
+FLUTTER_BUILD_MODE=debug
+FLUTTER_BUILD_DIR=build
+SYMROOT=${SOURCE_ROOT}/../build/ios
+FLUTTER_FRAMEWORK_DIR=/home/aiden/flutter/flutter/bin/cache/artifacts/engine/ios
+FLUTTER_BUILD_NAME=0.3.0
diff --git a/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.h b/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.h
new file mode 100644
index 0000000..3b700eb
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.h
@@ -0,0 +1,14 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GeneratedPluginRegistrant_h
+#define GeneratedPluginRegistrant_h
+
+#import
+
+@interface GeneratedPluginRegistrant : NSObject
++ (void)registerWithRegistry:(NSObject*)registry;
+@end
+
+#endif /* GeneratedPluginRegistrant_h */
diff --git a/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.m b/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.m
new file mode 100644
index 0000000..9fde087
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/Runner/GeneratedPluginRegistrant.m
@@ -0,0 +1,14 @@
+//
+// Generated file. Do not edit.
+//
+
+#import "GeneratedPluginRegistrant.h"
+#import
+
+@implementation GeneratedPluginRegistrant
+
++ (void)registerWithRegistry:(NSObject*)registry {
+ [VideoLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"VideoLauncherPlugin"]];
+}
+
+@end
diff --git a/packages/flutter_video_launcher/ios/video_launcher.podspec b/packages/flutter_video_launcher/ios/video_launcher.podspec
new file mode 100644
index 0000000..6e11ba7
--- /dev/null
+++ b/packages/flutter_video_launcher/ios/video_launcher.podspec
@@ -0,0 +1,21 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
+#
+Pod::Spec.new do |s|
+ s.name = 'video_launcher'
+ s.version = '0.0.1'
+ s.summary = 'A new flutter plugin project.'
+ s.description = <<-DESC
+A new flutter plugin project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.public_header_files = 'Classes/**/*.h'
+ s.dependency 'Flutter'
+
+ s.ios.deployment_target = '8.0'
+end
+
diff --git a/packages/flutter_video_launcher/lib/video_launcher.dart b/packages/flutter_video_launcher/lib/video_launcher.dart
new file mode 100644
index 0000000..80870f3
--- /dev/null
+++ b/packages/flutter_video_launcher/lib/video_launcher.dart
@@ -0,0 +1,27 @@
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+
+const _channel = const MethodChannel('bz.rxla.flutter/video_launcher');
+
+/// Parses the specified video URL string and delegates handling of it to the
+/// underlying platform.
+///
+/// The returned future completes with a [PlatformException] on invalid URLs and
+/// schemes which cannot be handled, that is when [canLaunchVideo] would complete
+/// with false.
+Future launchVideo(String urlString, {bool isLocal:false}) {
+ return _channel.invokeMethod(
+ /* FIXME had some trouble to send a false BOOL to objC => for now I send 1 || 0 */
+ 'launchVideo',{"url":urlString, "isLocal":isLocal ? 1 : 0 },
+ );
+}
+
+/// Checks whether the specified video URL can be handled by some app installed on the
+/// device.
+Future canLaunchVideo(String urlString, {bool isLocal:false}) async {
+ if (urlString == null) return false;
+ return await _channel.invokeMethod(
+ 'canLaunchVideo',{"url":urlString, "isLocal":isLocal ? 1 : 0}
+ );
+}
diff --git a/packages/flutter_video_launcher/pubspec.yaml b/packages/flutter_video_launcher/pubspec.yaml
new file mode 100644
index 0000000..d8cdd3b
--- /dev/null
+++ b/packages/flutter_video_launcher/pubspec.yaml
@@ -0,0 +1,17 @@
+name: video_launcher
+description: Fullscreen video launcher plugin
+version: 0.3.0
+author: Erick Ghaumez
+homepage: https://github.com/rxlabz/flutter_video_launcher
+
+flutter:
+ plugin:
+ androidPackage: bz.rxla.video_launcher
+ pluginClass: VideoLauncherPlugin
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+environment:
+ sdk: ">=2.0.0 <2.2.0"
diff --git a/packages/flutter_video_launcher/screenshot.png b/packages/flutter_video_launcher/screenshot.png
new file mode 100755
index 0000000..ab41470
Binary files /dev/null and b/packages/flutter_video_launcher/screenshot.png differ
diff --git a/packages/flutter_video_launcher/video_launcher.iml b/packages/flutter_video_launcher/video_launcher.iml
new file mode 100644
index 0000000..7f9681f
--- /dev/null
+++ b/packages/flutter_video_launcher/video_launcher.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pubspec.lock b/pubspec.lock
index 9bde5d5..213f31a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -8,8 +8,15 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.32.4"
+ android_intent:
+ dependency: "direct main"
+ description:
+ name: android_intent
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.1"
archive:
- dependency: transitive
+ dependency: "direct main"
description:
name: archive
url: "https://pub.dartlang.org"
@@ -36,6 +43,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.6"
+ audioplayers:
+ dependency: "direct main"
+ description:
+ name: audioplayers
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.7.8"
boolean_selector:
dependency: transitive
description:
@@ -186,6 +200,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
+ image_picker:
+ dependency: "direct main"
+ description:
+ name: image_picker
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.10"
intl:
dependency: "direct main"
description:
@@ -298,6 +319,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
plugin:
dependency: transitive
description:
@@ -471,6 +499,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
+ video_launcher:
+ dependency: "direct main"
+ description:
+ path: "packages/flutter_video_launcher"
+ relative: true
+ source: path
+ version: "0.3.0"
video_player:
dependency: "direct main"
description:
@@ -514,5 +549,5 @@ packages:
source: hosted
version: "2.1.15"
sdks:
- dart: ">=2.0.0 <3.0.0"
+ dart: ">=2.0.0 <2.2.0"
flutter: ">=0.2.5 <2.0.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 8413f5e..752eeaa 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -18,6 +18,7 @@ dependencies:
cupertino_icons: ^0.1.2
camera: 0.2.4
+ android_intent: 0.2.1
path_provider: ^0.4.1
sqflite: any
fluro: "^1.3.4"
@@ -29,6 +30,11 @@ dependencies:
image: any
video_player: "0.6.5"
flutter_swiper: any
+ audioplayers: 0.7.8
+ archive: 2.0.4
+ image_picker: 0.4.10
+ video_launcher:
+ path: './packages/flutter_video_launcher'
dev_dependencies:
flutter_test: