Merge pull request #49 from Abinanthankv/main

Subtitle customization for android.
This commit is contained in:
Horizon 2025-01-10 06:35:38 +05:30 committed by GitHub
commit 6eeaf5b682
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 207 additions and 6 deletions

View file

@ -423,11 +423,33 @@ class _VideoViewerState extends State<VideoViewer> {
});
},
);
String subtitleStyleName = config.subtitleStyle ?? 'Normal';
String subtitleStyleColor = config.subtitleColor ?? 'white';
double subtitleSize = config.subtitleSize ;
Color hexToColor(String hexColor) {
final hexCode = hexColor.replaceAll('#', '');
return Color(int.parse('0x$hexCode'));
}
FontStyle getFontStyleFromString(String styleName) {
switch (styleName.toLowerCase()) {
case 'italic':
return FontStyle.italic;
case 'normal': // Explicitly handle 'normal' (good practice)
default: // Default case for any other string or null
return FontStyle.normal;
}
}
FontStyle currentFontStyle = getFontStyleFromString(subtitleStyleName);
return MaterialVideoControlsTheme(
fullscreen: mobile,
normal: mobile,
child: Video(
subtitleViewConfiguration: SubtitleViewConfiguration(
style: TextStyle(color: hexToColor(subtitleStyleColor),
fontSize: subtitleSize,
fontStyle: currentFontStyle,
fontWeight: FontWeight.bold),
),
fit: isScaled ? BoxFit.fitWidth : BoxFit.fitHeight,
pauseUponEnteringBackgroundMode: true,
key: key,
@ -598,4 +620,4 @@ int calculateSecondsFromProgress(
final clampedProgress = progressPercentage.clamp(0.0, 100.0);
final currentSeconds = (duration * (clampedProgress / 100)).round();
return currentSeconds;
}
}

View file

@ -6,6 +6,8 @@ import 'package:pocketbase/pocketbase.dart';
import '../../../engine/engine.dart';
import '../../../utils/load_language.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/services.dart';
class PlaybackSettingsScreen extends StatefulWidget {
const PlaybackSettingsScreen({super.key});
@ -21,13 +23,69 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
// Playback settings
bool _autoPlay = true;
double _playbackSpeed = 1.0;
double _subtitleSize = 10.0;
String _defaultAudioTrack = 'eng';
String _defaultSubtitleTrack = 'eng';
bool _enableExternalPlayer = true;
String? _defaultPlayerId;
bool _disabledSubtitle = false;
Map<String, String> _availableLanguages = {};
final List<String> _subtitleStyle = [
'Normal',
'Italic',
];
String colorToHex(Color color) {
return '#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}';
}
Color hexToColor(String hexColor) {
final hexCode = hexColor.replaceAll('#', '');
return Color(int.parse('0x$hexCode'));
}
Color _selectedSubtitleColor = Colors.yellow;
_showColorPickerDialog(BuildContext context) async {
Color? color = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Pick a Subtitle Color'),
content: SingleChildScrollView(
child: ColorPicker(
color: _selectedSubtitleColor,
onColorChanged: (Color color) {
_selectedSubtitleColor = color;
},
// Remove pickerType
enableShadesSelection: true,
),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.of(context)
.pop(_selectedSubtitleColor); // Return the color
},
child: const Text('OK'),
),
],
);
},
);
if (color != null) {
setState(() {
_selectedSubtitleColor = color;
});
_debouncedSave(); // Debounced save after color change
}
}
List<DropdownMenuItem<String>> get dropdown =>
_availableLanguages.entries.map((item) {
@ -64,6 +122,12 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
? playbackConfig.externalPlayerId![currentPlatform]
: null;
_disabledSubtitle = playbackConfig.disableSubtitle;
final subtitleStyle = playbackConfig.subtitleStyle; // Get saved style
_subtitleStyle.removeWhere((style) =>
style == subtitleStyle); // Remove saved style from dropdown options
_subtitleStyle.insert(0, subtitleStyle ?? "Normal");
_selectedSubtitleColor = hexToColor(playbackConfig.subtitleColor!);
_subtitleSize = playbackConfig.subtitleSize.toDouble();
}
@override
@ -103,6 +167,9 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
'externalPlayer': _enableExternalPlayer,
'externalPlayerId': extranalId,
'disableSubtitle': _disabledSubtitle,
'subtitleStyle': _subtitleStyle[0],
'subtitleColor': colorToHex(_selectedSubtitleColor),
'subtitleSize': _subtitleSize,
},
};
@ -134,6 +201,12 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
@override
Widget build(BuildContext context) {
final dropdownstyle = _subtitleStyle.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList();
if (_error != null) {
return Scaffold(
body: Center(
@ -178,6 +251,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
divisions: 18,
label: '${_playbackSpeed.toStringAsFixed(2)}x',
onChanged: (value) {
HapticFeedback.mediumImpact();
setState(() => _playbackSpeed =
double.parse(value.toStringAsFixed(2)));
_debouncedSave();
@ -209,7 +283,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
_debouncedSave();
},
),
if (!_disabledSubtitle)
if (!_disabledSubtitle) ...[
ListTile(
title: const Text('Default Subtitle Track'),
trailing: DropdownButton<String>(
@ -223,6 +297,87 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
},
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Material( // Center the text
child: ConstrainedBox( // Prevent overflow
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8, // 80% of screen width
),
child: Text(
'Sample Text',
textAlign: TextAlign.center, // Center text within its box
style: TextStyle(
fontSize: _subtitleSize / 2,
color: _selectedSubtitleColor,
fontStyle: _subtitleStyle[0].toLowerCase() == 'italic'
? FontStyle.italic
: FontStyle.normal,
),
),
),
),
),
ListTile(
title: const Text('Subtitle Style'),
trailing: DropdownButton<String>(
value: _subtitleStyle[0],
items: dropdownstyle,
onChanged: (value) {
HapticFeedback.mediumImpact();
if (value != null) {
setState(() {
// <--- Crucial setState here
_subtitleStyle.remove(value);
_subtitleStyle.insert(0, value);
});
_debouncedSave();
}
},
),
),
ListTile(
title: const Text('Subtitle Color'),
trailing: GestureDetector(
// Use GestureDetector to make the color display tappable
onTap: () => _showColorPickerDialog(context),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: _selectedSubtitleColor,
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Colors.grey),
),
),
),
),
ListTile(
title: const Text('Font Size'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Slider(
value: _subtitleSize,
min: 20.0,
max: 60.0,
divisions: 18,
label: '${_subtitleSize.toStringAsFixed(2)}x',
onChanged: (value) {
HapticFeedback.mediumImpact();
setState(() =>
_subtitleSize = double.parse(value.toStringAsFixed(2)));
_debouncedSave();
},
),
Text('Current: ${_subtitleSize.toStringAsFixed(2)}x'),
],
),
),
],
const Divider(),
if (!isWeb)
SwitchListTile(

View file

@ -24,6 +24,7 @@ Future<Map<String, String>> loadLanguages(BuildContext context) async {
return availableLanguages;
}
PlaybackConfig getPlaybackConfig() {
final user = AppEngine.engine.pb.authStore.record;
if (user == null) {
@ -52,7 +53,10 @@ class PlaybackConfig {
@JsonKey(defaultValue: false)
final bool externalPlayer;
final Map<String, String?>? externalPlayerId;
final String? subtitleStyle;
final String? subtitleColor;
@JsonKey(defaultValue: 10.0)
final double subtitleSize;
PlaybackConfig({
required this.autoPlay,
required this.playbackSpeed,
@ -61,6 +65,9 @@ class PlaybackConfig {
required this.externalPlayer,
required this.disableSubtitle,
this.externalPlayerId,
this.subtitleStyle,
this.subtitleColor,
required this.subtitleSize,
});
String? get currentPlayerPackage {

View file

@ -542,6 +542,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flex_color_picker:
dependency: "direct main"
description:
name: flex_color_picker
sha256: c083b79f1c57eaeed9f464368be376951230b3cb1876323b784626152a86e480
url: "https://pub.dev"
source: hosted
version: "3.7.0"
flex_seed_scheme:
dependency: transitive
description:
name: flex_seed_scheme
sha256: d3ba3c5c92d2d79d45e94b4c6c71d01fac3c15017da1545880c53864da5dfeb0
url: "https://pub.dev"
source: hosted
version: "3.5.0"
flutter:
dependency: "direct main"
description: flutter
@ -1878,4 +1894,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.5.3 <4.0.0"
flutter: ">=3.24.0"
flutter: ">=3.27.0"

View file

@ -57,6 +57,7 @@ dependencies:
cast: ^2.1.0
permission_handler: ^11.3.1
android_intent_plus: ^5.2.1
flex_color_picker: ^3.7.0
dependency_overrides:
media_kit: