Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
955b691ed6
|
|||
|
fb03d06b0a
|
|||
|
d2173319b2
|
|||
|
f232324f11
|
|||
|
426c6b7567
|
|||
|
7e6aaf40c4
|
|||
|
bc30c9586e
|
|||
|
36e13ecb50
|
|||
|
d08ce9172f
|
|||
|
2cfddc0fe9
|
|||
|
c2c919b55e
|
2
.github/workflows/linux-web.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
run: flutter build linux
|
||||
|
||||
- name: Build for Android
|
||||
run: flutter build appbundle
|
||||
run: flutter build appbundle --debug
|
||||
|
||||
- name: Build for web
|
||||
run: flutter build web --no-web-resources-cdn --csp --web-renderer canvaskit
|
||||
|
||||
@@ -11,8 +11,8 @@ or to facilitate user input for internet radio sites.
|
||||
|
||||
This app has been designed to run across all platforms!
|
||||
|
||||
- Android
|
||||
- iOS
|
||||
- Android ([Google Play Store](https://play.google.com/store/apps/details?id=net.ything.radio.android))
|
||||
- iOS (Requires [TestFlight](https://itunes.apple.com/us/app/testflight/id899247664?mt=8), contact info@ything.app for invitation)
|
||||
- MacOS
|
||||
- Windows
|
||||
- Linux
|
||||
@@ -25,6 +25,5 @@ Want to have us modify this app for you? Reach out to info@ything.app
|
||||
Coming Soon:
|
||||
|
||||
- Apple App Store Live Demo!
|
||||
- Google Play Store Live Demo!
|
||||
- Windows portable exe
|
||||
- Screenshots / Demo Videos
|
||||
|
||||
@@ -5,6 +5,12 @@ plugins {
|
||||
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 {
|
||||
namespace = "net.ything.radio.android"
|
||||
//compileSdk = flutter.compileSdkVersion
|
||||
@@ -25,17 +31,27 @@ android {
|
||||
applicationId = "net.ything.radio.android"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = flutter.minSdkVersion
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 2
|
||||
versionName = "1.0.2"
|
||||
}
|
||||
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias = keystoreProperties['keyAlias']
|
||||
keyPassword = keystoreProperties['keyPassword']
|
||||
storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword = keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.debug
|
||||
signingConfig = signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
@@ -1,6 +1,7 @@
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
ios: true
|
||||
remove_alpha_ios: true
|
||||
image_path: "assets/appicon/icon.png"
|
||||
min_sdk_android: 21
|
||||
adaptive_icon_background: "#FFA86A"
|
||||
|
||||
@@ -40,5 +40,12 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |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
|
||||
|
||||
@@ -49,6 +49,6 @@ SPEC CHECKSUMS:
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
||||
PODFILE CHECKSUM: fe2316eacdf4337bc10b81b9cf460c6f69aee374
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
|
||||
|
Before Width: | Height: | Size: 23 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.5 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 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: 2.0 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.8 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: 3.1 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ything_radio/SafeZone.dart';
|
||||
|
||||
import 'Globals.dart';
|
||||
|
||||
class About extends StatelessWidget {
|
||||
const About({super.key});
|
||||
|
||||
@@ -17,31 +19,9 @@ class About extends StatelessWidget {
|
||||
child: SafeZone(
|
||||
child: DefaultTextStyle(
|
||||
style: Theme.of(context).textTheme.bodyLarge!,
|
||||
child: const SingleChildScrollView(
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
'Ything Radio is an app that is intended to provide a demonstration of the '
|
||||
'open source ything_radio app. This app is used to provide the ability for the '
|
||||
'"look and feel" of the app to be tested on real devices for those wishing to '
|
||||
'either use the code as a template for themselves; or, alternatively, have Ything LLC '
|
||||
'create a custom app for them!'
|
||||
'\n\n'
|
||||
'This app intentionally does not provide a method to change the streaming source '
|
||||
'as it is intended for use by internet radio stations to provide a dedicated app '
|
||||
'for their radio station. This is not intended to be an app for generic usage for '
|
||||
'multiple radio stations, a discovery platform, or to facilitate user input for '
|
||||
'internet radio sites.'
|
||||
'\n\n'
|
||||
'This app has been designed to run across all platforms!'
|
||||
'\n\n'
|
||||
'Android, iOS, MacOS, Windows, Linux, and Web!'
|
||||
'\n\n'
|
||||
'Have a single unified experience across all platforms for your users!'
|
||||
'\n\n'
|
||||
'Want to have us modify this app for you? Reach out at info@ything.net '
|
||||
'We can add additional features for you! Schedule views, up next listings, '
|
||||
'and even custom user interactivity! '
|
||||
'\n\n'
|
||||
'We look forward to hearing from you soon! We would love to build your app!',
|
||||
getAboutText(),
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,11 +1,55 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'ListenHandler.dart';
|
||||
|
||||
late final ListenHandler _listenHandler;
|
||||
late final AudioSession _session;
|
||||
|
||||
const String _fallback =
|
||||
"https://generic.ything.app/music/separation-185196.mp3";
|
||||
final _urlSource = Uri.parse("https://generic.ything.app/music/player.url");
|
||||
late final String _radioUrl;
|
||||
|
||||
Future<void> loadCurrentUrl() async {
|
||||
final resp = await http.get(_urlSource);
|
||||
if (resp.statusCode == 200) {
|
||||
_radioUrl = resp.body.trim();
|
||||
print('Loaded remote url');
|
||||
} else {
|
||||
_radioUrl = _fallback;
|
||||
print('Request for current streaming url failed, using fallback url');
|
||||
}
|
||||
}
|
||||
|
||||
const String _fallbackAbout =
|
||||
"Ything Radio is an internet radio streaming application.";
|
||||
final _urlAbout =
|
||||
Uri.parse("https://generic.ything.app/ything_radio/about.txt");
|
||||
|
||||
final _urlAboutIOS =
|
||||
Uri.parse("https://generic.ything.app/ything_radio/about_ios.txt");
|
||||
|
||||
late final String _remoteAbout;
|
||||
|
||||
Future<void> loadAboutUrl() async {
|
||||
final resp = await http.get(Platform.isIOS ? _urlAboutIOS : _urlAbout);
|
||||
|
||||
if (resp.statusCode == 200) {
|
||||
_remoteAbout = resp.body;
|
||||
print('Loaded remote about');
|
||||
} else {
|
||||
_remoteAbout = _fallbackAbout;
|
||||
print('Request for remote about failed, using fallback about');
|
||||
}
|
||||
}
|
||||
|
||||
String getAboutText() => _remoteAbout;
|
||||
|
||||
Future<void> setupListenHandler() async {
|
||||
_listenHandler = await AudioService.init(
|
||||
builder: () => ListenHandler(),
|
||||
@@ -51,6 +95,10 @@ Future<void> setupListenHandler() async {
|
||||
|
||||
ListenHandler getListenHandlder() => _listenHandler;
|
||||
|
||||
UrlSource getUrlSource() {
|
||||
return UrlSource(_radioUrl, mimeType: "audio/mpeg");
|
||||
}
|
||||
|
||||
Future<bool> startAudioSession() async {
|
||||
return await _session.setActive(true);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ class _PlayControlsState extends State<PlayControls>
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
final ListenHandler _listenHandler = getListenHandlder();
|
||||
|
||||
@override
|
||||
@@ -46,7 +48,11 @@ class _PlayControlsState extends State<PlayControls>
|
||||
|
||||
_listenHandler.playbackState.listen((PlaybackState state) {
|
||||
print("Playback state changed");
|
||||
setState(() {});
|
||||
setState(() {
|
||||
//if state changed, we're no longer loading!
|
||||
//doesn't matter if we're playing or not!
|
||||
_isLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,14 +82,26 @@ class _PlayControlsState extends State<PlayControls>
|
||||
child: InkWell(
|
||||
splashColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
onTap: () async {
|
||||
if (_isLoading) {
|
||||
//skip doing anything else if we're still loading...
|
||||
return;
|
||||
}
|
||||
await playPause();
|
||||
setState(() {});
|
||||
setState(() {
|
||||
if (!_listenHandler.isPlaying()) {
|
||||
_isLoading = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Icon(
|
||||
_listenHandler.isPlaying() ? Icons.stop : Icons.play_arrow,
|
||||
_isLoading
|
||||
? Icons.hourglass_empty
|
||||
: _listenHandler.isPlaying()
|
||||
? Icons.stop
|
||||
: Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
size: 50,
|
||||
),
|
||||
|
||||
@@ -5,9 +5,7 @@ import 'package:ything_radio/Globals.dart';
|
||||
class ListenHandler extends BaseAudioHandler {
|
||||
final _player = AudioPlayer();
|
||||
|
||||
final UrlSource _radioSource = UrlSource(
|
||||
"https://generic.ything.app/music/separation-185196.mp3",
|
||||
mimeType: "audio/mpeg");
|
||||
final UrlSource _radioSource = getUrlSource();
|
||||
|
||||
setup_player() {
|
||||
_player.setReleaseMode(ReleaseMode.release);
|
||||
@@ -67,6 +65,8 @@ class ListenHandler extends BaseAudioHandler {
|
||||
@override
|
||||
Future<void> play() async {
|
||||
if (await startAudioSession()) {
|
||||
//setSource followed by resume does not work on Android or web
|
||||
//play works everywhere though
|
||||
_player.play(_radioSource, mode: PlayerMode.mediaPlayer);
|
||||
}
|
||||
}
|
||||
@@ -79,4 +79,11 @@ class ListenHandler extends BaseAudioHandler {
|
||||
|
||||
@override
|
||||
Future<void> stop() => pause();
|
||||
|
||||
//make appo stop playing when swiped away in task manager on Android
|
||||
@override
|
||||
Future<void> onTaskRemoved() async {
|
||||
await stop();
|
||||
return super.onTaskRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class YthingRadio extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Ything Radio',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.orange,
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'Globals.dart';
|
||||
import 'YthingRadio.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await loadCurrentUrl();
|
||||
await loadAboutUrl();
|
||||
await setupListenHandler();
|
||||
runApp(const YthingRadio());
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ packages:
|
||||
source: hosted
|
||||
version: "7.7.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
|
||||
@@ -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
|
||||
# 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.
|
||||
version: 1.0.1
|
||||
version: 1.0.2
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.1
|
||||
@@ -40,6 +40,7 @@ dependencies:
|
||||
flutter_tabler_icons: ^1.38.0
|
||||
audio_service: ^0.18.15
|
||||
audio_session: ^0.1.21
|
||||
http: ^1.2.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||