Compare commits

..

No commits in common. "b05d3c0b0e7a1b7747c355a2a93c5b51b7348eb9" and "fb4d1b78d6ce5369b16ca4fe9eb9116def22d1e5" have entirely different histories.

26 changed files with 30 additions and 107 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 247 KiB

View File

@ -3,7 +3,4 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="fill|clip_vertical" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 247 KiB

View File

@ -3,7 +3,4 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="fill|clip_vertical" android:src="@drawable/splash"/>
</item>
</layer-list>

View File

@ -4,12 +4,11 @@
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#0a0a12</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#0a0a12</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@ -3,8 +3,8 @@
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">

View File

@ -4,12 +4,11 @@
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#0a0a12</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#0a0a12</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@ -3,8 +3,8 @@
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

After

Width:  |  Height:  |  Size: 69 B

View File

@ -16,19 +16,14 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
</subviews>
<color key="backgroundColor" red="0.039215686274509803" green="0.039215686274509803" blue="0.07058823529411765" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" red="0.04" green="0.04" blue="0.07" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
@ -38,7 +33,6 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="512" height="512"/>
<image name="LaunchBackground" width="1" height="1"/>
<image name="LaunchBackground" width="375" height="812"/>
</resources>
</document>

View File

@ -51,8 +51,6 @@
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -197,8 +197,8 @@ class AuthService {
const retryDelay = Duration(seconds: 2);
try {
//
await Future<void>.delayed(const Duration(milliseconds: 400));
// 访 App
await Future<void>.delayed(const Duration(seconds: 2));
final deviceId = await _getDeviceId();
_logMsg('init: deviceId=$deviceId');

View File

@ -18,8 +18,7 @@ class ReferrerService {
static final Completer<AdjustAttribution?> _attributionCallbackCompleter =
Completer<AdjustAttribution?>();
/// [AuthService.init] getReferrer Adjust Play Install Referrer fallback
static const int _adjustTimeoutMs = 4500;
static const int _adjustTimeoutMs = 15000;
/// Adjust attributionCallback getAttributionWithTimeout
static void receiveAttributionFromCallback(AdjustAttribution attribution) {

View File

@ -1,7 +1,4 @@
import 'package:flutter/foundation.dart';
/// [VisibilityDetector] [VideoPlayer]
///
/// pop Tab
/// / ext `HomeScreen._requestPlaybackPipeline`
/// Navigator pop [VisibilityDetector] [_MainScaffoldState.didPopNext]
final ValueNotifier<int> homePlaybackResumeNonce = ValueNotifier<int>(0);

View File

@ -27,7 +27,7 @@ class HomeScreen extends StatefulWidget {
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
class _HomeScreenState extends State<HomeScreen> {
List<CategoryItem> _categories = [];
CategoryItem? _selectedCategory;
List<TaskItem> _tasks = [];
@ -44,13 +44,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
/// vs
bool _visibilityHadMeaningfulReport = false;
/// nonce [VideoCard] + [_onHomePlaybackResumeSignal]
void _requestPlaybackPipeline() {
if (!mounted || !widget.isActive) return;
homePlaybackResumeNonce.value++;
}
/// [homePlaybackResumeNonce] + [notifyNow]
/// IndexedStack [homePlaybackResumeNonce] [VisibilityDetector]
/// [notifyNow] Modal / pop My Gallery +
void _scheduleVisibilityRefresh() {
if (!mounted || !widget.isActive) return;
void notify() {
@ -85,7 +80,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
UserState.needShowVideoMenu.addListener(_onExtConfigChanged);
UserState.extConfigItems.addListener(_onExtConfigChanged);
AuthService.isLoginComplete.addListener(_onExtConfigChanged);
@ -112,7 +106,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
homePlaybackResumeNonce.removeListener(_onHomePlaybackResumeSignal);
UserState.needShowVideoMenu.removeListener(_onExtConfigChanged);
UserState.extConfigItems.removeListener(_onExtConfigChanged);
@ -127,19 +120,11 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
_scheduleVisibilityRefresh();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed && mounted && widget.isActive) {
_requestPlaybackPipeline();
}
}
void _onExtConfigChanged() {
if (mounted) setState(() {});
// Timer listener cancel notify
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && widget.isActive) _requestPlaybackPipeline();
if (mounted && widget.isActive) _scheduleVisibilityRefresh();
});
}
@ -154,7 +139,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
static const int _maxConcurrentHomeVideos = 16;
void _onGridCardVisibilityChanged(int index, VisibilityInfo info) {
if (!mounted || !widget.isActive) return;
if (!mounted) return;
if (info.visibleFraction >= _videoVisibilityThreshold) {
_cardVisibleFraction[index] = info.visibleFraction;
_visibilityHadMeaningfulReport = true;
@ -170,7 +155,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
}
void _reconcileVisibleVideoIndicesFromDetector() {
if (!mounted || !widget.isActive) return;
final tasks = _displayTasks;
final scored = <MapEntry<int, double>>[];
for (final e in _cardVisibleFraction.entries) {
@ -216,7 +200,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
super.didUpdateWidget(oldWidget);
if (widget.isActive && !oldWidget.isActive) {
refreshAccount();
_requestPlaybackPipeline();
_scheduleVisibilityRefresh();
}
}
@ -316,7 +300,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
setState(() => _categoriesLoading = false);
// pets [_loadTasks]
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && widget.isActive) _requestPlaybackPipeline();
if (mounted && widget.isActive) _scheduleVisibilityRefresh();
});
}
}
@ -348,7 +332,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
setState(() => _tasksLoading = false);
//
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && widget.isActive) _requestPlaybackPipeline();
if (mounted && widget.isActive) _scheduleVisibilityRefresh();
});
}
}
@ -368,7 +352,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
_visibilityHadMeaningfulReport = false;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && widget.isActive) _requestPlaybackPipeline();
if (mounted && widget.isActive) _scheduleVisibilityRefresh();
});
} else {
_loadTasks(c.id);
@ -463,8 +447,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
_placeholderImage,
videoUrl: task.previewVideoUrl,
credits: credits,
playbackResumeListenable:
homePlaybackResumeNonce,
isActive: widget.isActive &&
_visibleVideoIndices.contains(index) &&
!_userPausedVideoIndices

View File

@ -1,7 +1,6 @@
import 'dart:async' show unawaited;
import 'dart:io' show File;
import 'package:flutter/foundation.dart' show ValueListenable;
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'
show DefaultCacheManager, DownloadProgress, FileInfo;
@ -23,8 +22,6 @@ class VideoCard extends StatefulWidget {
this.showCreditsBadge = true,
this.showBottomGenerateButton = true,
required this.isActive,
/// nonce [isActive] /
this.playbackResumeListenable,
required this.onPlayRequested,
required this.onStopRequested,
this.onGenerateSimilar,
@ -38,7 +35,6 @@ class VideoCard extends StatefulWidget {
final bool showCreditsBadge;
final bool showBottomGenerateButton;
final bool isActive;
final ValueListenable<int>? playbackResumeListenable;
final VoidCallback onPlayRequested;
final VoidCallback onStopRequested;
final VoidCallback? onGenerateSimilar;
@ -61,7 +57,6 @@ class _VideoCardState extends State<VideoCard> {
@override
void initState() {
super.initState();
widget.playbackResumeListenable?.addListener(_onPlaybackResumeSignal);
if (widget.isActive) {
WidgetsBinding.instance.addPostFrameCallback((_) => _loadAndPlay());
}
@ -69,43 +64,13 @@ class _VideoCardState extends State<VideoCard> {
@override
void dispose() {
widget.playbackResumeListenable?.removeListener(_onPlaybackResumeSignal);
_disposeController();
super.dispose();
}
void _onPlaybackResumeSignal() {
if (!mounted || !widget.isActive) return;
// [homePlaybackResumeNonce] dispose
// [_kickPlaybackAfterGlobalResume] setState
Future.microtask(() {
if (!mounted || !widget.isActive) return;
_kickPlaybackAfterGlobalResume();
});
}
/// Visibility pause [play] [didUpdateWidget]
void _kickPlaybackAfterGlobalResume() {
if (widget.videoUrl == null || widget.videoUrl!.isEmpty) return;
final c = _controller;
if (c != null && c.value.isInitialized) {
c.setVolume(0);
c.setLooping(true);
if (!c.value.isPlaying) {
unawaited(c.play());
}
return;
}
_loadAndPlay();
}
@override
void didUpdateWidget(covariant VideoCard oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.playbackResumeListenable != widget.playbackResumeListenable) {
oldWidget.playbackResumeListenable?.removeListener(_onPlaybackResumeSignal);
widget.playbackResumeListenable?.addListener(_onPlaybackResumeSignal);
}
if (oldWidget.isActive && !widget.isActive) {
_releaseVideoDecoder();
} else if (!oldWidget.isActive && widget.isActive) {

View File

@ -16,7 +16,9 @@ import 'features/recharge/google_play_purchase_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
_initAdjust();
// await ReferrerService.init()Adjust [AuthService.init] await getReferrer()
// Facebook/ App activateApp app.dart AutoLog
// Adjust ReferrerService Adjust.getAttributionWithTimeout
await ReferrerService.init();
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: AppColors.surface,

View File

@ -53,20 +53,14 @@ flutter_launcher_icons:
image_path: "assets/images/logo.png"
remove_alpha_ios: true
# 启动屏:与 Android 12+ 统一深色底,避免系统用默认 launcher 图标导致「中间图标过小」;
# android_12 必须单独配 image否则 12+ 只用 ic_launcher圆内显得很小且易有白边感。
flutter_native_splash:
color: "#0a0a12"
image: assets/images/logo.png
background_image: "assets/images/splash.png"
android: true
ios: true
android_gravity: fill|clip_vertical
ios_content_mode: scaleAspectFill
fullscreen: true
# Android 12+ 使用不同 API不支持全屏背景图需单独配置
android_12:
color: "#0a0a12"
image: assets/images/logo.png
icon_background_color: "#0a0a12"
image: "assets/images/logo.png"
flutter:
uses-material-design: true