Skip to content

Commit

Permalink
feat: bring back the audio type playing indicator by @HrX03 (#982)
Browse files Browse the repository at this point in the history
Co-authored-by: Davide Bianco <[email protected]>
  • Loading branch information
Feichtmeier and HrX03 authored Oct 26, 2024
1 parent 89ed1ff commit c29c80d
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 46 deletions.
123 changes: 123 additions & 0 deletions lib/common/view/audio_type_is_playing_indicator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import 'theme.dart';

// Code by @HrX03
class AudioTypeIsPlayingIndicator extends StatefulWidget {
final Color? color;
final double thickness;

const AudioTypeIsPlayingIndicator({
this.color,
this.thickness = 2.0,
super.key,
});

@override
State<AudioTypeIsPlayingIndicator> createState() =>
_AudioTypeIsPlayingIndicatorState();
}

class _AudioTypeIsPlayingIndicatorState
extends State<AudioTypeIsPlayingIndicator> with TickerProviderStateMixin {
static const _delayInMills = [770, 290, 280, 740];
static const _durationInMills = [1260, 430, 1010, 730];
static final TweenSequence<double> _seq = TweenSequence([
TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.5), weight: 1),
TweenSequenceItem(tween: Tween(begin: 0.5, end: 1.0), weight: 1),
]);

final List<AnimationController> _controllers = [];

@override
void initState() {
super.initState();
for (int i = 0; i < 4; i++) {
_controllers.add(
AnimationController(
value: _delayInMills[i] / _durationInMills[i],
vsync: this,
duration: Duration(milliseconds: _durationInMills[i]),
),
);
_controllers[i].repeat();
}
}

@override
void dispose() {
for (final AnimationController e in _controllers) {
e.dispose();
}

super.dispose();
}

@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Padding(
padding: const EdgeInsets.only(left: 3, right: 1),
child: SizedBox(
width: iconSize - 2,
height: iconSize,
child: AnimatedBuilder(
animation: Listenable.merge(_controllers),
builder: (context, _) {
return CustomPaint(
painter: _MusicIndicatorPainter(
animations: _controllers
.map(_seq.animate)
.map((e) => e.value)
.toList(),
color: widget.color ?? Theme.of(context).colorScheme.primary,
thickness: widget.thickness,
),
);
},
),
),
),
);
}
}

class _MusicIndicatorPainter extends CustomPainter {
final List<double> animations;
final Color? color;
final double thickness;

const _MusicIndicatorPainter({
required this.animations,
this.color,
this.thickness = 2.0,
});

@override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < animations.length; i++) {
final double dx = (size.width - thickness * animations.length) /
(animations.length - 1) *
i +
thickness / 2;

canvas.drawLine(
Offset(dx, 0.5 * animations[i] * size.height),
Offset(dx, size.height - 0.5 * animations[i] * size.height),
Paint()
..color = color ?? Colors.black
..style = PaintingStyle.stroke
..strokeWidth = thickness
..strokeCap = StrokeCap.round,
);
}
}

@override
bool shouldRepaint(covariant _MusicIndicatorPainter old) {
return !listEquals(animations, old.animations) ||
color != old.color ||
thickness != old.thickness;
}
}
1 change: 1 addition & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ const kFavLanguageCodes = 'favLanguageCodes';
const kAscendingFeeds = 'ascendingfeed:::';
const kPatchNotesDisposed = 'kPatchNotesDisposed';
const kCloseBtnAction = 'closeBtnAction';
const kUseMoreAnimations = 'useMoreAnimations';

const shops = <String, String>{
'https://us.7digital.com/': '7digital',
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@
"contributors": "Mitwirkende",
"version": "Version",
"theme": "Thema",
"useMoreAnimationsTitle": "Nutze mehr Animationen",
"useMoreAnimationsDescription": "Dies wird die CPU-Nutzung erhöhen, was auf älteren System unerwünscht sein könnte.",
"license": "Lizenz",
"dependencies": "Abhängigkeiten",
"light": "Hell",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@
"contributors": "Contributors",
"version": "Version",
"theme": "Theme",
"useMoreAnimationsTitle": "Use more animations",
"useMoreAnimationsDescription": "This will slightly increase the CPU usage, which might be undesired on older hardware.",
"license": "License",
"dependencies": "Dependencies",
"light": "Light",
Expand Down
20 changes: 14 additions & 6 deletions lib/local_audio/view/local_audio_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:yaru/yaru.dart';

import '../../common/data/audio.dart';
import '../../common/view/adaptive_container.dart';
import '../../common/view/audio_type_is_playing_indicator.dart';
import '../../common/view/common_widgets.dart';
import '../../common/view/header_bar.dart';
import '../../common/view/icons.dart';
Expand All @@ -18,6 +19,7 @@ import '../../library/library_model.dart';
import '../../player/player_model.dart';
import '../../search/search_model.dart';
import '../../search/search_type.dart';
import '../../settings/settings_model.dart';
import '../../settings/view/settings_dialog.dart';
import '../local_audio_model.dart';
import 'failed_imports_content.dart';
Expand Down Expand Up @@ -144,13 +146,19 @@ class LocalAudioPageIcon extends StatelessWidget with WatchItMixin {
@override
Widget build(BuildContext context) {
final audioType = watchPropertyValue((PlayerModel m) => m.audio?.audioType);
final isPlaying = watchPropertyValue((PlayerModel m) => m.isPlaying);
final useMoreAnimations =
watchPropertyValue((SettingsModel m) => m.useMoreAnimations);

final theme = context.theme;
if (audioType == AudioType.local) {
return Icon(
Iconz.playFilled,
color: theme.colorScheme.primary,
);
if (useMoreAnimations && audioType == AudioType.local) {
if (isPlaying) {
return const AudioTypeIsPlayingIndicator(thickness: 1);
} else {
return Icon(
Iconz.playFilled,
color: context.colorScheme.primary,
);
}
}

return Padding(
Expand Down
10 changes: 9 additions & 1 deletion lib/podcasts/view/podcasts_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:watch_it/watch_it.dart';
import 'package:yaru/theme.dart';

import '../../common/data/audio.dart';
import '../../common/view/audio_type_is_playing_indicator.dart';
import '../../common/view/header_bar.dart';
import '../../common/view/icons.dart';
import '../../common/view/progress.dart';
Expand All @@ -15,6 +16,7 @@ import '../../library/library_model.dart';
import '../../player/player_model.dart';
import '../../search/search_model.dart';
import '../../search/search_type.dart';
import '../../settings/settings_model.dart';
import '../podcast_model.dart';
import 'podcasts_collection_body.dart';

Expand Down Expand Up @@ -79,9 +81,15 @@ class PodcastsPageIcon extends StatelessWidget with WatchItMixin {
Widget build(BuildContext context) {
final theme = context.theme;
final audioType = watchPropertyValue((PlayerModel m) => m.audio?.audioType);

final checkingForUpdates =
watchPropertyValue((PodcastModel m) => m.checkingForUpdates);
final isPlaying = watchPropertyValue((PlayerModel m) => m.isPlaying);
final useMoreAnimations =
watchPropertyValue((SettingsModel m) => m.useMoreAnimations);

if (useMoreAnimations && audioType == AudioType.podcast && isPlaying) {
return const AudioTypeIsPlayingIndicator(thickness: 1);
}

if (checkingForUpdates) {
return const SideBarProgress();
Expand Down
25 changes: 16 additions & 9 deletions lib/radio/view/radio_page_icon.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';

import '../../common/data/audio.dart';
import '../../common/view/audio_type_is_playing_indicator.dart';
import '../../common/view/icons.dart';
import '../../extensions/build_context_x.dart';

import '../../constants.dart';
import '../../common/data/audio.dart';
import '../../extensions/build_context_x.dart';
import '../../player/player_model.dart';
import '../../settings/settings_model.dart';

class RadioPageIcon extends StatelessWidget with WatchItMixin {
const RadioPageIcon({
Expand All @@ -19,13 +20,19 @@ class RadioPageIcon extends StatelessWidget with WatchItMixin {
@override
Widget build(BuildContext context) {
final audioType = watchPropertyValue((PlayerModel m) => m.audio?.audioType);
final isPlaying = watchPropertyValue((PlayerModel m) => m.isPlaying);
final useMoreAnimations =
watchPropertyValue((SettingsModel m) => m.useMoreAnimations);

final theme = context.theme;
if (audioType == AudioType.radio) {
return Icon(
Iconz.playFilled,
color: theme.colorScheme.primary,
);
if (useMoreAnimations && audioType == AudioType.radio) {
if (isPlaying) {
return const AudioTypeIsPlayingIndicator(thickness: 1);
} else {
return Icon(
Iconz.playFilled,
color: context.colorScheme.primary,
);
}
}

return Padding(
Expand Down
3 changes: 3 additions & 0 deletions lib/settings/settings_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class SettingsModel extends SafeChangeNotifier {
bool get enableDiscordRPC => _service.enableDiscordRPC;
void setEnableDiscordRPC(bool value) => _service.setEnableDiscordRPC(value);

bool get useMoreAnimations => _service.useMoreAnimations;
void setUseMoreAnimations(bool value) => _service.setUseMoreAnimations(value);

bool get usePodcastIndex => _service.usePodcastIndex;
Future<void> setUsePodcastIndex(bool value) async =>
_service.setUsePodcastIndex(value);
Expand Down
14 changes: 12 additions & 2 deletions lib/settings/settings_service.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';

import 'package:shared_preferences/shared_preferences.dart';

Expand Down Expand Up @@ -28,7 +29,6 @@ class SettingsService {

bool get neverShowFailedImports =>
_preferences.getBool(kNeverShowImportFails) ?? false;

void setNeverShowFailedImports(bool value) {
_preferences.setBool(kNeverShowImportFails, value).then(
(saved) {
Expand All @@ -38,7 +38,6 @@ class SettingsService {
}

bool get enableDiscordRPC => _preferences.getBool(kEnableDiscordRPC) ?? false;

void setEnableDiscordRPC(bool value) {
_preferences.setBool(kEnableDiscordRPC, value).then(
(saved) {
Expand All @@ -47,6 +46,17 @@ class SettingsService {
);
}

// TODO: check how this increases cpu usage
bool get useMoreAnimations =>
_preferences.getBool(kUseMoreAnimations) ?? !Platform.isLinux;
void setUseMoreAnimations(bool value) {
_preferences.setBool(kUseMoreAnimations, value).then(
(saved) {
if (saved) _propertiesChangedController.add(true);
},
);
}

bool recentPatchNotesDisposed(String version) =>
_preferences.getString(kPatchNotesDisposed) == version;

Expand Down
Loading

0 comments on commit c29c80d

Please sign in to comment.