Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
36e13ecb50
|
|||
|
d08ce9172f
|
|||
|
2cfddc0fe9
|
|||
|
c2c919b55e
|
|||
|
c3717042a1
|
|||
|
04873f1240
|
|||
|
137f31c104
|
2
.github/workflows/linux-web.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
run: flutter build linux
|
run: flutter build linux
|
||||||
|
|
||||||
- name: Build for Android
|
- name: Build for Android
|
||||||
run: flutter build appbundle
|
run: flutter build appbundle --debug
|
||||||
|
|
||||||
- name: Build for web
|
- name: Build for web
|
||||||
run: flutter build web --no-web-resources-cdn --csp --web-renderer canvaskit
|
run: flutter build web --no-web-resources-cdn --csp --web-renderer canvaskit
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ plugins {
|
|||||||
id "dev.flutter.flutter-gradle-plugin"
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "net.ything.radio.android"
|
namespace = "net.ything.radio.android"
|
||||||
//compileSdk = flutter.compileSdkVersion
|
//compileSdk = flutter.compileSdkVersion
|
||||||
@@ -31,11 +37,21 @@ android {
|
|||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias = keystoreProperties['keyAlias']
|
||||||
|
keyPassword = keystoreProperties['keyPassword']
|
||||||
|
storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword = keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig = signingConfigs.debug
|
signingConfig = signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.1 KiB |
4
android/key.properties.dist
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
storePassword=
|
||||||
|
keyPassword=
|
||||||
|
keyAlias=
|
||||||
|
storeFile=
|
||||||
4
android/readme-signing.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
https://docs.flutter.dev/deployment/android#signing-the-app
|
||||||
|
|
||||||
|
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
|
||||||
BIN
assets/appicon/feature_radio.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/appicon/feature_radio.xcf
Normal file
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 8.1 KiB |
1
assets/appicon/radio.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-radio"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3l-9.371 3.749a1 1 0 0 0 -.629 .928v11.323a1 1 0 0 0 1 1h14a1 1 0 0 0 1 -1v-11a1 1 0 0 0 -1 -1h-14.5" /><path d="M4 12h16" /><path d="M7 12v-2" /><path d="M17 16v.01" /><path d="M13 16v.01" /></svg>
|
||||||
|
After Width: | Height: | Size: 517 B |
@@ -1,6 +1,7 @@
|
|||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: true
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
|
remove_alpha_ios: true
|
||||||
image_path: "assets/appicon/icon.png"
|
image_path: "assets/appicon/icon.png"
|
||||||
min_sdk_android: 21
|
min_sdk_android: 21
|
||||||
adaptive_icon_background: "#FFA86A"
|
adaptive_icon_background: "#FFA86A"
|
||||||
|
|||||||
@@ -40,5 +40,12 @@ end
|
|||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
installer.pods_project.targets.each do |target|
|
installer.pods_project.targets.each do |target|
|
||||||
flutter_additional_ios_build_settings(target)
|
flutter_additional_ios_build_settings(target)
|
||||||
|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||||
|
'$(inherited)',
|
||||||
|
'AUDIO_SESSION_MICROPHONE=0'
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -49,6 +49,6 @@ SPEC CHECKSUMS:
|
|||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: fe2316eacdf4337bc10b81b9cf460c6f69aee374
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 944 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -17,6 +17,36 @@ Future<void> setupListenHandler() async {
|
|||||||
_session = await AudioSession.instance;
|
_session = await AudioSession.instance;
|
||||||
|
|
||||||
await _session.configure(const AudioSessionConfiguration.music());
|
await _session.configure(const AudioSessionConfiguration.music());
|
||||||
|
|
||||||
|
//TODO: Do I need to handle these events? audioplayers may be doing it already
|
||||||
|
//_session.interruptionEventStream.listen((event) {
|
||||||
|
// if (event.begin) {
|
||||||
|
// switch (event.type) {
|
||||||
|
// case AudioInterruptionType.duck:
|
||||||
|
// _listenHandler.duck();
|
||||||
|
// break;
|
||||||
|
// case AudioInterruptionType.pause:
|
||||||
|
// case AudioInterruptionType.unknown:
|
||||||
|
// _listenHandler.pause();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// switch (event.type) {
|
||||||
|
// case AudioInterruptionType.duck:
|
||||||
|
// _listenHandler.unDuck();
|
||||||
|
// break;
|
||||||
|
// case AudioInterruptionType.pause:
|
||||||
|
// _listenHandler.play();
|
||||||
|
// break;
|
||||||
|
// case AudioInterruptionType.unknown:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
|
||||||
|
//_session.becomingNoisyEventStream.listen((_) {
|
||||||
|
// _listenHandler.pause();
|
||||||
|
//});
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenHandler getListenHandlder() => _listenHandler;
|
ListenHandler getListenHandlder() => _listenHandler;
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ class _PlayControlsState extends State<PlayControls>
|
|||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
bool isPlaying = false;
|
|
||||||
|
|
||||||
final ListenHandler _listenHandler = getListenHandlder();
|
final ListenHandler _listenHandler = getListenHandlder();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -46,15 +44,9 @@ class _PlayControlsState extends State<PlayControls>
|
|||||||
|
|
||||||
print("Listen init");
|
print("Listen init");
|
||||||
|
|
||||||
_listenHandler.playbackState.listen((PlaybackState event) {
|
_listenHandler.playbackState.listen((PlaybackState state) {
|
||||||
if (isPlaying == event.playing) {
|
print("Playback state changed");
|
||||||
print("State unchanged, skipping setState()");
|
setState(() {});
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
isPlaying = event.playing;
|
|
||||||
print("Updated playing state to $isPlaying");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,48 +57,33 @@ class _PlayControlsState extends State<PlayControls>
|
|||||||
_listenHandler.stop();
|
_listenHandler.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> playPause() async {
|
||||||
|
print("playPause called");
|
||||||
|
_listenHandler.isPlaying()
|
||||||
|
? await _listenHandler.stop()
|
||||||
|
: await _listenHandler.play();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
|
||||||
print("Running build - Listen");
|
print("Running build - Listen");
|
||||||
|
|
||||||
if (isPlaying) {
|
|
||||||
_listenHandler.play();
|
|
||||||
_listenHandler.playbackState.add(PlaybackState(
|
|
||||||
controls: [
|
|
||||||
MediaControl.stop,
|
|
||||||
MediaControl.pause,
|
|
||||||
],
|
|
||||||
systemActions: {
|
|
||||||
MediaAction.stop,
|
|
||||||
MediaAction.pause,
|
|
||||||
},
|
|
||||||
playing: true,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
_listenHandler.stop();
|
|
||||||
_listenHandler.playbackState.add(PlaybackState(
|
|
||||||
controls: [],
|
|
||||||
systemActions: {},
|
|
||||||
playing: false,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return ClipOval(
|
return ClipOval(
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Theme.of(context).colorScheme.primaryContainer,
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
splashColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
splashColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
setState(() {
|
await playPause();
|
||||||
isPlaying = !isPlaying;
|
setState(() {});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
isPlaying ? Icons.stop : Icons.play_arrow,
|
_listenHandler.isPlaying() ? Icons.stop : Icons.play_arrow,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 50,
|
size: 50,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,12 +11,59 @@ class ListenHandler extends BaseAudioHandler {
|
|||||||
|
|
||||||
setup_player() {
|
setup_player() {
|
||||||
_player.setReleaseMode(ReleaseMode.release);
|
_player.setReleaseMode(ReleaseMode.release);
|
||||||
|
_player.onPlayerStateChanged.listen((event) {
|
||||||
|
print("Received audioplayers event: $event");
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case PlayerState.playing:
|
||||||
|
super.playbackState.add(PlaybackState(
|
||||||
|
controls: [
|
||||||
|
MediaControl.stop,
|
||||||
|
MediaControl.pause,
|
||||||
|
],
|
||||||
|
systemActions: {
|
||||||
|
MediaAction.stop,
|
||||||
|
MediaAction.pause,
|
||||||
|
},
|
||||||
|
playing: true,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case PlayerState.paused:
|
||||||
|
case PlayerState.stopped:
|
||||||
|
case PlayerState.completed:
|
||||||
|
case PlayerState.disposed:
|
||||||
|
super.playbackState.add(PlaybackState(
|
||||||
|
controls: [],
|
||||||
|
systemActions: {},
|
||||||
|
playing: false,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenHandler() {
|
ListenHandler() {
|
||||||
setup_player();
|
setup_player();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPlaying() => super.playbackState.value.playing;
|
||||||
|
|
||||||
|
double _duckVol = 0;
|
||||||
|
|
||||||
|
void duck() {
|
||||||
|
//TODO: Confirm I need to do this manually, it looks like audioplayers already does this
|
||||||
|
print("Duck requested");
|
||||||
|
//_duckVol = _player.volume;
|
||||||
|
|
||||||
|
//_player.setVolume(_duckVol - 0.4 > 0 ? _duckVol - 0.4 : 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unDuck() {
|
||||||
|
//TODO: Confirm I need to do this manually, it looks like audioplayers already does this
|
||||||
|
print("Unduck requested");
|
||||||
|
//_player.setVolume(_duckVol);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> play() async {
|
Future<void> play() async {
|
||||||
if (await startAudioSession()) {
|
if (await startAudioSession()) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class YthingRadio extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Ything Radio',
|
title: 'Ything Radio',
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: Colors.orange,
|
seedColor: Colors.orange,
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 851 B |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.1
|
version: 1.0.1+a
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.1
|
sdk: ^3.5.1
|
||||||
|
|||||||
BIN
web/favicon.png
|
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 851 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.2 KiB |