diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..166a998 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: c860cba910319332564e1e9d470a17074c1f2dfd + channel: stable + +project_type: app diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7b38701 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "whatskit", + "request": "launch", + "type": "dart" + }, + { + "name": "whatskit (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "whatskit (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef7e1d0 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# whatskit + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..885e260 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,68 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.netharuM.whatskit" + minSdkVersion 16 + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..a420917 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a8d8672 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/whatskit/MainActivity.kt b/android/app/src/main/kotlin/com/example/whatskit/MainActivity.kt new file mode 100644 index 0000000..45b1a76 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/whatskit/MainActivity.kt @@ -0,0 +1,5 @@ +package com.netharuM.whatskit + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() {} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..c754479 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..1949d4d Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..f5a7a83 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..bd46982 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..41037cf Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..3db14bb --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d460d1e --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..a420917 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..4256f91 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bc6a58a --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..977e71f Binary files /dev/null and b/assets/icon.png differ diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..472cdaa --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:whatskit/pages/statuses_page.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: generateMaterialColorFromColor(const Color(0xff00a884)), + scaffoldBackgroundColor: const Color(0xff111b21), + cardColor: const Color(0xff202c33), + primaryColor: const Color(0xff00a884), + textTheme: Theme.of(context).textTheme.apply(bodyColor: Colors.white), + iconTheme: Theme.of(context).iconTheme.copyWith(color: Colors.white), + ), + home: const StatusesPage(), + ); + } +} + +MaterialColor generateMaterialColorFromColor(Color color) { + return MaterialColor(color.value, { + 50: Color.fromRGBO(color.red, color.green, color.blue, 0.1), + 100: Color.fromRGBO(color.red, color.green, color.blue, 0.2), + 200: Color.fromRGBO(color.red, color.green, color.blue, 0.3), + 300: Color.fromRGBO(color.red, color.green, color.blue, 0.4), + 400: Color.fromRGBO(color.red, color.green, color.blue, 0.5), + 500: Color.fromRGBO(color.red, color.green, color.blue, 0.6), + 600: Color.fromRGBO(color.red, color.green, color.blue, 0.7), + 700: Color.fromRGBO(color.red, color.green, color.blue, 0.8), + 800: Color.fromRGBO(color.red, color.green, color.blue, 0.9), + 900: Color.fromRGBO(color.red, color.green, color.blue, 1.0), + }); +} diff --git a/lib/pages/preview_page.dart b/lib/pages/preview_page.dart new file mode 100644 index 0000000..4ac2eb0 --- /dev/null +++ b/lib/pages/preview_page.dart @@ -0,0 +1,263 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:filesystem_picker/filesystem_picker.dart'; + +class PreviewPage extends StatefulWidget { + final File file; + const PreviewPage({Key? key, required this.file}) : super(key: key); + + @override + State createState() => _PreviewPageState(); +} + +class _PreviewPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: SafeArea( + child: Builder( + builder: (context) { + if (widget.file.path.endsWith('.mp4')) { + return Column( + children: [ + StatusPlayer( + file: widget.file, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + Share.shareFiles( + [widget.file.path], + ); + }, + child: const Icon(Icons.share), + ), + TextButton( + onPressed: () async { + String? path = await FilesystemPicker.open( + title: 'Save to folder', + context: context, + rootDirectory: Directory( + '/storage/emulated/0/', + ), + fsType: FilesystemType.folder, + pickText: 'Save file to this folder', + folderIconColor: Colors.teal, + ); + if (path != null) { + await widget.file.copy( + '$path/${widget.file.path.split('/').last}'); + } + }, + child: const Icon(Icons.download), + ), + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Icon(Icons.cancel), + ), + ], + ) + ], + ); + } else if (widget.file.path.endsWith('.jpg')) { + return Stack( + children: [ + Center( + child: Image.file( + File(widget.file.path), + width: double.infinity, + ), + ), + Column( + children: [ + Expanded( + child: Container(), + ), + Container( + color: Colors.black.withOpacity(0.7), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + Share.shareFiles( + [widget.file.path], + ); + }, + child: const Icon(Icons.share), + ), + TextButton( + onPressed: () async { + String? path = await FilesystemPicker.open( + title: 'Save to folder', + context: context, + rootDirectory: Directory( + '/storage/emulated/0/', + ), + fsType: FilesystemType.folder, + pickText: 'Save file to this folder', + folderIconColor: Colors.teal, + ); + if (path != null) { + await widget.file.copy( + '$path/${widget.file.path.split('/').last}'); + } + }, + child: const Icon(Icons.download), + ), + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Icon(Icons.cancel), + ), + ], + ), + ), + ], + ) + ], + ); + } else { + return const Center(child: Text('Unknown file type')); + } + }, + ), + ), + ); + } +} + +class StatusPlayer extends StatefulWidget { + final File file; + const StatusPlayer({Key? key, required this.file}) : super(key: key); + + @override + State createState() => _StatusPlayerState(); +} + +class _StatusPlayerState extends State { + late VideoPlayerController? _controller; + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.file(File(widget.file.path)); + _controller!.initialize().then((_) { + _controller!.play(); + setState(() {}); + }); + super.initState(); + } + + @override + void dispose() { + _controller?.pause(); + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Expanded( + child: Stack( + children: [ + Center( + child: _controller != null + ? AspectRatio( + aspectRatio: _controller!.value.aspectRatio, + child: VideoPlayer(_controller!), + ) + : const CircularProgressIndicator(), + ), + Column( + children: [ + Expanded(child: Container()), + Container( + height: 50, + margin: const EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.black.withOpacity(0.5), + ), + child: Row( + children: [ + IconButton( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + color: Theme.of(context).primaryColor, + onPressed: () { + setState(() { + _controller!.value.isPlaying + ? _controller!.pause() + : _controller!.play(); + }); + }, + icon: _controller!.value.isPlaying + ? const Icon(Icons.pause) + : const Icon(Icons.play_arrow), + ), + PlayerPosIndicator(controller: _controller!), + ], + ), + ), + ], + ), + ], + ), + ); + } +} + +class PlayerPosIndicator extends StatefulWidget { + final VideoPlayerController controller; + const PlayerPosIndicator({Key? key, required this.controller}) + : super(key: key); + + @override + State createState() => _PlayerPosIndicatorState(); +} + +class _PlayerPosIndicatorState extends State { + double position = 0; + + void _update() { + setState(() { + position = widget.controller.value.position.inMilliseconds.toDouble(); + }); + } + + @override + void initState() { + position = widget.controller.value.position.inMilliseconds.toDouble(); + widget.controller.addListener(_update); + super.initState(); + } + + @override + void dispose() { + widget.controller.removeListener(_update); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Expanded( + child: Slider( + value: position, + min: 0, + max: widget.controller.value.duration.inMilliseconds.toDouble(), + onChanged: (double value) { + widget.controller.seekTo(Duration(milliseconds: value.toInt())); + }, + ), + ); + } +} diff --git a/lib/pages/statuses_page.dart b/lib/pages/statuses_page.dart new file mode 100644 index 0000000..4dd6a8d --- /dev/null +++ b/lib/pages/statuses_page.dart @@ -0,0 +1,99 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:whatskit/widgets/status_card.dart'; + +class StatusesPage extends StatefulWidget { + const StatusesPage({Key? key}) : super(key: key); + + @override + State createState() => _StatusesPageState(); +} + +class _StatusesPageState extends State { + List _statusFiles = []; + + Future> getFileList() async { + final bool permissionStatus = + await Permission.manageExternalStorage.isGranted; + if (!permissionStatus) { + await Permission.manageExternalStorage.request(); + } + Directory dir = Directory( + '/storage/emulated/0/Android/media/com.whatsapp/Whatsapp/Media/.Statuses/'); + List fileList = await dir.list(recursive: false).toList(); + return fileList.where((element) { + return element.path.endsWith('.mp4') || element.path.endsWith('.jpg'); + }).toList(); + } + + @override + void initState() { + _init(); + super.initState(); + } + + Future _init() async { + List statusFiles = await getFileList(); + setState(() { + _statusFiles = statusFiles; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('whatsapp status'), + ), + body: RefreshIndicator( + onRefresh: _init, + child: GridView.count( + crossAxisCount: 2, + children: [ + for (int i = 0; i < _statusFiles.length; i++) + Padding( + padding: const EdgeInsets.all(8.0), + child: WhatsappStatusCard( + file: _statusFiles[i], + ), + ), + ], + ), + ), + drawer: Drawer( + backgroundColor: Colors.transparent, + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(12), + bottomRight: Radius.circular(12)), + color: Theme.of(context).scaffoldBackgroundColor, + ), + child: ListView( + children: [ + DrawerHeader( + child: Column( + children: [ + Image.asset( + 'assets/icon.png', + width: 100, + ), + Text( + 'whatskit', + style: Theme.of(context).textTheme.headline6, + ), + ], + ), + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: BorderRadius.circular(12), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/status_card.dart b/lib/widgets/status_card.dart new file mode 100644 index 0000000..b0ea7e3 --- /dev/null +++ b/lib/widgets/status_card.dart @@ -0,0 +1,115 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:video_thumbnail/video_thumbnail.dart'; +import 'package:flutter/material.dart'; +import 'package:whatskit/pages/preview_page.dart'; + +class WhatsappStatusCard extends StatelessWidget { + final FileSystemEntity file; + const WhatsappStatusCard({Key? key, required this.file}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Theme.of(context).cardColor, + ), + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12), + child: PreviewFile( + file: File(file.path), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: () async { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => PreviewPage( + file: File(file.path), + ), + ), + ); + }, + borderRadius: BorderRadius.circular(12), + ), + ), + ], + ), + ); + } +} + +class PreviewFile extends StatelessWidget { + final File file; + const PreviewFile({Key? key, required this.file}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (file.path.endsWith('.jpg')) { + return Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.file( + File(file.path), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + ); + } else if (file.path.endsWith('.mp4')) { + return FutureBuilder( + future: VideoThumbnail.thumbnailData( + video: file.path, + imageFormat: ImageFormat.JPEG, + quality: 50, + ), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Stack( + children: [ + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.memory( + snapshot.data!, + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + ), + Center( + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: BorderRadius.circular(50), + ), + child: const Icon( + Icons.play_arrow, + color: Colors.white, + ), + ), + ), + ], + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } else { + return Text( + file.path, + style: const TextStyle(color: Colors.white), + ); + } + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..df11810 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,460 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + filesystem_picker: + dependency: "direct main" + description: + name: filesystem_picker + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.2" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "9.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2+1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + share_plus: + dependency: "direct main" + description: + name: share_plus + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.4" + share_plus_linux: + dependency: transitive + description: + name: share_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_macos: + dependency: transitive + description: + name: share_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + share_plus_web: + dependency: transitive + description: + name: share_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + share_plus_windows: + dependency: transitive + description: + name: share_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.8" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.20" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + video_player: + dependency: "direct main" + description: + name: video_player + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + video_player_android: + dependency: transitive + description: + name: video_player_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.2" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + video_player_web: + dependency: transitive + description: + name: video_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + video_thumbnail: + dependency: "direct main" + description: + name: video_thumbnail + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" +sdks: + dart: ">=2.16.2 <3.0.0" + flutter: ">=2.10.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..7d485e8 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,98 @@ +name: whatskit +description: toolkit for whatsapp. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.16.2 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + permission_handler: ^9.2.0 + video_thumbnail: ^0.5.0 + video_player: ^2.3.0 + share_plus: ^4.0.4 + filesystem_picker: ^2.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + flutter_launcher_icons: "^0.9.2" + +flutter_icons: + image_path: "assets/icon.png" + android: true + ios: false + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/icon.png + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages