mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-09 07:28:14 -07:00
feat: Add seek overlay to native player
This commit is contained in:
parent
cfd4b4a5cc
commit
660298c083
2 changed files with 80 additions and 34 deletions
|
|
@ -0,0 +1,80 @@
|
||||||
|
package nl.jknaapen.fladder.composables.controls
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.draw.scale
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.github.rabehx.iconsax.Iconsax
|
||||||
|
import io.github.rabehx.iconsax.outline.Refresh
|
||||||
|
import nl.jknaapen.fladder.utility.visible
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.time.DurationUnit
|
||||||
|
import kotlin.time.toDuration
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun BoxScope.SeekOverlay(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
value: Long = 0L,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val infiniteTransition = rememberInfiniteTransition()
|
||||||
|
|
||||||
|
val rotation by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 360f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(
|
||||||
|
durationMillis = 6000,
|
||||||
|
easing = FastOutLinearInEasing,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.visible(value != 0L)
|
||||||
|
.wrapContentSize()
|
||||||
|
.background(
|
||||||
|
color = Color.Black.copy(alpha = 0.85f),
|
||||||
|
shape = CircleShape
|
||||||
|
)
|
||||||
|
.padding(12.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Iconsax.Outline.Refresh,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(65.dp)
|
||||||
|
.scale(scaleX = if (value < 0) 1f else -1f, scaleY = 1f)
|
||||||
|
.rotate(-rotation),
|
||||||
|
contentDescription = "SkipLogoRotating",
|
||||||
|
tint = Color.White,
|
||||||
|
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
value.absoluteValue.toDuration(DurationUnit.MILLISECONDS).inWholeSeconds.toString(),
|
||||||
|
modifier = Modifier
|
||||||
|
.align(alignment = Alignment.Center),
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:markdown_widget/widget/markdown.dart';
|
import 'package:markdown_widget/widget/markdown.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/update_provider.dart';
|
import 'package:fladder/providers/update_provider.dart';
|
||||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
|
||||||
import 'package:fladder/screens/shared/media/external_urls.dart';
|
import 'package:fladder/screens/shared/media/external_urls.dart';
|
||||||
import 'package:fladder/util/list_padding.dart';
|
import 'package:fladder/util/list_padding.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
@ -93,8 +87,6 @@ class UpdateInformation extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final apkDownload =
|
|
||||||
releaseInfo.preferredDownloads.entries.where((entry) => entry.value.toLowerCase().endsWith('.apk')).firstOrNull;
|
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
releaseInfo.isNewerThanCurrent ? context.colors.primaryContainer : context.colors.surfaceContainer,
|
releaseInfo.isNewerThanCurrent ? context.colors.primaryContainer : context.colors.surfaceContainer,
|
||||||
|
|
@ -115,32 +107,6 @@ class UpdateInformation extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (apkDownload != null)
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () async {
|
|
||||||
try {
|
|
||||||
final response = await http.get(Uri.parse(apkDownload.value));
|
|
||||||
|
|
||||||
final tempDir = await getTemporaryDirectory();
|
|
||||||
final apkPath = '${tempDir.path}/update.apk';
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final file = File(apkPath);
|
|
||||||
await file.writeAsBytes(response.bodyBytes);
|
|
||||||
launchUrl(context, file.path);
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to download APK: ${response.statusCode}');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (context.mounted) {
|
|
||||||
fladderSnackbar(context, title: 'Failed to download update: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
"Install",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
...releaseInfo.preferredDownloads.entries.map(
|
...releaseInfo.preferredDownloads.entries.map(
|
||||||
(entry) {
|
(entry) {
|
||||||
return FilledButton(
|
return FilledButton(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue