Compare commits

...

17 Commits

100 changed files with 1316 additions and 930 deletions

View File

@ -1,10 +1,30 @@
# 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.
# This file should be version controlled.
version:
revision: 5464c5bac742001448fe4fc0597be939379f88ea
revision: f72efea43c3013323d1b95cff571f3c1caa37583
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
- platform: windows
create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -0,0 +1,8 @@
{
"ExpandedNodes": [
"",
"\\windows"
],
"SelectedNode": "\\windows",
"PreviewInSolutionExplorer": false
}

BIN
.vs/fipy/v17/.suo Normal file

Binary file not shown.

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

1
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -1,4 +1,4 @@
# fip_parser_ui
# fipy
Explore FIP's webradio latest tracks, play it and download what you want.

View File

@ -43,7 +43,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.fip_parser_ui"
applicationId "fipy.p2p.legal"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fip_parser_ui">
package="fipy.p2p.legal">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,9 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fip_parser_ui">
package="fipy.p2p.legal">
<application
android:label="fip_parser_ui"
android:label="fipy"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,4 +1,4 @@
package com.example.fip_parser_ui
package fipy.p2p.legal
import io.flutter.embedding.android.FlutterActivity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fip_parser_ui">
package="fipy.p2p.legal">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

BIN
assets/logo_aquamarine.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/logo_green_dark.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -294,7 +294,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.fipParserUi;
PRODUCT_BUNDLE_IDENTIFIER = fipy.p2p.legal;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -422,7 +422,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.fipParserUi;
PRODUCT_BUNDLE_IDENTIFIER = fipy.p2p.legal;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -444,7 +444,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.fipParserUi;
PRODUCT_BUNDLE_IDENTIFIER = fipy.p2p.legal;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -478,4 +478,4 @@
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
}

View File

@ -1,122 +1,122 @@
{
"images" : [
"images": [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
"filename": "Icon-App-20x20@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "20x20"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
"filename": "Icon-App-20x20@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "20x20"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
"filename": "Icon-App-29x29@1x.png",
"idiom": "iphone",
"scale": "1x",
"size": "29x29"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
"filename": "Icon-App-29x29@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "29x29"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
"filename": "Icon-App-29x29@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "29x29"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
"filename": "Icon-App-40x40@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "40x40"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
"filename": "Icon-App-40x40@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "40x40"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
"filename": "Icon-App-60x60@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "60x60"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
"filename": "Icon-App-60x60@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "60x60"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
"filename": "Icon-App-20x20@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "20x20"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
"filename": "Icon-App-20x20@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "20x20"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
"filename": "Icon-App-29x29@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "29x29"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
"filename": "Icon-App-29x29@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "29x29"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
"filename": "Icon-App-40x40@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "40x40"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
"filename": "Icon-App-40x40@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "40x40"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
"filename": "Icon-App-76x76@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "76x76"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
"filename": "Icon-App-76x76@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "76x76"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
"filename": "Icon-App-83.5x83.5@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "83.5x83.5"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
"filename": "Icon-App-1024x1024@1x.png",
"idiom": "ios-marketing",
"scale": "1x",
"size": "1024x1024"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"info": {
"author": "icons_launcher",
"version": 1
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>fip_parser_ui</string>
<string>fipy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -1,7 +1,14 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
late String token;
TextStyle globalTextStyle = TextStyle(color: Colors.grey[350]);
const proxyHeader =
kDebugMode || !kIsWeb ? 'https://' : 'http://127.0.0.1:8080/';
// Logger
final log = Logger();
// context
late BuildContext homeContext;
late BuildContext playerContext;

View File

@ -1,13 +1,12 @@
import 'package:fip_parser_ui/providers/home.dart';
import 'package:fip_parser_ui/providers/player.dart';
import 'package:fip_parser_ui/screens/home.dart';
import 'package:fipy/providers/download.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:fipy/screens/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:kplayer/kplayer.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Player.boot();
runApp(const FipyApp());
}
@ -25,7 +24,8 @@ class FipyApp extends StatelessWidget {
child: MaterialApp(
title: 'Fipy',
theme: ThemeData(
primarySwatch: Colors.blue, backgroundColor: Colors.grey[900]),
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue)
.copyWith(background: Colors.grey[900])),
home: const HomeScreen(title: 'Fipy'),
),
);

View File

@ -8,6 +8,7 @@ class Track {
String? image;
String? imageUrl;
String? id;
Duration? position;
Duration? duration;
File? file;
@ -18,6 +19,7 @@ class Track {
this.album,
this.id,
this.duration,
this.position,
this.file,
this.image,
this.imageUrl});

View File

@ -0,0 +1,61 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:fipy/globals.dart';
import 'package:fipy/models/track.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class DownloadProvider with ChangeNotifier {
Future downloadMusic(BuildContext context, Track track) async {
var yt = YoutubeExplode();
var manifest = await yt.videos.streamsClient.getManifest(track.id);
var streamManifest = StreamManifest(manifest.streams);
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
var stream = yt.videos.streamsClient.get(streamInfo);
final fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
if (!kIsWeb) {
final filePath = Platform.isAndroid
? Directory('/storage/emulated/0/Download')
: await getDownloadsDirectory();
var file = File('${filePath!.path}/$fileName.webm');
var fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// convertToMp3(file.path);
track.file = file;
ScaffoldMessenger.of(playerContext).showSnackBar(
SnackBar(
content:
SizedBox(height: 90, child: Text('Son téléchargé: ${file.path}')),
),
);
log.d(file.path);
}
}
void reload() {
notifyListeners();
}
}

View File

@ -1,22 +1,21 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io';
import 'package:fip_parser_ui/models/track.dart';
import 'package:fipy/globals.dart';
import 'package:fipy/models/track.dart';
import 'package:flutter/material.dart';
import 'package:kplayer/kplayer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:fip_parser_ui/providers/player.dart';
import 'package:fipy/providers/player.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart';
import 'package:audioplayers/audioplayers.dart';
class HomeProvider with ChangeNotifier {
Track? currentTrack;
PlayerController? player;
AudioPlayer player = AudioPlayer(playerId: 'fipyPlayerID');
List<Track> trackList = [];
int trackNbr = 0;
int userPageNbr = 3;
double currentVolume = 1;
Future<List<Track>> getTracks(String radio, {String cursor = ''}) async {
int pageNbr = 0;
@ -25,18 +24,20 @@ class HomeProvider with ChangeNotifier {
bool stop = false;
while (pageNbr < userPageNbr && !stop) {
final req = Uri.parse(
'https://www.radiofrance.fr/api/v1.7/stations/fip/webradios/$radio/songs?pageCursor=$cursor');
'${proxyHeader}www.radiofrance.fr:443/api/v1.7/stations/fip/webradios/$radio/songs?pageCursor=$cursor');
final res = await get(req, headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS"
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
'Content-Type': 'text/plain'
});
if (res.statusCode == 200) {
Map body = jsonDecode(res.body);
if (body['next'] != null) {
cursor = body['next'];
} else {
print('Page N°$pageNbr');
log.d('Page N°$pageNbr');
stop = true;
}
@ -65,115 +66,112 @@ class HomeProvider with ChangeNotifier {
return trackList;
}
Future playTrack(BuildContext context, Track track) async {
Future playTrack(Track track) async {
var yt = YoutubeExplode();
PlayerProvider playerProvider =
Provider.of<PlayerProvider>(context, listen: false);
Provider.of<PlayerProvider>(homeContext, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
Provider.of<HomeProvider>(homeContext, listen: false);
// track = trackList[track.number - 1];
currentTrack = track;
final currentVolume = player?.volume ?? 1;
player?.dispose();
if (track.id == null) {
final secondMatch = track.artiste == '' ? track.album : track.artiste;
final resultUrl = await yt.search
.search('${track.title} ${secondMatch!}', filter: TypeFilters.video);
track.id = resultUrl.first.id.value;
}
const invidiousUrl = [
'yewtu.be',
'vid.puffyan.us',
'invidious.snopyta.org',
'invidious.fdn.fr',
];
player.stop();
Future.delayed(const Duration(milliseconds: 5));
if (track.file == null) {
if (track.id == null) {
final secondMatch = track.artiste == '' ? track.album : track.artiste;
final resultUrl =
await yt.search.search(track.title + ' ' + secondMatch!);
track.id = resultUrl.first.id.value;
}
player = Player.network(
"https://invidious.fdn.fr/embed/${track.id}?raw=1&listen=1");
print(track.id);
} else {
player = Player.asset(track.file!.path);
}
player!.volume = currentVolume;
try {
player!.play();
final source = UrlSource(
"https://${invidiousUrl[3]}/latest_version?id=${track.id}&itag=140&local=true&listen=1");
log.d(source.url);
await player.play(source);
} catch (e) {
print('Play error: ' + e.toString());
log.d('Play error: $e');
}
Future.delayed(const Duration(milliseconds: 500));
player!.callback = (PlayerEvent event) {
if (event.name == 'position') {
currentTrack!.duration = player!.duration;
playerProvider.reload();
}
if (event.name == 'status') {
var nextTrack = trackList
.firstWhere((element) => element.number == track.number + 1);
playTrack(context, nextTrack);
}
};
Future.delayed(const Duration(milliseconds: 50));
player.onDurationChanged.listen((event) async {
currentTrack!.duration = await player.getDuration();
playerProvider.reload();
});
player.onPositionChanged.listen((position) async {
currentTrack!.position = position;
playerProvider.reload();
});
player.onPlayerComplete.listen((event) {
var nextTrack =
trackList.firstWhere((element) => element.number == track.number + 1);
currentTrack = nextTrack;
playTrack(nextTrack);
});
playerProvider.reload();
homeProvider.reload();
yt.close();
}
Future downloadMusic(BuildContext context, Track track) async {
var yt = YoutubeExplode();
var manifest = await yt.videos.streamsClient.getManifest(track.id);
var streamManifest = StreamManifest(manifest.streams);
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
var stream = yt.videos.streamsClient.get(streamInfo);
final filePath = await getDownloadsDirectory();
final fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
var file = File('${filePath!.path}/$fileName.webm');
var fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// Play it
track.file = file;
// playTrack(context, track);
print(file.path);
Future nextTrack() async {
if (currentTrack != null && currentTrack!.number < trackList.last.number) {
currentTrack = trackList
.firstWhere((element) => element.number == currentTrack!.number + 1);
await playTrack(currentTrack!);
}
}
Future backTrack() async {
if (currentTrack != null && currentTrack!.number > 1) {
currentTrack = trackList
.firstWhere((element) => element.number == currentTrack!.number - 1);
await playTrack(currentTrack!);
}
}
void resumePlay() {
if (currentTrack == null) {
currentTrack = trackList.first;
playTrack(currentTrack!);
} else {
player.state.name == 'playing' ? player.pause() : player.resume();
}
}
List<DropdownMenuItem<String>> get radioList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("FIP"), value: "fip"),
const DropdownMenuItem(child: Text("Electro"), value: "fip_electro"),
const DropdownMenuItem(child: Text("Groove"), value: "fip_groove"),
const DropdownMenuItem(child: Text("Rock"), value: "fip_rock"),
const DropdownMenuItem(child: Text("Jazz"), value: "fip_jazz"),
const DropdownMenuItem(child: Text("Pop"), value: "fip_pop"),
const DropdownMenuItem(child: Text("Reggae"), value: "fip_reggae"),
const DropdownMenuItem(child: Text("World"), value: "fip_world"),
const DropdownMenuItem(value: "fip", child: Text("FIP")),
const DropdownMenuItem(value: "fip_electro", child: Text("Electro")),
const DropdownMenuItem(value: "fip_groove", child: Text("Groove")),
const DropdownMenuItem(value: "fip_rock", child: Text("Rock")),
const DropdownMenuItem(value: "fip_jazz", child: Text("Jazz")),
const DropdownMenuItem(value: "fip_pop", child: Text("Pop")),
const DropdownMenuItem(value: "fip_reggae", child: Text("Reggae")),
const DropdownMenuItem(value: "fip_world", child: Text("World")),
const DropdownMenuItem(
child: Text("Nouveautés"), value: "fip_nouveautes"),
value: "fip_nouveautes", child: Text("Nouveautés")),
];
return menuItems;
}
List<DropdownMenuItem<String>> get pageList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("25"), value: "3"),
const DropdownMenuItem(child: Text("50"), value: "6"),
const DropdownMenuItem(child: Text("100"), value: "12"),
const DropdownMenuItem(child: Text("200"), value: "25"),
const DropdownMenuItem(child: Text("500"), value: "62"),
const DropdownMenuItem(value: "3", child: Text("25")),
const DropdownMenuItem(value: "6", child: Text("50")),
const DropdownMenuItem(value: "12", child: Text("100")),
const DropdownMenuItem(value: "25", child: Text("200")),
const DropdownMenuItem(value: "62", child: Text("500")),
];
return menuItems;
}
@ -182,9 +180,3 @@ class HomeProvider with ChangeNotifier {
notifyListeners();
}
}
class DownloadProvider with ChangeNotifier {
void reload() {
notifyListeners();
}
}

View File

@ -1,13 +1,15 @@
// ignore_for_file: prefer_const_literals_to_create_immutables, avoid_print
import 'dart:convert';
import 'package:fip_parser_ui/models/track.dart';
import 'package:fip_parser_ui/providers/home.dart';
import 'package:fip_parser_ui/providers/player.dart';
import 'package:fipy/globals.dart';
import 'package:fipy/models/track.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:fipy/widgets/download_track.dart';
import 'package:fipy/widgets/player/player.dart';
import 'package:flutter/material.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key, required this.title}) : super(key: key);
@ -21,65 +23,104 @@ class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
homeContext = context;
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
final MiniplayerController controller = MiniplayerController();
return Stack(
children: <Widget>[
Scaffold(
body: RawScrollbar(
thumbColor: Colors.grey[600],
radius: const Radius.circular(20),
thickness: 12,
thumbVisibility: true,
mainAxisMargin: 70,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.grey[900],
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
dropdownColor: Colors.grey[900],
value: radio,
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newRadio) {
setState(() {
radio = newRadio!;
});
},
items: hp.radioList),
const SizedBox(width: 50),
DropdownButton(
dropdownColor: Colors.grey[900],
value: hp.userPageNbr.toString(),
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newPageNumber) {
setState(() {
hp.userPageNbr = int.parse(newPageNumber!);
});
},
items: hp.pageList),
]),
),
FutureBuilder<List<Track>>(
future: hp.getTracks(radio),
builder: (
BuildContext context,
AsyncSnapshot<List<Track>> snapshot,
) {
print(snapshot.connectionState);
if (snapshot.connectionState ==
ConnectionState.waiting) {
return RawKeyboardListener(
autofocus: true,
focusNode: FocusNode(),
onKey: (RawKeyEvent event) {
// log.d(event.data.logicalKey.keyId);
if (event.runtimeType == RawKeyDownEvent) //Enter Key ID from keyboard
{
switch (event.data.logicalKey.keyId) {
case 32:
case 112:
case 4294969861:
hp.resumePlay();
break;
case 110:
case 94489280688:
hp.nextTrack();
break;
case 98:
case 94489280689:
hp.backTrack();
break;
}
}
},
child: Stack(
children: <Widget>[
Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.grey[900],
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
dropdownColor: Colors.grey[900],
value: radio,
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newRadio) {
setState(() {
radio = newRadio!;
});
},
items: hp.radioList),
const SizedBox(width: 50),
DropdownButton(
dropdownColor: Colors.grey[900],
value: hp.userPageNbr.toString(),
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newPageNumber) {
setState(() {
hp.userPageNbr = int.parse(newPageNumber!);
});
},
items: hp.pageList),
]),
),
FutureBuilder<List<Track>>(
future: hp.getTracks(radio),
builder: (
BuildContext context,
AsyncSnapshot<List<Track>> snapshot,
) {
print(snapshot.connectionState);
if (snapshot.connectionState == ConnectionState.waiting) {
return Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
color: Colors.amber[100],
),
),
]),
),
]);
} else if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
return Stack(children: [
Container(
height: 10000,
@ -88,297 +129,63 @@ class _HomeScreenState extends State<HomeScreen> {
Center(
child: Column(children: [
const SizedBox(height: 20),
SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
color: Colors.amber[100],
),
Text(
'Error: ${snapshot.error}',
style: TextStyle(color: Colors.grey[500]),
),
]),
),
]);
} else if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
return Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
Text(
'Error: ' + snapshot.error.toString(),
style: TextStyle(color: Colors.grey[500]),
),
]),
),
]);
} else if (snapshot.hasData) {
return Table(
columnWidths: {
0: const FlexColumnWidth(4),
1: const FlexColumnWidth(2),
2: const FlexColumnWidth(1),
},
defaultVerticalAlignment:
TableCellVerticalAlignment.middle,
children: snapshot.data!
.map((item) => _buildTableRow(item, context))
.toList()
..insert(
0,
_buildTableRow(
Track(
number: -1,
title: 'TITRE',
artiste: 'ARTISTE',
album: 'ALBUM',
id: 'URL'),
context),
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
const SizedBox(height: 70)
],
),
)),
),
Consumer<PlayerProvider>(builder: (context, playerProvider, _) {
TextEditingController trackTitle = TextEditingController();
TextEditingController trackArtiste = TextEditingController();
trackTitle.text = hp.currentTrack?.title ?? '';
trackArtiste.text = hp.currentTrack?.artiste ?? '';
return Miniplayer(
controller: controller,
backgroundColor: Colors.grey[900]!,
minHeight: 70,
maxHeight: 370,
builder: (height, percentage) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
hp.currentTrack?.imageUrl != '' &&
hp.currentTrack?.imageUrl != null
? Image.network(
hp.currentTrack!.imageUrl!,
width: 70,
)
: const SizedBox(width: 70),
Expanded(
child: Column(children: [
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackTitle,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 14,
color: Colors.grey[300],
),
),
),
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackArtiste,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
),
]),
),
Column(children: [
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(Icons.skip_previous,
color: Colors.grey[500], size: 32),
onPressed: () {
if (hp.currentTrack != null &&
hp.currentTrack!.number > 1) {
hp.currentTrack = hp.trackList.firstWhere(
(element) =>
element.number ==
hp.currentTrack!.number - 1);
hp.playTrack(context, hp.currentTrack!);
}
}),
]),
const SizedBox(width: 1),
ElevatedButton(
onPressed: () {
hp.player?.playing ?? false
? hp.player?.pause()
: hp.player?.play();
playerProvider.reload();
},
child: Icon(
hp.player?.playing ?? false
? Icons.pause
: Icons.play_arrow,
color: Colors.grey[900],
size: 30),
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
padding: const EdgeInsets.all(12),
primary: Colors.grey[300],
onPrimary: Colors.grey[900],
),
),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(Icons.skip_next,
color: Colors.grey[500], size: 32),
onPressed: () {
if (hp.currentTrack != null &&
hp.currentTrack!.number <
hp.trackList.last.number) {
hp.currentTrack = hp.trackList.firstWhere(
(element) =>
element.number ==
hp.currentTrack!.number + 1);
hp.playTrack(context, hp.currentTrack!);
}
}),
// const SizedBox(height: 7),
]),
]),
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Text(
timeFormat(
hp.player?.position ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
const SizedBox(width: 3),
SliderTheme(
data: const SliderThemeData(
thumbShape:
RoundSliderThumbShape(enabledThumbRadius: 2),
overlayShape:
RoundSliderThumbShape(enabledThumbRadius: 5),
trackHeight: 2,
),
child: SizedBox(
width: 300,
child: Slider(
value: double.parse(
hp.player?.position.inSeconds.toString() ??
'0'),
max: double.parse(
hp.player?.duration.inSeconds.toString() ??
'2000'),
onChanged: (double value) {
if (hp.player?.position != null) {
hp.player!.position =
Duration(seconds: value.toInt());
playerProvider.reload();
}
} else if (snapshot.hasData) {
return Table(
columnWidths: {
0: const FlexColumnWidth(4),
1: const FlexColumnWidth(2),
2: const FlexColumnWidth(1),
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
),
const SizedBox(width: 3),
Text(
timeFormat(
hp.player?.duration ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
]),
const Spacer(),
]),
const Spacer(),
SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
overlayShape:
RoundSliderThumbShape(enabledThumbRadius: 8),
trackHeight: 2.5,
),
child: SizedBox(
width: 130,
child: Slider(
value: hp.player?.volume ?? 1,
max: 1,
onChanged: (double value) {
if (hp.player?.volume != null) {
hp.player!.volume = value;
playerProvider.reload();
}
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
defaultVerticalAlignment:
TableCellVerticalAlignment.middle,
children: snapshot.data!
.map((item) => _buildTableRow(item))
.toList()
..insert(
0,
_buildTableRow(Track(
number: -1,
title: 'TITRE',
artiste: 'ARTISTE',
album: 'ALBUM',
id: 'URL')),
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
const SizedBox(width: 20)
const SizedBox(height: 70)
],
);
},
);
})
],
),
),
),
const Player()
],
),
);
}
}
Widget trackLine(Track track) {
return SizedBox(
height: 30,
child: Row(
children: [Text(track.title + ' - ' + track.artiste)],
),
);
}
TableRow _buildTableRow(Track track, BuildContext context) {
DownloadProvider downloadProvider =
Provider.of<DownloadProvider>(context, listen: false);
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
int isDownloading = -1;
TableRow _buildTableRow(Track track) {
final hp = Provider.of<HomeProvider>(homeContext, listen: false);
final textStyle = TextStyle(
fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal,
color: Colors.grey[500],
fontSize: track.number == -1 ? 15 : 14);
const rowPadding = EdgeInsets.all(10);
final yt = YoutubeExplode();
return TableRow(
decoration: const BoxDecoration(
@ -391,7 +198,7 @@ TableRow _buildTableRow(Track track, BuildContext context) {
child: InkWell(
onTap: () async {
if (track.number != -1) {
hp.playTrack(context, track);
hp.playTrack(track);
}
},
child: Row(children: [
@ -409,9 +216,12 @@ TableRow _buildTableRow(Track track, BuildContext context) {
return Text(track.title,
style: TextStyle(
fontWeight: FontWeight.normal,
color: hp.currentTrack?.title == track.title && hp.currentTrack?.artiste == track.artiste
? Colors.green
: Colors.grey[350],
color:
hp.currentTrack?.title == track.title &&
hp.currentTrack?.artiste ==
track.artiste
? Colors.green
: Colors.grey[350],
fontSize: 14));
}),
const SizedBox(height: 2),
@ -433,49 +243,9 @@ TableRow _buildTableRow(Track track, BuildContext context) {
),
TableCell(
child: Padding(
padding: rowPadding,
child: InkWell(
onTap: () async {
isDownloading = track.number;
downloadProvider.reload();
if (track.id == null) {
final secondMatch =
track.artiste == '' ? track.album : track.artiste;
final resultUrl = await yt.search
.search(track.title + ' ' + secondMatch!);
track.id = resultUrl.first.id.value;
}
if (track.id != null) {
hp.downloadMusic(context, track);
}
isDownloading = -1;
yt.close();
downloadProvider.reload();
},
child: track.number == -1
? Text('TÉLÉCHARGER', style: textStyle)
: Consumer<DownloadProvider>(builder: (context, _, __) {
return Row(children: [
const SizedBox(width: 37),
isDownloading == track.number
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.grey[500],
),
)
: Icon(Icons.download, color: Colors.grey[500]),
]);
}))),
padding: rowPadding,
child: DownloadTrack(track: track),
),
),
]);
}
String timeFormat(Duration d) {
String dd = d.toString().split('.').first;
String minutes = dd.toString().split(':')[1];
String seconds = dd.toString().split(':')[2];
return '$minutes:$seconds';
}

View File

@ -0,0 +1,61 @@
import 'package:fipy/models/track.dart';
import 'package:fipy/providers/download.dart';
import 'package:fipy/providers/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class DownloadTrack extends StatelessWidget {
const DownloadTrack({Key? key, this.track}) : super(key: key);
final Track? track;
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final dl = Provider.of<DownloadProvider>(context, listen: false);
int isDownloading = -2;
final yt = YoutubeExplode();
final track = this.track ??
hp.currentTrack ??
Track(number: -3, title: 'title', artiste: 'artiste');
return InkWell(
onTap: track.number == -3
? null
: () async {
isDownloading = track.number;
dl.reload();
if (track.id == null) {
final secondMatch =
track.artiste == '' ? track.album : track.artiste;
final resultUrl =
await yt.search.search('${track.title} ${secondMatch!}');
track.id = resultUrl.first.id.value;
}
if (track.id != null) {
// ignore: use_build_context_synchronously
await dl.downloadMusic(context, track);
}
isDownloading = -2;
yt.close();
dl.reload();
},
child: Consumer<DownloadProvider>(builder: (context, _, __) {
return Row(children: [
const SizedBox(width: 37),
isDownloading == track.number
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.grey[500],
),
)
: Icon(Icons.download, color: Colors.grey[500]),
]);
}));
}
}

View File

@ -0,0 +1,50 @@
import 'package:fipy/globals.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/widgets/download_track.dart';
import 'package:fipy/widgets/player/player_controls.dart';
import 'package:fipy/widgets/player/player_title.dart';
import 'package:fipy/widgets/player/player_volume.dart';
import 'package:flutter/material.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:provider/provider.dart';
class Player extends StatelessWidget {
const Player({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
playerContext = context;
final controller = MiniplayerController();
final hp = Provider.of<HomeProvider>(context, listen: false);
return Miniplayer(
controller: controller,
backgroundColor: Colors.grey[900]!,
minHeight: 70,
maxHeight: 70,
builder: (height, percentage) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
hp.currentTrack?.imageUrl != '' && hp.currentTrack?.imageUrl != null
? Image.network(
hp.currentTrack!.imageUrl!,
width: 70,
)
: const SizedBox(width: 70),
const PlayerTitle(),
const SizedBox(width: 90),
const PlayerControls(),
const Spacer(),
const DownloadTrack(),
const SizedBox(width: 20),
const PlayerVolume(),
const SizedBox(width: 20)
],
);
},
);
}
}

View File

@ -0,0 +1,126 @@
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerControls extends StatelessWidget {
const PlayerControls({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final playerProvider = Provider.of<PlayerProvider>(context, listen: true);
return Column(children: [
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon:
Icon(Icons.skip_previous, color: Colors.grey[500], size: 32),
onPressed: () {
hp.backTrack();
}),
]),
const SizedBox(width: 1),
ElevatedButton(
onPressed: () {
hp.resumePlay();
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.grey[900],
backgroundColor: Colors.grey[300],
shape: const CircleBorder(),
padding: const EdgeInsets.all(12),
),
child: Icon(
hp.player.state.name == 'playing'
? Icons.pause
: Icons.play_arrow,
color: Colors.grey[900],
size: 30),
),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(Icons.skip_next, color: Colors.grey[500], size: 32),
onPressed: () {
hp.nextTrack();
}),
// const SizedBox(height: 7),
]),
]),
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Text(
timeFormat(hp.currentTrack?.position ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
const SizedBox(width: 3),
(hp.currentTrack?.duration?.inSeconds ?? -1) <= 0
? Column(
children: [
const SizedBox(width: 300),
hp.currentTrack?.title == null
? Text(
'Choisissez un titre',
style: TextStyle(color: Colors.grey[500]),
)
: const SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.orange,
),
),
],
)
: SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 2),
overlayShape: RoundSliderThumbShape(enabledThumbRadius: 5),
trackHeight: 2,
),
child: SizedBox(
width: 300,
child: Slider(
value: double.parse(
hp.currentTrack?.position?.inSeconds.toString() ?? '0'),
max: double.parse(
hp.currentTrack?.duration?.inSeconds.toString() ??
'2000'),
onChanged: (double value) {
if (hp.currentTrack?.position != null) {
hp.player.seek(
Duration(seconds: value.toInt()),
);
playerProvider.reload();
}
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
),
const SizedBox(width: 3),
Text(
timeFormat(hp.currentTrack?.duration ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
]),
const Spacer(),
]);
}
}
String timeFormat(Duration d) {
String dd = d.toString().split('.').first;
String minutes = dd.toString().split(':')[1];
String seconds = dd.toString().split(':')[2];
return '$minutes:$seconds';
}

View File

@ -0,0 +1,61 @@
import 'package:fipy/providers/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerTitle extends StatelessWidget {
const PlayerTitle({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final trackTitle = TextEditingController();
final trackArtiste = TextEditingController();
trackTitle.text = hp.currentTrack?.title ?? '';
trackArtiste.text = hp.currentTrack?.artiste ?? '';
return Expanded(
child: Column(children: [
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackTitle,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 14,
color: Colors.grey[300],
),
),
),
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackArtiste,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
),
]),
);
}
}

View File

@ -0,0 +1,36 @@
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerVolume extends StatelessWidget {
const PlayerVolume({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: false);
final playerProvider = Provider.of<PlayerProvider>(context, listen: true);
return SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
overlayShape: RoundSliderThumbShape(enabledThumbRadius: 8),
trackHeight: 2.5,
),
child: SizedBox(
width: 130,
child: Slider(
value: hp.currentVolume,
max: 1,
onChanged: (double value) async {
hp.currentVolume = value;
await hp.player.setVolume(value);
playerProvider.reload();
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
);
}
}

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "fipy")
set(APPLICATION_ID "com.example.fipy")
set(APPLICATION_ID "fipy.p2p.legal")
cmake_policy(SET CMP0063 NEW)

View File

@ -6,14 +6,10 @@
#include "generated_plugin_registrant.h"
#include <dart_vlc/dart_vlc_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <audioplayers_linux/audioplayers_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin");
dart_vlc_plugin_register_with_registrar(dart_vlc_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
}

View File

@ -3,8 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
dart_vlc
url_launcher_linux
audioplayers_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -0,0 +1,14 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import audioplayers_darwin
import path_provider_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}

View File

@ -0,0 +1,11 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=C:\Users\poka\dev\flutter
FLUTTER_APPLICATION_PATH=C:\Users\poka\dev\fipy
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=0.0.2
FLUTTER_BUILD_NUMBER=2
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json

View File

@ -0,0 +1,12 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=C:\Users\poka\dev\flutter"
export "FLUTTER_APPLICATION_PATH=C:\Users\poka\dev\fipy"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=0.0.2"
export "FLUTTER_BUILD_NUMBER=2"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"

View File

@ -0,0 +1,68 @@
{
"images": [
{
"filename": "app_icon_16.png",
"idiom": "mac",
"scale": "1x",
"size": "16x16"
},
{
"filename": "app_icon_32.png",
"idiom": "mac",
"scale": "2x",
"size": "16x16"
},
{
"filename": "app_icon_32.png",
"idiom": "mac",
"scale": "1x",
"size": "32x32"
},
{
"filename": "app_icon_64.png",
"idiom": "mac",
"scale": "2x",
"size": "32x32"
},
{
"filename": "app_icon_128.png",
"idiom": "mac",
"scale": "1x",
"size": "128x128"
},
{
"filename": "app_icon_256.png",
"idiom": "mac",
"scale": "2x",
"size": "128x128"
},
{
"filename": "app_icon_256.png",
"idiom": "mac",
"scale": "1x",
"size": "256x256"
},
{
"filename": "app_icon_512.png",
"idiom": "mac",
"scale": "2x",
"size": "256x256"
},
{
"filename": "app_icon_512.png",
"idiom": "mac",
"scale": "1x",
"size": "512x512"
},
{
"filename": "app_icon_1024.png",
"idiom": "mac",
"scale": "2x",
"size": "512x512"
}
],
"info": {
"author": "icons_launcher",
"version": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -5,7 +5,7 @@
#define MyAppVersion "0.1"
#define MyAppPublisher "p2p.legal"
#define MyAppURL "https://p2p.legal"
#define MyAppExeName "fip_parser_ui.exe"
#define MyAppExeName "fipy.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
@ -35,14 +35,10 @@ Name: "french"; MessagesFile: "compiler:Languages\French.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\dart_vlc_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\flutter_windows.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\libvlc.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\libvlccore.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\poka\dev\fip_parser_ui\build\windows\runner\Release\plugins\*"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\flutter_windows.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\audioplayers_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]

View File

@ -5,147 +5,183 @@ packages:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
url: "https://pub.dev"
source: hosted
version: "3.3.0"
version: "3.3.7"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.4.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.8.2"
audio_session:
version: "2.10.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
sha256: "6063c05f987596ba7a3dad9bb9a5d8adfa5e7c07b9bae5301b27c11d0b3a239f"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
audioplayers_android:
dependency: transitive
description:
name: audio_session
url: "https://pub.dartlang.org"
name: audioplayers_android
sha256: fb6bca878ad175d8f6ddc0e0a2d4226d81fa7c10747c12db420e96c7a096b2cc
url: "https://pub.dev"
source: hosted
version: "0.1.6+1"
audio_video_progress_bar:
version: "3.0.1"
audioplayers_darwin:
dependency: transitive
description:
name: audio_video_progress_bar
url: "https://pub.dartlang.org"
name: audioplayers_darwin
sha256: c4a56c49347b2e85ac4e1efea218948ca0fba87f04d2a3d3de07ce2410037038
url: "https://pub.dev"
source: hosted
version: "0.10.0"
version: "4.0.1"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
sha256: "897e24f190232a3fbb88134b062aa83a9240f55789b5e8d17c114283284ef56b"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
sha256: "3a90a46198d375fc7d47bc1d3070c8fd8863b6469b7d87ca80f953efb090f976"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
sha256: "4f5dcbfec0bf98ea09e243d5f5b64ea43a4e6710a2f292724bed16cdba3c691e"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
sha256: "010f575653c01ccbe9756050b18df83d89426740e04b684f6438aa26c775a965"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
url: "https://pub.dev"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
url: "https://pub.dev"
source: hosted
version: "0.17.1"
version: "0.17.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
url: "https://pub.dev"
source: hosted
version: "1.0.4"
dart_vlc:
dependency: transitive
description:
name: dart_vlc
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.9"
dart_vlc_ffi:
dependency: transitive
description:
name: dart_vlc_ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5+1"
version: "1.0.5"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "2.0.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.2"
version: "6.1.4"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons_maker:
dependency: "direct dev"
description:
name: flutter_launcher_icons_maker
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.2"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -160,128 +196,106 @@ packages:
dependency: transitive
description:
name: freezed_annotation
url: "https://pub.dartlang.org"
sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "2.2.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
version: "0.15.2"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev"
source: hosted
version: "0.13.4"
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "4.0.2"
icons_launcher:
dependency: "direct dev"
description:
name: icons_launcher
sha256: f8ccfb80b56856b6eac586980bdd9c14f5ec24fb87127514055445ceb9424f4c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "4.0.15"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.4"
version: "0.6.5"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
url: "https://pub.dev"
source: hosted
version: "4.5.0"
just_audio:
dependency: transitive
description:
name: just_audio
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.20"
just_audio_platform_interface:
dependency: transitive
description:
name: just_audio_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
just_audio_web:
dependency: transitive
description:
name: just_audio_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.7"
kplayer:
dependency: "direct main"
description:
name: kplayer
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.12"
kplayer_platform_interface:
dependency: transitive
description:
name: kplayer_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.7"
kplayer_with_dart_vlc:
dependency: transitive
description:
name: kplayer_with_dart_vlc
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.8"
kplayer_with_just_audio:
dependency: transitive
description:
name: kplayer_with_just_audio
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.8"
version: "4.8.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "2.0.1"
logger:
dependency: "direct main"
description:
name: logger
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
url: "https://pub.dev"
source: hosted
version: "0.12.11"
version: "0.12.13"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.1.4"
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
url: "https://pub.dev"
source: hosted
version: "1.7.0"
version: "1.8.0"
miniplayer:
dependency: "direct main"
description:
@ -295,107 +309,114 @@ packages:
dependency: transitive
description:
name: nested
url: "https://pub.dartlang.org"
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
url: "https://pub.dev"
source: hosted
version: "1.8.1"
version: "1.8.2"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4
url: "https://pub.dev"
source: hosted
version: "2.0.9"
version: "2.0.14"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
sha256: da97262be945a72270513700a92b39dd2f4a54dad55d061687e2e37a6390366a
url: "https://pub.dev"
source: hosted
version: "2.0.13"
path_provider_ios:
version: "2.0.25"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
name: path_provider_foundation
sha256: ad4c4d011830462633f03eb34445a45345673dfd4faf1ab0b4735fbd93b19183
url: "https://pub.dev"
source: hosted
version: "2.0.8"
version: "2.2.2"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.1.10"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
version: "2.0.6"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.5"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
version: "5.1.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.3"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
provider:
dependency: "direct main"
description:
name: provider
url: "https://pub.dartlang.org"
sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
url: "https://pub.dev"
source: hosted
version: "6.0.2"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.27.3"
version: "6.0.5"
sky_engine:
dependency: transitive
description: flutter
@ -405,163 +426,130 @@ packages:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.8.2"
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
url: "https://pub.dev"
source: hosted
version: "0.4.9"
version: "0.4.16"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.3.1"
universal_io:
dependency: "direct main"
description:
name: universal_io
url: "https://pub.dartlang.org"
sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.16"
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"
version: "2.2.0"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
version: "3.0.7"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "3.1.4"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
url: "https://pub.dev"
source: hosted
version: "0.2.0+1"
version: "1.0.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
url: "https://pub.dev"
source: hosted
version: "5.3.1"
version: "6.2.2"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
youtube_explode_dart:
dependency: "direct main"
description:
name: youtube_explode_dart
url: "https://pub.dartlang.org"
sha256: "07889a6229a63e78f8d45a3b852897c2e0fa42e96c4daa38d411be211575bc38"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.4"
sdks:
dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.10.0"
dart: ">=2.18.0 <3.0.0"
flutter: ">=3.3.0"

View File

@ -1,8 +1,8 @@
name: fip_parser_ui
name: fipy
description: Advanced FIP radio track explorer
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
version: 0.0.3+2
environment:
sdk: ">=2.16.2 <3.0.0"
@ -15,9 +15,9 @@ dependencies:
# git:
# url: https://git.p2p.legal/poka/youtube_explode.git
# ref: master
url_launcher: ^6.1.0
http: ^0.13.4
kplayer: ^0.1.12
# kplayer: ^0.1.18
# kplayer_with_audioplayers: ^0.0.10
miniplayer: #^1.0.1
git:
url: https://git.p2p.legal/poka/flutter_miniplayer.git
@ -25,27 +25,48 @@ dependencies:
provider: ^6.0.1
path_provider: ^2.0.9
universal_io: ^2.0.4
# ffmpeg_cli: ^0.1.0
audioplayers: ^4.0.1
logger: ^1.3.0
# git:
# url: https://github.com/bluefireteam/audioplayers/tree/main/packages/audioplayers_linux
# ref: main
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
flutter_launcher_icons_maker: ^0.10.2
flutter_lints: ^2.0.1
icons_launcher: ^2.0.6
flutter_icons:
android: "launcher_icon"
ios: true
macos: true
windows: true
web: true
image_path: "assets/logo.jpg"
icons_launcher:
image_path: 'assets/logo_green_dark.jpg'
platforms:
android:
enable: true
ios:
enable: true
windows:
enable: true
linux:
enable: true
macos:
enable: true
# flutter_icons:
# android: true
# ios: true
# # macos: true
# windows: true
# web: true
# image_path: "assets/logo_green_dark.jpg"
flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/logo.jpg
# assets:
# - assets/test.webm
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

View File

@ -0,0 +1,8 @@
[Desktop Entry]
Name=Flutter Linux App
Comment=Flutter Linux launcher icon
Exec=app_icon
Icon=app_icon.png
Terminal=false
Type=Application
Categories=Entertainment;

BIN
snap/gui/app_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -8,7 +8,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fip_parser_ui/main.dart';
import 'package:fipy/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -23,13 +23,13 @@
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="fip_parser_ui">
<meta name="apple-mobile-web-app-title" content="fipy">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>fip_parser_ui</title>
<title>fipy</title>
<link rel="manifest" href="manifest.json">
</head>
<body>

View File

@ -1,6 +1,6 @@
{
"name": "fip_parser_ui",
"short_name": "fip_parser_ui",
"name": "fipy",
"short_name": "fipy",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",

View File

@ -1,13 +1,16 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(fip_parser_ui LANGUAGES CXX)
project(fipy LANGUAGES CXX)
set(BINARY_NAME "fip_parser_ui")
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "fipy")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
@ -20,7 +23,7 @@ else()
"Debug" "Profile" "Release")
endif()
endif()
# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
@ -30,6 +33,10 @@ set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
@ -38,12 +45,11 @@ function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding

View File

@ -1,3 +1,4 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")

View File

@ -6,12 +6,9 @@
#include "generated_plugin_registrant.h"
#include <dart_vlc/dart_vlc_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <audioplayers_windows/audioplayers_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DartVlcPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DartVlcPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
}

View File

@ -3,8 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
dart_vlc
url_launcher_windows
audioplayers_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -1,6 +1,11 @@
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
@ -10,8 +15,26 @@ add_executable(${BINARY_NAME} WIN32
"Runner.rc"
"runner.exe.manifest"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
// Version
//
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
@ -89,13 +89,13 @@ BEGIN
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "fip_parser_ui" "\0"
VALUE "CompanyName", "fipy.p2p" "\0"
VALUE "FileDescription", "fipy" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "fip_parser_ui" "\0"
VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "fip_parser_ui.exe" "\0"
VALUE "ProductName", "fip_parser_ui" "\0"
VALUE "InternalName", "fipy" "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 fipy.p2p. All rights reserved." "\0"
VALUE "OriginalFilename", "fipy.exe" "\0"
VALUE "ProductName", "fipy" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END

View File

@ -26,6 +26,11 @@ bool FlutterWindow::OnCreate() {
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
});
return true;
}

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"fip_parser_ui", origin, size)) {
if (!window.Create(L"fipy", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -7,7 +7,7 @@
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

View File

@ -48,10 +48,10 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) {
int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
if (target_length == 0) {
return std::string();
}
std::string utf8_string;
if (target_length == 0 || target_length > utf8_string.max_size()) {
return utf8_string;
}
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,

View File

@ -1,13 +1,31 @@
#include "win32_window.h"
#include <dwmapi.h>
#include <flutter_windows.h>
#include "resource.h"
namespace {
/// Window attribute that enables dark mode window decorations.
///
/// Redefined in case the developer's machine has a Windows SDK older than
/// version 10.0.22000.0.
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
/// Registry key for app theme preference.
///
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) {
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
FreeLibrary(user32_module);
}
} // namespace
@ -102,9 +120,9 @@ Win32Window::~Win32Window() {
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
bool Win32Window::Create(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
return false;
}
UpdateTheme(window);
return OnCreate();
}
bool Win32Window::Show() {
return ShowWindow(window_handle_, SW_SHOWNORMAL);
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd,
SetFocus(child_content_);
}
return 0;
case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd);
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
@ -243,3 +271,18 @@ bool Win32Window::OnCreate() {
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}
void Win32Window::UpdateTheme(HWND const window) {
DWORD light_mode;
DWORD light_mode_size = sizeof(light_mode);
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue,
RRF_RT_REG_DWORD, nullptr, &light_mode,
&light_mode_size);
if (result == ERROR_SUCCESS) {
BOOL enable_dark_mode = light_mode == 0;
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
&enable_dark_mode, sizeof(enable_dark_mode));
}
}

View File

@ -28,15 +28,16 @@ class Win32Window {
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// Creates a win32 window with |title| that is positioned and sized using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// consistent size this function will scale the inputted width and height as
// as appropriate for the default monitor. The window is invisible until
// |Show| is called. Returns true if the window was created successfully.
bool Create(const std::wstring& title, const Point& origin, const Size& size);
// Show the current window. Returns true if the window was successfully shown.
bool Show();
// Release OS resources associated with window.
void Destroy();
@ -86,6 +87,9 @@ class Win32Window {
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
// Update the window frame's theme to match the system theme.
static void UpdateTheme(HWND const window);
bool quit_on_close_ = false;
// window handle for top level window.