主体功能完成

This commit is contained in:
fengshengxiong 2024-08-01 13:38:25 +08:00
parent f5cc0098fc
commit db53f60925
183 changed files with 12651 additions and 4336 deletions

View File

@ -6,6 +6,6 @@
<item> <item>
<bitmap <bitmap
android:gravity="fill" android:gravity="fill"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_bg" />
</item> </item>
</layer-list> </layer-list>

View File

@ -6,6 +6,6 @@
<item> <item>
<bitmap <bitmap
android:gravity="fill" android:gravity="fill"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_bg" />
</item> </item>
</layer-list> </layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

View File

@ -19,7 +19,8 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false // id "org.jetbrains.kotlin.android" version "1.7.10" apply false
id "org.jetbrains.kotlin.android" version "1.9.20" apply false
} }
include ":app" include ":app"

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 1016 B

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/side_b/report.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -503,7 +503,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline; PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = tone_snap; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ToneSnap_DEV;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
@ -702,7 +702,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline; PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = tone_snap; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ToneSnap_DEV;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
@ -736,7 +736,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline; PRODUCT_BUNDLE_IDENTIFIER = com.tone.music.offline;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = tone_snap; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ToneSnap_DEV;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;

View File

@ -51,7 +51,7 @@
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Profile"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 225 KiB

View File

@ -24,18 +24,224 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-5684307632319406~7113477061</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppleMusicUsageDescription</key>
<string>We need to access the device media library to select audio files.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>We need to access the microphone to record or select audio files.</string> <string>We need to access the microphone to record or select audio files.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>We need access to the photo library to pick audio files.</string> <string>We need access to the photo library to pick audio files.</string>
<key>NSAppleMusicUsageDescription</key> <key>NSUserTrackingUsageDescription</key>
<string>We need to access the device media library to select audio files.</string> <string>We need your permission to access the advertising identifier to provide better ad services.</string>
<key>NSUserTrackingUsageDescription</key> <key>SKAdNetworkItems</key>
<string>We need your permission to access the advertising identifier to provide better ad services.</string> <array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4fzdc2evr5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4pfyvq9l8r.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2fnua5tdw4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ydx93a7ass.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>5a6flpkh64.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>p78axxw29g.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v72qych5uu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ludvb6z3bs.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cp8zw746q7.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3sh42y64q3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>c6k4g5qg8m.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>s39g8k73mm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qy4746246.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>f38h382jlk.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>hs6bdukanm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v4nxqhlyqp.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>wzmmz9fp6w.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>yclnxrl5pm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>t38b2kh725.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>7ug5zh24hu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>gta9lk7p23.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>vutu7akeur.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>y5ghdn5j9k.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n6fk4nfna4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v9wttpbfk9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n38lu8286q.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>47vhws6wlr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>kbd757ywx3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>9t245vhmpl.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>eh6m2bh4zr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>a2p9lx4jpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>22mmun2rn5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4468km3ulz.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2u9pt9hc89.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8s468mfl3y.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>klf5c3l5u5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ppxm28t8ap.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ecpz2srf59.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>uw77j35x4d.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>pwa73g5rt2.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>mlmmfzh3r3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>578prtvx9j.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4dzt52r2t5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>e5fvkxwrpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8c4e2ghe7u.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>zq492l623r.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3rd42ekr43.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qcr597p9d.skadnetwork</string>
</dict>
</array>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@ -53,206 +259,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-5684307632319406~7113477061</string>
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4fzdc2evr5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4pfyvq9l8r.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2fnua5tdw4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ydx93a7ass.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>5a6flpkh64.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>p78axxw29g.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v72qych5uu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ludvb6z3bs.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cp8zw746q7.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3sh42y64q3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>c6k4g5qg8m.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>s39g8k73mm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qy4746246.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>f38h382jlk.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>hs6bdukanm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v4nxqhlyqp.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>wzmmz9fp6w.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>yclnxrl5pm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>t38b2kh725.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>7ug5zh24hu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>gta9lk7p23.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>vutu7akeur.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>y5ghdn5j9k.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n6fk4nfna4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v9wttpbfk9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n38lu8286q.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>47vhws6wlr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>kbd757ywx3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>9t245vhmpl.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>eh6m2bh4zr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>a2p9lx4jpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>22mmun2rn5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4468km3ulz.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2u9pt9hc89.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8s468mfl3y.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>klf5c3l5u5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ppxm28t8ap.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ecpz2srf59.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>uw77j35x4d.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>pwa73g5rt2.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>mlmmfzh3r3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>578prtvx9j.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4dzt52r2t5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>e5fvkxwrpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8c4e2ghe7u.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>zq492l623r.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3rd42ekr43.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qcr597p9d.skadnetwork</string>
</dict>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -7,7 +7,10 @@ import 'dart:io' show Platform;
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:tone_snap/data/storage/music_box.dart';
import 'package:tone_snap/modules/launch/launch_controller.dart';
import 'package:tone_snap/utils/log_util.dart'; import 'package:tone_snap/utils/log_util.dart';
class AppOpenAdManager { class AppOpenAdManager {
@ -26,13 +29,16 @@ class AppOpenAdManager {
AppOpenAd? _appOpenAd; AppOpenAd? _appOpenAd;
bool _isShowingAd = false; bool _isShowingAd = false;
///
DateTime? closeDateTime;
/// 广id /// 广id
final adUnitId = Platform.isAndroid final adUnitId = Platform.isAndroid
? (kDebugMode ? 'ca-app-pub-3940256099942544/9257395921' : '') ? (kReleaseMode ? '' : 'ca-app-pub-3940256099942544/9257395921')
: (kDebugMode ? 'ca-app-pub-3940256099942544/5575463023' : 'ca-app-pub-5684307632319406/2523581084'); : (kReleaseMode ? 'ca-app-pub-5684307632319406/2523581084' : 'ca-app-pub-3940256099942544/5575463023');
/// 广 /// 广
void loadAd() async { Future<void> loadAd() async {
final List<ConnectivityResult> connectivityResult = await (Connectivity().checkConnectivity()); final List<ConnectivityResult> connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult.contains(ConnectivityResult.none)) { if (connectivityResult.contains(ConnectivityResult.none)) {
return; return;
@ -40,7 +46,7 @@ class AppOpenAdManager {
if (isAdAvailable) { if (isAdAvailable) {
return; return;
} }
AppOpenAd.load( await AppOpenAd.load(
adUnitId: adUnitId, adUnitId: adUnitId,
request: const AdRequest(), request: const AdRequest(),
adLoadCallback: AppOpenAdLoadCallback( adLoadCallback: AppOpenAdLoadCallback(
@ -48,6 +54,11 @@ class AppOpenAdManager {
LogUtil.d('开屏广告加载完成'); LogUtil.d('开屏广告加载完成');
_appOpenAd = ad; _appOpenAd = ad;
_appOpenLoadTime = DateTime.now(); _appOpenLoadTime = DateTime.now();
if (AppOpenAdManager().isAdAvailable) {
if (Get.isRegistered<LaunchController>()) {
LaunchController.to.editChangeValue();
}
}
}, },
onAdFailedToLoad: (error) { onAdFailedToLoad: (error) {
LogUtil.e('开屏广告加载失败: $error'); LogUtil.e('开屏广告加载失败: $error');
@ -84,6 +95,17 @@ class AppOpenAdManager {
if(onTap != null) onTap(); if(onTap != null) onTap();
return; return;
} }
if (closeDateTime != null) {
//
Duration timeDifference = DateTime.now().difference(closeDateTime!);
// openAppEventDuration
int openAppEventDuration = MusicBox().getOpenAppEventDuration();
// 10
if (timeDifference < Duration(seconds: openAppEventDuration)) {
return;
}
}
// fullScreenContentCallback 广 // fullScreenContentCallback 广
_appOpenAd!.fullScreenContentCallback = FullScreenContentCallback( _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(
// 广 // 广
@ -108,6 +130,8 @@ class AppOpenAdManager {
}, },
onAdDismissedFullScreenContent: (ad) { onAdDismissedFullScreenContent: (ad) {
LogUtil.d('$ad onAdDismissedFullScreenContent'); LogUtil.d('$ad onAdDismissedFullScreenContent');
closeDateTime = DateTime.now();
// 广 // 广
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);

View File

@ -27,8 +27,8 @@ class InterstitialAdManager {
/// 广id /// 广id
final adUnitId = Platform.isAndroid final adUnitId = Platform.isAndroid
? (kDebugMode ? 'ca-app-pub-3940256099942544/1033173712' : '') ? (kReleaseMode ? '' : 'ca-app-pub-3940256099942544/1033173712')
: (kDebugMode ? 'ca-app-pub-3940256099942544/4411468910' : 'ca-app-pub-5684307632319406/2760767691'); : (kReleaseMode ? 'ca-app-pub-5684307632319406/2760767691' : 'ca-app-pub-3940256099942544/4411468910');
/// 广 /// 广
void loadAd() async { void loadAd() async {

View File

@ -43,7 +43,7 @@ class BaseEasyLoading {
bool show = true, bool show = true,
}) { }) {
EasyLoading.instance.userInteractions = false; EasyLoading.instance.userInteractions = false;
if (show) EasyLoading.show(status: value, dismissOnTap: true); if (show) EasyLoading.show(status: value, dismissOnTap: false);
} }
static void dismiss({bool dismiss = true}) { static void dismiss({bool dismiss = true}) {

View File

@ -0,0 +1,143 @@
// Author: fengshengxiong
// Date: 2024/5/11
// Description:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:tone_snap/components/divider_widget.dart';
import 'package:tone_snap/res/themes/app_colors.dart';
import 'package:tone_snap/utils/obj_util.dart';
class CreatePlaylistDialog extends Dialog {
CreatePlaylistDialog({super.key,this.name, required this.onTap});
final _textEditingController = TextEditingController();
final String? name;
final Function(String name) onTap;
@override
Widget build(BuildContext context) {
_textEditingController.text = name ?? '';
return Scaffold(
backgroundColor: Colors.transparent,
body: Material(
type: MaterialType.transparency,
child: Center(
child: IntrinsicHeight(
child: Container(
width: 1.sw * 0.72,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: const Color(0xFF282A2C),
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
children: [
SizedBox(height: 19.h),
Text(
'Create playlist',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 17.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 19.h),
Container(
height: 30.h,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 16.w),
decoration: BoxDecoration(
color: const Color(0xFF1c1c1e),
borderRadius: BorderRadius.circular(6).r,
border: Border.all(
color: const Color(0xff333437),
width: 1.w,
),
),
child: TextField(
maxLines: 1,
controller: _textEditingController,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
style: TextStyle(color: Colors.white, fontSize: 15.sp),
textAlignVertical: TextAlignVertical.center,
maxLength: 30,
decoration: InputDecoration(
counterText: '',
hintText: 'My playlist1',
hintStyle: TextStyle(
color: const Color(0xFF5a635f),
fontSize: 15.sp,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 10).w,
isCollapsed: true,
border: InputBorder.none,
),
),
),
SizedBox(height: 19.h),
DividerWidget(
height: 0.5.h,
color: const Color(0xA6545458),
),
SizedBox(
height: 44.h,
child: Row(
children: [
_optionButton('Cancel', false),
Container(
width: 0.5.w,
height: double.infinity,
color: const Color(0xA6545458),
),
_optionButton('Confirm', true),
],
),
),
],
),
),
),
),
),
);
}
Widget _optionButton(String label, bool isConfirm) {
return Expanded(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
if (isConfirm) {
if (ObjUtil.isNotEmpty(_textEditingController.text)) {
onTap(_textEditingController.text);
} else {
onTap('My playlist1');
}
Get.back();
} else {
Get.back();
}
},
child: SizedBox(
height: double.infinity,
child: Center(
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
color: isConfirm ? seedColor : Colors.white,
fontSize: 17.sp,
),
),
),
),
),
),
);
}
}

View File

@ -134,7 +134,7 @@ class RenameDialogState extends State<RenameDialog> {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (isConfirm) { if (isConfirm) {
if (!ObjUtil.isNotEmptyStr(_textEditingController.text)) { if (!ObjUtil.isNotEmpty(_textEditingController.text)) {
BaseEasyLoading.toast('name cannot be empty'); BaseEasyLoading.toast('name cannot be empty');
return; return;
} }

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
/// [author] Fson
/// [date] 2022/12/7
/// [description]
/// GetBindWidget can bind GetxController, and when the page is disposed,
/// it can automatically destroy the bound related GetXController
///
///
/// Sample:
///
/// class SampleController extends GetxController {
/// final String title = 'My Awesome View';
/// }
///
/// class SamplePage extends StatelessWidget {
/// final controller = SampleController();
///
/// @override
/// Widget build(BuildContext context) {
/// return GetBindWidget(
/// bind: controller,
/// child: Container(),
/// );
/// }
/// }
class GetBindWidget extends StatefulWidget {
const GetBindWidget({
super.key,
this.bind,
this.tag,
this.binds,
this.tags,
required this.child,
}) : assert(
binds == null || tags == null || binds.length == tags.length,
'The binds and tags arrays length should be equal\n'
'and the elements in the two arrays correspond one-to-one',
);
final GetxController? bind;
final String? tag;
final List<GetxController>? binds;
final List<String>? tags;
final Widget child;
@override
GetBindWidgetState createState() => GetBindWidgetState();
}
class GetBindWidgetState extends State<GetBindWidget>{
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void dispose() {
_closeGetXController();
_closeGetXControllers();
super.dispose();
}
///Close GetxController bound to the current page
void _closeGetXController() {
if (widget.bind == null) {
return;
}
var key = widget.bind.runtimeType.toString() + (widget.tag ?? '');
GetInstance().delete(key: key);
}
///Batch close GetxController bound to the current page
void _closeGetXControllers() {
if (widget.binds == null) {
return;
}
for (var i = 0; i < widget.binds!.length; i++) {
var type = widget.binds![i].runtimeType.toString();
if (widget.tags == null) {
GetInstance().delete(key: type);
} else {
var key = type + (widget.tags?[i] ?? '');
GetInstance().delete(key: key);
}
}
}
}

View File

@ -1,49 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tone_snap/routes/app_routes.dart';
class MusicBarController extends GetxController with GetSingleTickerProviderStateMixin {
static MusicBarController get to => Get.find<MusicBarController>();
AnimationController? _controller;
var bottom = 0.0.obs;
double bottomPadding = 0.0;
@override
void onInit() {
super.onInit();
_controller = AnimationController(vsync: this)..duration = const Duration(milliseconds: 200);
WidgetsBinding.instance.addPostFrameCallback((_) {
bottomPadding = MediaQuery.of(Get.context!).padding.bottom;
bottom.value = kBottomNavigationBarHeight + bottomPadding;
});
}
@override
void onClose() {
_controller?.dispose();
super.onClose();
}
///
void toBottom() {
var animation = Tween<double>(begin: bottom.value, end: bottomPadding).animate(_controller!);
animation.addListener(() {
bottom.value = animation.value;
});
_controller!.forward();
}
///
void riseUp() {
var animation = Tween<double>(begin: bottom.value, end: kBottomNavigationBarHeight + bottomPadding).animate(_controller!);
animation.addListener(() {
bottom.value = animation.value;
});
_controller!.forward();
}
///
void openPlayPage() {
Get.toNamed(AppRoutes.playPage, arguments: {'isMusicBarOpen': true});
}
}

View File

@ -1,95 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:tone_snap/components/my_marquee_text.dart';
import 'package:tone_snap/components/network_image_widget.dart';
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
import 'package:tone_snap/res/themes/app_sizes.dart';
import 'package:tone_snap/utils/obj_util.dart';
import 'music_bar_controller.dart';
class MusicBarView extends StatelessWidget {
MusicBarView({super.key});
final controller = Get.put(MusicBarController(), permanent: true);
final musicPlayerController = MusicPlayerController.to;
@override
Widget build(BuildContext context) {
return Obx(() {
return Stack(
fit: StackFit.expand,
children: [
Positioned(
bottom: controller.bottom.value,
left: 16.w,
right: 16.w,
child: GestureDetector(
onTap: controller.openPlayPage,
child: Container(
width: 1.sw - 32.w,
height: musicBarHeight,
padding: EdgeInsets.symmetric(horizontal: 26.w),
decoration: BoxDecoration(
color: const Color(0xFF80F988),
borderRadius: BorderRadius.circular(36).r,
boxShadow: const [
BoxShadow(
color: Color(0x40040604),
offset: Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
],
),
child: Row(
children: [
ClipOval(
child: Obx(() {
return NetworkImageWidget(
url: musicPlayerController.musicModel.value.thumbnail,
width: 48.w,
height: 48.w,
);
}),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() {
return MyMarqueeText(
text: ObjUtil.getStr(musicPlayerController.musicModel.value.title),
textStyle: TextStyle(
color: Colors.black,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
);
}),
SizedBox(height: 4.h),
Obx(() {
return MyMarqueeText(
text: ObjUtil.getStr(musicPlayerController.musicModel.value.subTitle),
textStyle: TextStyle(
color: Colors.black,
fontSize: 12.sp,
),
);
}),
],
),
),
],
),
),
),
),
],
);
});
}
}

View File

@ -20,8 +20,8 @@ class MyMarqueeText extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Marquee( return Marquee(
delay: const Duration(seconds: 2), delay: Duration.zero,
duration: Duration(seconds: enable ? 12 : 0), duration: Duration(seconds: enable ? 16 : 0),
pause: Duration.zero, pause: Duration.zero,
gap: 80, gap: 80,
child: Text(text, style: textStyle), child: Text(text, style: textStyle),

View File

@ -4,6 +4,7 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:tone_snap/generated/assets.dart'; import 'package:tone_snap/generated/assets.dart';
class NetworkImageWidget extends StatelessWidget { class NetworkImageWidget extends StatelessWidget {
@ -14,7 +15,9 @@ class NetworkImageWidget extends StatelessWidget {
this.radius = 0.0, this.radius = 0.0,
this.url, this.url,
this.fit = BoxFit.cover, this.fit = BoxFit.cover,
this.placeholder, this.placeholderWidth,
this.placeholderHeight,
this.placeholderImg,
this.noPlaceholder = false, this.noPlaceholder = false,
}); });
@ -23,7 +26,9 @@ class NetworkImageWidget extends StatelessWidget {
final double radius; final double radius;
final String? url; final String? url;
final BoxFit fit; final BoxFit fit;
final String? placeholder; final double? placeholderWidth;
final double? placeholderHeight;
final String? placeholderImg;
final bool noPlaceholder; final bool noPlaceholder;
@override @override
@ -35,20 +40,27 @@ class NetworkImageWidget extends StatelessWidget {
height: height, height: height,
imageUrl: '$url', imageUrl: '$url',
fit: fit, fit: fit,
placeholder: noPlaceholder ? null : (context, url) { placeholder: (context, url) {
return _placeholderWidget(Assets.sideBImgPlaceholder); return noPlaceholder ? Container() : _placeholderWidget();
}, },
errorWidget: noPlaceholder ? null : (context, url, error) { errorWidget: (context, url, error) {
return _placeholderWidget(Assets.sideBImgError); return noPlaceholder ? Container() : _placeholderWidget();
}, },
), ),
); );
} }
Widget _placeholderWidget(String img) { Widget _placeholderWidget() {
return Image.asset( return Container(
placeholder ?? img, color: const Color(0xFF242529),
color: Colors.white12, child: FittedBox(
fit: BoxFit.none,
child: Image.asset(
placeholderImg ?? Assets.sideBMusicPlaceholder,
width: placeholderWidth ?? 24.w,
height: placeholderHeight ?? 24.w,
),
),
); );
} }
} }

View File

@ -1,58 +1,42 @@
// Author: fengshengxiong
// Date: 2024/5/7
// Description:
import 'dart:async'; import 'dart:async';
import 'package:easy_refresh/easy_refresh.dart'; import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:tone_snap/components/view_state_widget.dart';
class BaseEasyRefresh extends StatelessWidget { class BaseEasyRefresh extends StatelessWidget {
const BaseEasyRefresh({ final EasyRefreshController? controller;
super.key, final bool noMoreRefresh;
required this.controller, final bool noMoreLoad;
this.header, final bool refreshOnStart;
this.footer,
this.onRefresh,
this.onLoad,
this.refreshOnStart = false,
required this.viewState,
required this.child,
this.width,
this.height,
});
final EasyRefreshController controller;
final Header? header; final Header? header;
final Footer? footer; final Footer? footer;
final FutureOr Function()? onRefresh; final FutureOr Function()? onRefresh;
final FutureOr Function()? onLoad; final FutureOr Function()? onLoad;
final bool refreshOnStart;
final ViewState viewState;
final Widget child; final Widget child;
final double? width;
final double? height; const BaseEasyRefresh({
super.key,
this.controller,
this.noMoreRefresh = false,
this.noMoreLoad = false,
this.refreshOnStart = false,
this.header,
this.footer,
required this.child,
this.onRefresh,
this.onLoad,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EasyRefresh( return EasyRefresh(
refreshOnStart: refreshOnStart,
controller: controller, controller: controller,
header: header ?? const ClassicHeader(), header: header,
footer: footer ?? const ClassicFooter(), footer: footer,
onRefresh: onRefresh, onRefresh: onRefresh,
onLoad: onLoad, onLoad: onLoad,
refreshOnStart: refreshOnStart, child: child,
child: viewState == ViewState.normal ? child : SingleChildScrollView(
child: SizedBox(
width: width ?? 1.sw,
height: height ?? 300.h,
child: ViewStateWidget(
viewState: viewState,
child: child,
),
),
),
); );
} }
} }

View File

@ -4,6 +4,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:tone_snap/data/enum/app_side_enum.dart';
import 'package:tone_snap/generated/assets.dart';
import 'package:tone_snap/global/app_config.dart';
import 'package:tone_snap/res/themes/app_colors.dart';
/// ///
enum ViewState { normal, error, loading, empty } enum ViewState { normal, error, loading, empty }
@ -26,11 +30,9 @@ class ViewStateWidget extends StatelessWidget {
case ViewState.normal: case ViewState.normal:
return child; return child;
case ViewState.loading: case ViewState.loading:
return loadingView( return loadingView(backgroundColor: cpiBgColor);
backgroundColor: cpiBgColor,
);
case ViewState.empty: case ViewState.empty:
return emptyView(); return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB();
case ViewState.error: case ViewState.error:
return errorView(); return errorView();
} }
@ -40,23 +42,19 @@ class ViewStateWidget extends StatelessWidget {
/// ///
Widget loadingView({ Widget loadingView({
Color? color, Color? color,
Animation<Color?>? valueColor,
Color? backgroundColor, Color? backgroundColor,
double? value,
}) { }) {
return Center( return Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 3, strokeWidth: 3,
color: color, color: AppConfig.appSideEnum == AppSideEnum.sideA ? color : seedColor,
valueColor: valueColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
value: value,
), ),
); );
} }
/// ///
Widget emptyView({String? msg, Color? textColor}) { Widget emptyViewA({String? msg, Color? textColor}) {
return Center( return Center(
child: Text( child: Text(
msg ?? 'No data', msg ?? 'No data',
@ -69,6 +67,17 @@ Widget emptyView({String? msg, Color? textColor}) {
); );
} }
/// 2
Widget emptyViewB() {
return Center(
child: Image.asset(
Assets.sideBEmpty,
width: 180.w,
height: 160.h,
),
);
}
/// ///
Widget errorView({String? msg, Color? textColor}) { Widget errorView({String? msg, Color? textColor}) {
return Center( return Center(

View File

@ -2,65 +2,72 @@
// Date: 2024/6/18 // Date: 2024/6/18
// Description: Api // Description: Api
import 'package:devicelocale/devicelocale.dart'; import 'package:dio/dio.dart';
import 'package:tone_snap/data/models/player_model.dart'; import 'package:flutter/material.dart';
import 'package:tone_snap/global/app_config.dart';
import 'package:tone_snap/data/models/browse_model.dart';
import 'package:tone_snap/data/network/dio_client.dart';
import 'package:tone_snap/data/models/next_model.dart'; import 'package:tone_snap/data/models/next_model.dart';
import 'package:tone_snap/utils/date_util.dart'; import 'package:tone_snap/data/models/player_model.dart';
import 'package:tone_snap/data/models/search_suggestions_model.dart';
import 'package:tone_snap/data/network/base_error.dart';
import 'package:tone_snap/data/network/dio_client.dart';
import 'package:tone_snap/data/storage/music_box.dart';
import 'package:tone_snap/global/app_config.dart';
class MusicApi { class MusicApi {
static const String baseUrl = 'https://music.youtube.com/youtubei/v1/'; static const String baseUrl = 'https://music.youtube.com/youtubei/v1/';
/// browse接口 /// browse
static Future<T?> browse<T>({ static Future<T?> browse<T>({
String? visitorData, String? visitorData,
Map<String, dynamic>? queryParameters, Map<String, dynamic>? queryParameters,
T Function(Map<String, dynamic>)? formJson, T Function(Map<String, dynamic>)? formJson,
bool showLoading = false,
bool showToast = false,
}) async { }) async {
String date = DateUtil.getSevenDaysAgo(); String clientVersion = MusicBox().getClientVersion();
String locale = await Devicelocale.currentLocale ?? AppConfig.defaultLocale; String preferredLanguages = WidgetsBinding.instance.platformDispatcher.locale.languageCode;
final body = { final body = {
"context": { "context": {
"client": { "client": {
"visitorData": visitorData, "visitorData": visitorData,
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
// "clientVersion": "1.$date.01.00", "clientVersion": clientVersion,
"clientVersion": "1.20240607.01.00",
"platform": "DESKTOP", "platform": "DESKTOP",
"hl": locale, "hl": preferredLanguages,
// "gl": AppConfig.isoCode "gl": AppConfig.isoCode
"gl": 'HK'
} }
} }
}; };
T? resultModel; T? model;
await DioClient().request<T>( await DioClient().request<T>(
'browse', 'browse',
showLoading: showLoading,
showToast: showToast,
requestMethod: RequestMethod.post, requestMethod: RequestMethod.post,
data: body, data: body,
queryParameters: queryParameters, queryParameters: queryParameters,
formJson: formJson, formJson: formJson,
success: (model) => resultModel = model, success: (data) => model = data,
); );
return resultModel; return model;
} }
/// next接口 /// next
static Future<NextModel?> next({String? visitorData, String? playlistId, String? videoId}) async { static Future<NextModel?> next({
String date = DateUtil.getSevenDaysAgo(); String? playlistId,
String locale = await Devicelocale.currentLocale ?? AppConfig.defaultLocale; String? videoId,
bool showLoading = false,
bool showToast = true,
}) async {
String clientVersion = MusicBox().getClientVersion();
String preferredLanguages = WidgetsBinding.instance.platformDispatcher.locale.languageCode;
final body = { final body = {
"context": { "context": {
"client": { "client": {
"visitorData": visitorData,
"clientName": "WEB_REMIX", "clientName": "WEB_REMIX",
"clientVersion": "1.$date", "clientVersion": clientVersion,
"platform": "DESKTOP", "platform": "DESKTOP",
"hl": locale, "hl": preferredLanguages,
// "gl": AppConfig.isoCode "gl": AppConfig.isoCode
"gl": 'HK'
} }
} }
}; };
@ -69,28 +76,34 @@ class MusicApi {
'playlistId': playlistId, 'playlistId': playlistId,
'videoId': videoId 'videoId': videoId
}; };
NextModel? nextModel; NextModel? model;
await DioClient().request<NextModel>( await DioClient().request<NextModel>(
'next', 'next',
showLoading: true, showLoading: showLoading,
showToast: true, showToast: showToast,
requestMethod: RequestMethod.post, requestMethod: RequestMethod.post,
data: body, data: body,
queryParameters: queryParameters, queryParameters: queryParameters,
formJson: NextModel.fromMap, formJson: NextModel.fromMap,
success: (model) => nextModel = model, success: (data) => model = data,
); );
return nextModel; return model;
} }
/// player接口 /// player
static Future<PlayerModel?> player({String? visitorData, String? videoId}) async { static Future<PlayerModel?> player({
String date = DateUtil.getSevenDaysAgo(); String? videoId,
CancelToken? cancelToken,
Function(BaseError baseError)? fail,
bool showLoading = false,
bool showToast = true,
}) async {
String playerVersion = MusicBox().getPlayerVersion();
final body = { final body = {
"context": { "context": {
"client": { "client": {
"clientName": "ANDROID_MUSIC", "clientName": "ANDROID_MUSIC",
"clientVersion": "6.07.1", "clientVersion": playerVersion,
"platform": "MOBILE", "platform": "MOBILE",
"browserVersion":"125.0.0.0" "browserVersion":"125.0.0.0"
} }
@ -101,17 +114,92 @@ class MusicApi {
'playlistId': null, 'playlistId': null,
'videoId': videoId 'videoId': videoId
}; };
PlayerModel? playerModel; PlayerModel? model;
await DioClient().request<PlayerModel>( await DioClient().request<PlayerModel>(
'player', 'player',
showLoading: true, showLoading: showLoading,
showToast: true, showToast: showToast,
requestMethod: RequestMethod.post, requestMethod: RequestMethod.post,
data: body, data: body,
queryParameters: queryParameters, queryParameters: queryParameters,
cancelToken: cancelToken,
formJson: PlayerModel.fromMap, formJson: PlayerModel.fromMap,
success: (model) => playerModel = model, success: (data) => model = data,
fail: fail,
); );
return playerModel; return model;
}
///
static Future<SearchSuggestionsModel?> searchSuggestions({
String? input,
CancelToken? cancelToken,
bool showLoading = false,
bool showToast = false,
}) async {
String clientVersion = MusicBox().getClientVersion();
String preferredLanguages = WidgetsBinding.instance.platformDispatcher.locale.languageCode;
final body = {
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": clientVersion,
"platform": "DESKTOP",
"hl": preferredLanguages,
"gl": AppConfig.isoCode
}
}
};
Map<String, dynamic>? queryParameters = {
'prettyPrint': false,
'input': input,
};
SearchSuggestionsModel? model;
await DioClient().request<SearchSuggestionsModel>(
'music/get_search_suggestions',
showLoading: showLoading,
showToast: showToast,
requestMethod: RequestMethod.post,
data: body,
queryParameters: queryParameters,
cancelToken: cancelToken,
formJson: SearchSuggestionsModel.fromMap,
success: (data) => model = data,
);
return model;
}
///
static Future<T?> search<T>({
Map<String, dynamic>? queryParameters,
T Function(Map<String, dynamic>)? formJson,
bool showLoading = false,
bool showToast = true,
}) async {
String clientVersion = MusicBox().getClientVersion();
String preferredLanguages = WidgetsBinding.instance.platformDispatcher.locale.languageCode;
final body = {
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": clientVersion,
"platform": "DESKTOP",
"hl": preferredLanguages,
"gl": AppConfig.isoCode
}
}
};
T? model;
await DioClient().request<T>(
'search',
showLoading: showLoading,
showToast: showToast,
requestMethod: RequestMethod.post,
data: body,
queryParameters: queryParameters,
formJson: formJson,
success: (data) => model = data,
);
return model;
} }
} }

View File

@ -9,13 +9,13 @@ import 'package:tone_snap/data/models/isocode_model.dart';
class TikUsTokApi { class TikUsTokApi {
static const String baseUrl = 'https://api.tikustok.com/'; static const String baseUrl = 'https://api.tikustok.com/';
/// ip ///
static Future<BaseModel<IosCodeModel>?> getIp() async { static Future<BaseModel<IsoCodeModel>?> getIsoCode() async {
BaseModel<IosCodeModel>? baseModel; BaseModel<IsoCodeModel>? baseModel;
await DioClient(baseUrl: baseUrl).request<BaseModel<IosCodeModel>>( await DioClient(baseUrl: baseUrl).request<BaseModel<IsoCodeModel>>(
'app/common/getIPInfo', 'app/common/getIPInfo',
requestMethod: RequestMethod.get, requestMethod: RequestMethod.get,
formJson: (json) => BaseModel<IosCodeModel>.fromMap(json, IosCodeModel.fromMap), formJson: (json) => BaseModel<IsoCodeModel>.fromMap(json, IsoCodeModel.fromMap),
success: (model) => baseModel = model, success: (model) => baseModel = model,
); );
return baseModel; return baseModel;

View File

@ -1,9 +1,9 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/6/13 // Date: 2024/6/13
// Description: // Description:
enum BrowseType { enum MusicType {
/// / ///
musicVideoTypeAtv(name: 'MUSIC_VIDEO_TYPE_ATV'), musicVideoTypeAtv(name: 'MUSIC_VIDEO_TYPE_ATV'),
/// ///
@ -12,11 +12,11 @@ enum BrowseType {
/// ///
musicPageTypeAlbum(name: 'MUSIC_PAGE_TYPE_ALBUM'), musicPageTypeAlbum(name: 'MUSIC_PAGE_TYPE_ALBUM'),
///
// musicPageTypeArtist(name: 'MUSIC_PAGE_TYPE_ARTIST'),
/// / /// /
musicPageTypePlaylist(name: 'MUSIC_PAGE_TYPE_PLAYLIST'); musicPageTypePlaylist(name: 'MUSIC_PAGE_TYPE_PLAYLIST'),
///
musicPageTypeArtist(name: 'MUSIC_PAGE_TYPE_ARTIST');
/// ///
// musicPageTypeTrackLyrics(name: 'MUSIC_PAGE_TYPE_TRACK_LYRICS'), // musicPageTypeTrackLyrics(name: 'MUSIC_PAGE_TYPE_TRACK_LYRICS'),
@ -24,20 +24,21 @@ enum BrowseType {
/// ///
// musicPageTypeTrackRelated(name: 'MUSIC_PAGE_TYPE_TRACK_RELATED'); // musicPageTypeTrackRelated(name: 'MUSIC_PAGE_TYPE_TRACK_RELATED');
const BrowseType({ const MusicType({
required this.name, required this.name,
}); });
final String name; final String name;
} }
extension BrowseTypeExtension on BrowseType { extension MusicTypeExtension on MusicType {
static bool isThereAny(String? type) { static bool isThereAny(String? type) {
if (type == BrowseType.musicVideoTypeAtv.name || if (type == MusicType.musicVideoTypeAtv.name ||
type == BrowseType.musicVideoTypeOmv.name || // type == BrowseType.musicVideoTypeOmv.name ||
type == BrowseType.musicPageTypeAlbum.name || type == MusicType.musicPageTypeAlbum.name ||
// type == BrowseType.musicPageTypeArtist.name || type == MusicType.musicPageTypePlaylist.name
type == BrowseType.musicPageTypePlaylist.name) { // type == BrowseType.musicPageTypeArtist.name
) {
return true; return true;
} else { } else {
return false; return false;

View File

@ -0,0 +1,35 @@
// Author: fengshengxiong
// Date: 2024/6/20
// Description: Browse分组模型
import 'dart:convert';
import 'package:tone_snap/data/models/music_model.dart';
class BrowseGroupModel {
String? groupTitle;
String? musicType;
List<MusicModel>? browseList;
BrowseGroupModel({
this.groupTitle,
this.musicType,
this.browseList,
});
factory BrowseGroupModel.fromJson(String str) => BrowseGroupModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory BrowseGroupModel.fromMap(Map<String, dynamic> json) => BrowseGroupModel(
groupTitle: json["groupTitle"],
musicType: json["musicType"],
browseList: json["browseList"] == null ? <MusicModel>[] : List<MusicModel>.from(json["browseList"]!.map((x) => MusicModel.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"groupTitle": groupTitle,
"musicType": musicType,
"browseList": browseList == null ? [] : List<dynamic>.from(browseList!.map((x) => x.toMap())),
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +0,0 @@
// Author: fengshengxiong
// Date: 2024/6/20
// Description:
import 'dart:convert';
class HomeModel {
String? headerTitle;
List<Content>? contents;
String? browseType;
HomeModel({
this.headerTitle,
this.contents,
this.browseType,
});
factory HomeModel.fromJson(String str) => HomeModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory HomeModel.fromMap(Map<String, dynamic> json) => HomeModel(
headerTitle: json["headerTitle"],
contents: json["contents"] == null ? [] : List<Content>.from(json["contents"]!.map((x) => Content.fromMap(x))),
browseType: json["browseType"]
);
Map<String, dynamic> toMap() => {
"headerTitle": headerTitle,
"contents": contents == null ? [] : List<dynamic>.from(contents!.map((x) => x.toMap())),
"browseType": browseType,
};
}
class Content {
String? title;
String? subTitle;
String? thumbnail;
String? videoId;
String? playlistId;
String? browseId;
String? params;
Content({
this.title,
this.subTitle,
this.thumbnail,
this.videoId,
this.playlistId,
this.browseId,
this.params,
});
factory Content.fromJson(String str) => Content.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Content.fromMap(Map<String, dynamic> json) => Content(
title: json["title"],
subTitle: json["subTitle"],
thumbnail: json["thumbnail"],
videoId: json["videoId"],
playlistId: json["playlistId"],
browseId: json["browseId"],
params: json["params"],
);
Map<String, dynamic> toMap() => {
"title": title,
"subTitle": subTitle,
"thumbnail": thumbnail,
"videoId": videoId,
"playlistId": playlistId,
"browseId": browseId,
"params": params,
};
}

View File

@ -4,20 +4,20 @@
import 'dart:convert'; import 'dart:convert';
class IosCodeModel { class IsoCodeModel {
String? isoCode; String? isoCode;
String? ip; String? ip;
IosCodeModel({ IsoCodeModel({
this.isoCode, this.isoCode,
this.ip, this.ip,
}); });
factory IosCodeModel.fromJson(String str) => IosCodeModel.fromMap(json.decode(str)); factory IsoCodeModel.fromJson(String str) => IsoCodeModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
factory IosCodeModel.fromMap(Map<String, dynamic> json) => IosCodeModel( factory IsoCodeModel.fromMap(Map<String, dynamic> json) => IsoCodeModel(
isoCode: json["isoCode"], isoCode: json["isoCode"],
ip: json["ip"], ip: json["ip"],
); );

View File

@ -1,9 +1,11 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/6/24 // Date: 2024/6/24
// Description: // Description:
import 'dart:convert'; import 'dart:convert';
import 'package:background_downloader/background_downloader.dart';
import 'package:dio/dio.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
part 'music_model.g.dart'; part 'music_model.g.dart';
@ -11,42 +13,82 @@ part 'music_model.g.dart';
@HiveType(typeId: 2) @HiveType(typeId: 2)
class MusicModel extends HiveObject { class MusicModel extends HiveObject {
@HiveField(0) @HiveField(0)
String? title;
@HiveField(1)
String? subTitle;
@HiveField(2)
String? thumbnail;
@HiveField(3)
String? videoId; String? videoId;
@HiveField(1)
String? title;
@HiveField(2)
String? subtitle;
@HiveField(3)
String? coverUrl;
@HiveField(4) @HiveField(4)
String? playlistId;
@HiveField(5)
String? url; String? url;
@HiveField(5)
String? localPath;
@HiveField(6)
String? musicType;
@HiveField(7)
String? playlistId;
@HiveField(8)
String? browseId;
@HiveField(9)
String? params;
///
bool isLove = false;
///
double progress;
TaskStatus? taskStatus;
CancelToken? cancelToken;
MusicModel({ MusicModel({
this.title,
this.subTitle,
this.thumbnail,
this.videoId, this.videoId,
this.playlistId, this.title,
this.subtitle,
this.coverUrl,
this.url, this.url,
this.localPath,
this.musicType,
this.playlistId,
this.browseId,
this.params,
this.isLove = false,
this.progress = 0.0,
this.taskStatus,
this.cancelToken,
}); });
MusicModel copyWith({ MusicModel copyWith({
String? title,
String? subTitle,
String? thumbnail,
String? url,
String? videoId, String? videoId,
String? title,
String? subtitle,
String? coverUrl,
String? url,
String? localPath,
String? musicType,
String? playlistId, String? playlistId,
String? browseId,
String? params,
bool? isLove,
double? progress,
TaskStatus? taskStatus,
CancelToken? cancelToken,
}) => }) =>
MusicModel( MusicModel(
title: title ?? this.title,
subTitle: subTitle ?? this.subTitle,
thumbnail: thumbnail ?? this.thumbnail,
url: url ?? this.url,
videoId: videoId ?? this.videoId, videoId: videoId ?? this.videoId,
title: title ?? this.title,
subtitle: subtitle ?? this.subtitle,
coverUrl: coverUrl ?? this.coverUrl,
url: url ?? this.url,
localPath: localPath ?? this.localPath,
musicType: musicType ?? this.musicType,
playlistId: playlistId ?? this.playlistId, playlistId: playlistId ?? this.playlistId,
browseId: browseId ?? this.browseId,
params: params ?? this.params,
isLove: isLove ?? this.isLove,
progress: progress ?? this.progress,
taskStatus: taskStatus ?? this.taskStatus,
cancelToken: cancelToken ?? this.cancelToken,
); );
factory MusicModel.fromJson(String str) => factory MusicModel.fromJson(String str) =>
@ -55,20 +97,36 @@ class MusicModel extends HiveObject {
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
factory MusicModel.fromMap(Map<String, dynamic> json) => MusicModel( factory MusicModel.fromMap(Map<String, dynamic> json) => MusicModel(
title: json["title"],
subTitle: json["subTitle"],
thumbnail: json["thumbnail"],
videoId: json["videoId"], videoId: json["videoId"],
playlistId: json["playlistId"], title: json["title"],
subtitle: json["subtitle"],
coverUrl: json["coverUrl"],
url: json["url"], url: json["url"],
localPath: json["localPath"],
musicType: json["musicType"],
playlistId: json["playlistId"],
browseId: json["browseId"],
params: json["params"],
isLove: json["isLove"] ?? false,
progress: json["progress"] ?? 0.0,
taskStatus: json["taskStatus"],
cancelToken: json["cancelToken"],
); );
Map<String, dynamic> toMap() => { Map<String, dynamic> toMap() => {
"title": title,
"subTitle": subTitle,
"thumbnail": thumbnail,
"videoId": videoId, "videoId": videoId,
"playlistId": playlistId, "title": title,
"subTitle": subtitle,
"coverUrl": coverUrl,
"url": url, "url": url,
"localPath": localPath,
"musicType": musicType,
"playlistId": playlistId,
"browseId": browseId,
"params": params,
"isLove": isLove,
"progress": progress,
"taskStatus": taskStatus,
"cancelToken": cancelToken,
}; };
} }

View File

@ -17,31 +17,40 @@ class MusicModelAdapter extends TypeAdapter<MusicModel> {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
}; };
return MusicModel( return MusicModel(
title: fields[0] as String?, videoId: fields[0] as String?,
subTitle: fields[1] as String?, title: fields[1] as String?,
thumbnail: fields[2] as String?, subtitle: fields[2] as String?,
videoId: fields[3] as String?, coverUrl: fields[3] as String?,
playlistId: fields[4] as String?, url: fields[4] as String?,
url: fields[5] as String?, localPath: fields[5] as String?,
musicType: fields[6] as String?,
playlistId: fields[7] as String?,
browseId: fields[8] as String?,
); );
} }
@override @override
void write(BinaryWriter writer, MusicModel obj) { void write(BinaryWriter writer, MusicModel obj) {
writer writer
..writeByte(6) ..writeByte(9)
..writeByte(0) ..writeByte(0)
..write(obj.title)
..writeByte(1)
..write(obj.subTitle)
..writeByte(2)
..write(obj.thumbnail)
..writeByte(3)
..write(obj.videoId) ..write(obj.videoId)
..writeByte(1)
..write(obj.title)
..writeByte(2)
..write(obj.subtitle)
..writeByte(3)
..write(obj.coverUrl)
..writeByte(4) ..writeByte(4)
..write(obj.playlistId) ..write(obj.url)
..writeByte(5) ..writeByte(5)
..write(obj.url); ..write(obj.localPath)
..writeByte(6)
..write(obj.musicType)
..writeByte(7)
..write(obj.playlistId)
..writeByte(8)
..write(obj.browseId);
} }
@override @override

View File

@ -0,0 +1,85 @@
// Author: fengshengxiong
// Date: 2024/6/24
// Description:
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/music_model.dart';
part 'playlist_model.g.dart';
@HiveType(typeId: 3)
class PlaylistModel extends HiveObject {
@HiveField(0)
String id;
@HiveField(1)
String title;
/// playlists
@HiveField(2)
int? milliseconds;
@HiveField(3)
List<MusicModel>? musicList;
/// collect_playlists
@HiveField(4)
String? params;
@HiveField(5)
String? coverUrl;
@HiveField(6)
String? subtitle;
PlaylistModel({
required this.id,
required this.title,
this.milliseconds,
this.musicList,
this.params,
this.coverUrl,
this.subtitle,
});
PlaylistModel copyWith({
required String id,
required String title,
int? milliseconds,
List<MusicModel>? musicList,
String? params,
String? coverUrl,
String? subtitle,
}) =>
PlaylistModel(
id: id,
title: title,
milliseconds: milliseconds ?? this.milliseconds,
musicList: musicList ?? this.musicList,
params: params ?? this.params,
coverUrl: coverUrl ?? this.coverUrl,
subtitle: subtitle ?? this.subtitle,
);
factory PlaylistModel.fromJson(String str) => PlaylistModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory PlaylistModel.fromMap(Map<String, dynamic> json) => PlaylistModel(
id: json["id"],
title: json["title"],
milliseconds: json["milliseconds"],
musicList: json["musicList"],
params: json["params"],
coverUrl: json["coverUrl"],
subtitle: json["subtitle"],
);
Map<String, dynamic> toMap() => {
"id": id,
"title": title,
"milliseconds": milliseconds,
"musicList": musicList,
"params": params,
"coverUrl": coverUrl,
"subtitle": subtitle,
};
}

View File

@ -0,0 +1,59 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'playlist_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class PlaylistModelAdapter extends TypeAdapter<PlaylistModel> {
@override
final int typeId = 3;
@override
PlaylistModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return PlaylistModel(
id: fields[0] as String,
title: fields[1] as String,
milliseconds: fields[2] as int?,
musicList: (fields[3] as List?)?.cast<MusicModel>(),
params: fields[4] as String?,
coverUrl: fields[5] as String?,
subtitle: fields[6] as String?,
);
}
@override
void write(BinaryWriter writer, PlaylistModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.title)
..writeByte(2)
..write(obj.milliseconds)
..writeByte(3)
..write(obj.musicList)
..writeByte(4)
..write(obj.params)
..writeByte(5)
..write(obj.coverUrl)
..writeByte(6)
..write(obj.subtitle);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PlaylistModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
// Author: fengshengxiong
// Date: 2024/7/28
// Description: TabBar模型
import 'dart:convert';
import 'package:tone_snap/data/models/music_model.dart';
class SearchResultTabBarModel {
String? title;
String? params;
String? uniqueId;
List<MusicModel>? musicList;
SearchResultTabBarModel({
this.title,
this.params,
this.uniqueId,
this.musicList,
});
SearchResultTabBarModel copyWith({
String? title,
String? params,
String? uniqueId,
List<MusicModel>? musicList,
}) =>
SearchResultTabBarModel(
title: title ?? this.title,
params: params ?? this.params,
uniqueId: uniqueId ?? this.uniqueId,
musicList: musicList ?? this.musicList,
);
factory SearchResultTabBarModel.fromJson(String str) => SearchResultTabBarModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchResultTabBarModel.fromMap(Map<String, dynamic> json) => SearchResultTabBarModel(
title: json["title"],
params: json["params"],
uniqueId: json["uniqueId"],
musicList: json["musicList"] == null ? <MusicModel>[] : List<MusicModel>.from(json["musicList"]!.map((x) => MusicModel.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"title": title,
"params": params,
"uniqueId": uniqueId,
"musicList": musicList == null ? [] : List<dynamic>.from(musicList!.map((x) => x.toMap())),
};
}

View File

@ -0,0 +1,305 @@
// Author: fengshengxiong
// Date: 2024/7/28
// Description:
import 'dart:convert';
class SearchSuggestionsModel {
ResponseContext? responseContext;
List<SearchSuggestionsModelContent>? contents;
String? trackingParams;
SearchSuggestionsModel({
this.responseContext,
this.contents,
this.trackingParams,
});
factory SearchSuggestionsModel.fromJson(String str) => SearchSuggestionsModel.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchSuggestionsModel.fromMap(Map<String, dynamic> json) => SearchSuggestionsModel(
responseContext: json["responseContext"] == null ? null : ResponseContext.fromMap(json["responseContext"]),
contents: json["contents"] == null ? [] : List<SearchSuggestionsModelContent>.from(json["contents"]!.map((x) => SearchSuggestionsModelContent.fromMap(x))),
trackingParams: json["trackingParams"],
);
Map<String, dynamic> toMap() => {
"responseContext": responseContext?.toMap(),
"contents": contents == null ? [] : List<dynamic>.from(contents!.map((x) => x.toMap())),
"trackingParams": trackingParams,
};
}
class SearchSuggestionsModelContent {
SearchSuggestionsSectionRenderer? searchSuggestionsSectionRenderer;
SearchSuggestionsModelContent({
this.searchSuggestionsSectionRenderer,
});
factory SearchSuggestionsModelContent.fromJson(String str) => SearchSuggestionsModelContent.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchSuggestionsModelContent.fromMap(Map<String, dynamic> json) => SearchSuggestionsModelContent(
searchSuggestionsSectionRenderer: json["searchSuggestionsSectionRenderer"] == null ? null : SearchSuggestionsSectionRenderer.fromMap(json["searchSuggestionsSectionRenderer"]),
);
Map<String, dynamic> toMap() => {
"searchSuggestionsSectionRenderer": searchSuggestionsSectionRenderer?.toMap(),
};
}
class SearchSuggestionsSectionRenderer {
List<SearchSuggestionsSectionRendererContent>? contents;
SearchSuggestionsSectionRenderer({
this.contents,
});
factory SearchSuggestionsSectionRenderer.fromJson(String str) => SearchSuggestionsSectionRenderer.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchSuggestionsSectionRenderer.fromMap(Map<String, dynamic> json) => SearchSuggestionsSectionRenderer(
contents: json["contents"] == null ? [] : List<SearchSuggestionsSectionRendererContent>.from(json["contents"]!.map((x) => SearchSuggestionsSectionRendererContent.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"contents": contents == null ? [] : List<dynamic>.from(contents!.map((x) => x.toMap())),
};
}
class SearchSuggestionsSectionRendererContent {
SearchSuggestionRenderer? searchSuggestionRenderer;
SearchSuggestionsSectionRendererContent({
this.searchSuggestionRenderer,
});
factory SearchSuggestionsSectionRendererContent.fromJson(String str) => SearchSuggestionsSectionRendererContent.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchSuggestionsSectionRendererContent.fromMap(Map<String, dynamic> json) => SearchSuggestionsSectionRendererContent(
searchSuggestionRenderer: json["searchSuggestionRenderer"] == null ? null : SearchSuggestionRenderer.fromMap(json["searchSuggestionRenderer"]),
);
Map<String, dynamic> toMap() => {
"searchSuggestionRenderer": searchSuggestionRenderer?.toMap(),
};
}
class SearchSuggestionRenderer {
Suggestion? suggestion;
NavigationEndpoint? navigationEndpoint;
String? trackingParams;
Icon? icon;
SearchSuggestionRenderer({
this.suggestion,
this.navigationEndpoint,
this.trackingParams,
this.icon,
});
factory SearchSuggestionRenderer.fromJson(String str) => SearchSuggestionRenderer.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchSuggestionRenderer.fromMap(Map<String, dynamic> json) => SearchSuggestionRenderer(
suggestion: json["suggestion"] == null ? null : Suggestion.fromMap(json["suggestion"]),
navigationEndpoint: json["navigationEndpoint"] == null ? null : NavigationEndpoint.fromMap(json["navigationEndpoint"]),
trackingParams: json["trackingParams"],
icon: json["icon"] == null ? null : Icon.fromMap(json["icon"]),
);
Map<String, dynamic> toMap() => {
"suggestion": suggestion?.toMap(),
"navigationEndpoint": navigationEndpoint?.toMap(),
"trackingParams": trackingParams,
"icon": icon?.toMap(),
};
}
class Icon {
String? iconType;
Icon({
this.iconType,
});
factory Icon.fromJson(String str) => Icon.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Icon.fromMap(Map<String, dynamic> json) => Icon(
iconType: json["iconType"],
);
Map<String, dynamic> toMap() => {
"iconType": iconType,
};
}
class NavigationEndpoint {
String? clickTrackingParams;
SearchEndpoint? searchEndpoint;
NavigationEndpoint({
this.clickTrackingParams,
this.searchEndpoint,
});
factory NavigationEndpoint.fromJson(String str) => NavigationEndpoint.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory NavigationEndpoint.fromMap(Map<String, dynamic> json) => NavigationEndpoint(
clickTrackingParams: json["clickTrackingParams"],
searchEndpoint: json["searchEndpoint"] == null ? null : SearchEndpoint.fromMap(json["searchEndpoint"]),
);
Map<String, dynamic> toMap() => {
"clickTrackingParams": clickTrackingParams,
"searchEndpoint": searchEndpoint?.toMap(),
};
}
class SearchEndpoint {
String? query;
SearchEndpoint({
this.query,
});
factory SearchEndpoint.fromJson(String str) => SearchEndpoint.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory SearchEndpoint.fromMap(Map<String, dynamic> json) => SearchEndpoint(
query: json["query"],
);
Map<String, dynamic> toMap() => {
"query": query,
};
}
class Suggestion {
List<Run>? runs;
Suggestion({
this.runs,
});
factory Suggestion.fromJson(String str) => Suggestion.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Suggestion.fromMap(Map<String, dynamic> json) => Suggestion(
runs: json["runs"] == null ? [] : List<Run>.from(json["runs"]!.map((x) => Run.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"runs": runs == null ? [] : List<dynamic>.from(runs!.map((x) => x.toMap())),
};
}
class Run {
String? text;
bool? bold;
Run({
this.text,
this.bold,
});
factory Run.fromJson(String str) => Run.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Run.fromMap(Map<String, dynamic> json) => Run(
text: json["text"],
bold: json["bold"],
);
Map<String, dynamic> toMap() => {
"text": text,
"bold": bold,
};
}
class ResponseContext {
String? visitorData;
List<ServiceTrackingParam>? serviceTrackingParams;
ResponseContext({
this.visitorData,
this.serviceTrackingParams,
});
factory ResponseContext.fromJson(String str) => ResponseContext.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory ResponseContext.fromMap(Map<String, dynamic> json) => ResponseContext(
visitorData: json["visitorData"],
serviceTrackingParams: json["serviceTrackingParams"] == null ? [] : List<ServiceTrackingParam>.from(json["serviceTrackingParams"]!.map((x) => ServiceTrackingParam.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"visitorData": visitorData,
"serviceTrackingParams": serviceTrackingParams == null ? [] : List<dynamic>.from(serviceTrackingParams!.map((x) => x.toMap())),
};
}
class ServiceTrackingParam {
String? service;
List<Param>? params;
ServiceTrackingParam({
this.service,
this.params,
});
factory ServiceTrackingParam.fromJson(String str) => ServiceTrackingParam.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory ServiceTrackingParam.fromMap(Map<String, dynamic> json) => ServiceTrackingParam(
service: json["service"],
params: json["params"] == null ? [] : List<Param>.from(json["params"]!.map((x) => Param.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"service": service,
"params": params == null ? [] : List<dynamic>.from(params!.map((x) => x.toMap())),
};
}
class Param {
String? key;
String? value;
Param({
this.key,
this.value,
});
factory Param.fromJson(String str) => Param.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory Param.fromMap(Map<String, dynamic> json) => Param(
key: json["key"],
value: json["value"],
);
Map<String, dynamic> toMap() => {
"key": key,
"value": value,
};
}

View File

@ -44,8 +44,8 @@ class DioClient {
_dio = Dio(); _dio = Dio();
final baseOptions = BaseOptions( final baseOptions = BaseOptions(
baseUrl: MusicApi.baseUrl, baseUrl: MusicApi.baseUrl,
connectTimeout: const Duration(seconds: 15), connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 30),
); );
_dio.options = baseOptions; _dio.options = baseOptions;
_dio.interceptors.add(DioInterceptor()); _dio.interceptors.add(DioInterceptor());
@ -103,6 +103,7 @@ class DioClient {
Future<void> download( Future<void> download(
String urlPath, String urlPath,
dynamic savePath, { dynamic savePath, {
bool showToast = false,
ProgressCallback? onReceiveProgress, ProgressCallback? onReceiveProgress,
Map<String, dynamic>? queryParameters, Map<String, dynamic>? queryParameters,
CancelToken? cancelToken, CancelToken? cancelToken,
@ -125,7 +126,7 @@ class DioClient {
} catch (e) { } catch (e) {
BaseError baseError = getError(e); BaseError baseError = getError(e);
if (fail != null) fail(baseError); if (fail != null) fail(baseError);
BaseEasyLoading.toast(baseError.message); BaseEasyLoading.toast(baseError.message, show: showToast);
LogUtil.e(baseError.message); LogUtil.e(baseError.message);
} }
} }
@ -134,19 +135,19 @@ class DioClient {
if (e.runtimeType == DioException) { if (e.runtimeType == DioException) {
switch ((e as DioException).type) { switch ((e as DioException).type) {
case DioExceptionType.connectionTimeout: case DioExceptionType.connectionTimeout:
return OtherError(statusCode: -1, statusMessage: 'connection timed out'); return OtherError(statusCode: DioExceptionType.connectionTimeout.index, statusMessage: 'Connection timed out');
case DioExceptionType.sendTimeout: case DioExceptionType.sendTimeout:
return OtherError(statusCode: -1, statusMessage: 'send timeout'); return OtherError(statusCode: DioExceptionType.sendTimeout.index, statusMessage: 'Send timeout');
case DioExceptionType.receiveTimeout: case DioExceptionType.receiveTimeout:
return OtherError(statusCode: -1, statusMessage: 'receive timeout'); return OtherError(statusCode: DioExceptionType.receiveTimeout.index, statusMessage: 'Receive timeout');
case DioExceptionType.badCertificate: case DioExceptionType.badCertificate:
return OtherError(statusCode: -1, statusMessage: 'certificate error'); return OtherError(statusCode: DioExceptionType.badCertificate.index, statusMessage: 'Certificate error');
case DioExceptionType.cancel: case DioExceptionType.cancel:
return OtherError(statusCode: -1, statusMessage: 'request canceled'); return OtherError(statusCode: DioExceptionType.cancel.index, statusMessage: 'Request canceled');
case DioExceptionType.connectionError: case DioExceptionType.connectionError:
return OtherError(statusCode: -1, statusMessage: 'connection error'); return OtherError(statusCode: DioExceptionType.connectionError.index, statusMessage: 'Connection error');
case DioExceptionType.unknown: case DioExceptionType.unknown:
return OtherError(statusCode: -1, statusMessage: 'unknown error'); return OtherError(statusCode: DioExceptionType.unknown.index, statusMessage: 'Unknown error');
case DioExceptionType.badResponse: case DioExceptionType.badResponse:
final response = e.response; final response = e.response;
if (response!.statusCode == 401) { if (response!.statusCode == 401) {
@ -161,7 +162,7 @@ class DioClient {
} }
} }
} }
return OtherError(statusCode: -1, statusMessage: 'unknown error'); return OtherError(statusCode: -1, statusMessage: 'Unknown error');
} }
} }

View File

@ -0,0 +1,59 @@
// Author: fengshengxiong
// Date: 2024/5/8
// Description:
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/playlist_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart';
class CollectPlaylistsBox {
CollectPlaylistsBox._();
static final CollectPlaylistsBox _instance = CollectPlaylistsBox._();
factory CollectPlaylistsBox() {
return _instance;
}
///
/// , main函数中这个盒子已经打开,
final _box = Hive.box<PlaylistModel>(collectPlaylistsBox);
///
List<PlaylistModel> getList() {
return _box.values.toList();
}
///
List<PlaylistModel> getReversedList() {
return _box.values.toList().reversed.toList();
}
///
Future<int> add({required String id, required String title, String? params, String? coverUrl, String? subtitle}) async {
return await _box.add(PlaylistModel(
id: id,
title: title,
params: params,
coverUrl: coverUrl,
subtitle: subtitle,
));
}
///
Future<void> delete(String id) async {
var list = getList();
var model = list.firstWhereOrNull((e) => e.id == id);
if (model != null) {
await _box.deleteAt(list.indexOf(model));
await _box.flush();
}
}
///
Future<void> clear() async {
await _box.clear();
await _box.flush();
}
}

View File

@ -1,7 +1,8 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/5/8 // Date: 2024/5/8
// Description: // Description:
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/voice_model.dart'; import 'package:tone_snap/data/models/voice_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart'; import 'package:tone_snap/data/storage/hive_storage.dart';
@ -19,7 +20,7 @@ class FavoriteBox {
/// ///
/// , main函数中这个盒子已经打开, /// , main函数中这个盒子已经打开,
final _box = getFavoriteBox(); final _box = Hive.box<VoiceModel>(favoriteBox);
/// ///
List<VoiceModel> getList() { List<VoiceModel> getList() {

View File

@ -5,12 +5,16 @@
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:tone_snap/data/enum/play_mode.dart'; import 'package:tone_snap/data/enum/play_mode.dart';
import 'package:tone_snap/data/models/music_model.dart'; import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/models/playlist_model.dart';
import 'package:tone_snap/data/models/voice_model.dart'; import 'package:tone_snap/data/models/voice_model.dart';
const myVoiceBox = 'myVoiceBox'; const myVoiceBox = 'myVoiceBox';
const favoriteBox = 'favoriteBox'; const favoriteBox = 'favoriteBox';
const musicBox = 'musicBox'; const musicBox = 'musicBox';
const loveSongsBox = 'loveSongsBox'; const loveSongsBox = 'loveSongsBox';
const offlineBox = 'offlineBox';
const playlistsBox = 'playlistsBox';
const collectPlaylistsBox = 'collectPlaylistsBox';
Future initHive() async { Future initHive() async {
// //
@ -19,25 +23,14 @@ Future initHive() async {
Hive.registerAdapter(VoiceModelAdapter()); Hive.registerAdapter(VoiceModelAdapter());
Hive.registerAdapter(PlayModeAdapter()); Hive.registerAdapter(PlayModeAdapter());
Hive.registerAdapter(MusicModelAdapter()); Hive.registerAdapter(MusicModelAdapter());
Hive.registerAdapter(PlaylistModelAdapter());
// //
await Hive.openBox<VoiceModel>(myVoiceBox); await Hive.openBox<VoiceModel>(myVoiceBox);
await Hive.openBox<VoiceModel>(favoriteBox); await Hive.openBox<VoiceModel>(favoriteBox);
await Hive.openBox(musicBox); await Hive.openBox(musicBox);
await Hive.openBox<MusicModel>(loveSongsBox); await Hive.openBox<MusicModel>(loveSongsBox);
} await Hive.openBox<MusicModel>(offlineBox);
await Hive.openBox<PlaylistModel>(playlistsBox);
Box<VoiceModel> getMyVoiceBox() { await Hive.openBox<PlaylistModel>(collectPlaylistsBox);
return Hive.box<VoiceModel>(myVoiceBox);
}
Box<VoiceModel> getFavoriteBox() {
return Hive.box<VoiceModel>(favoriteBox);
}
Box getMusicBox() {
return Hive.box(musicBox);
}
Box<MusicModel> getLoveSongsBox() {
return Hive.box<MusicModel>(loveSongsBox);
} }

View File

@ -1,8 +1,9 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/5/8 // Date: 2024/5/8
// Description: // Description:
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/music_model.dart'; import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart'; import 'package:tone_snap/data/storage/hive_storage.dart';
@ -17,22 +18,21 @@ class LoveSongsBox {
/// ///
/// , main函数中这个盒子已经打开, /// , main函数中这个盒子已经打开,
final _box = getLoveSongsBox(); final _box = Hive.box<MusicModel>(loveSongsBox);
/// ///
List<MusicModel> getList() { List<MusicModel> getList() {
return _box.values.toList(); return _box.values.toList();
} }
/// ///
Future<int> addData(MusicModel model) async { List<MusicModel> getReversedList() {
return await _box.add(model); return _box.values.toList().reversed.toList();
} }
/// ///
bool isLove(String videoId) { Future<int> add(MusicModel model) async {
var model = getList().firstWhereOrNull((e) => e.videoId == videoId); return await _box.add(model);
return model != null;
} }
/// ///
@ -40,7 +40,7 @@ class LoveSongsBox {
var list = getList(); var list = getList();
var model = list.firstWhereOrNull((e) => e.videoId == videoId); var model = list.firstWhereOrNull((e) => e.videoId == videoId);
if (model != null) { if (model != null) {
await _box.delete(list.indexOf(model)); await _box.deleteAt(list.indexOf(model));
await _box.flush(); await _box.flush();
} }
} }
@ -50,4 +50,18 @@ class LoveSongsBox {
await _box.clear(); await _box.clear();
await _box.flush(); await _box.flush();
} }
///
String? getFirstCoverUrl() {
if (getReversedList().isNotEmpty) {
return getReversedList().first.coverUrl;
}
return null;
}
///
bool checkLove(String? videoId) {
var model = getList().firstWhereOrNull((e) => e.videoId == videoId);
return model != null;
}
} }

View File

@ -1,9 +1,16 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/5/8 // Date: 2024/5/8
// Description: // Description:
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:tone_snap/data/enum/play_mode.dart'; import 'package:tone_snap/data/enum/play_mode.dart';
import 'package:tone_snap/data/storage/hive_storage.dart'; import 'package:tone_snap/data/storage/hive_storage.dart';
import 'package:tone_snap/global/app_config.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class MusicBox { class MusicBox {
MusicBox._(); MusicBox._();
@ -14,17 +21,159 @@ class MusicBox {
return _instance; return _instance;
} }
/// RemoteConfig openStatus
final _openStatusKey = 'openStatusKey';
/// RemoteConfig dataVersion
final _dataVersionKey = 'dataVersionKey';
/// B面
final _isOpenedSideBKey = 'isOpenedSideBKey';
///
final _openAppEventDurationKey = 'openAppEventDurationKey';
/// 广
final _interstitialEventDurationKey = 'interstitialEventDurationKey';
///
final _playModeKey = 'playModeKey';
///
final _searchHistoryKey = 'searchHistoryKey';
/// ///
/// , main函数中这个盒子已经打开, /// , main函数中这个盒子已经打开,
final _box = getMusicBox(); final _box = Hive.box(musicBox);
/// openStatus
Future<void> putOpenStatus(String openStatus) async {
return await _box.put(_openStatusKey, openStatus);
}
///
Future<String> getVersionCode() async {
String? openStatus = _box.get(_openStatusKey);
String? versionCode;
if (ObjUtil.isNotEmpty(openStatus)) {
try {
versionCode = jsonDecode(openStatus!)['versionCode'];
} catch (e) {
LogUtil.e(e.toString());
}
}
final packageInfo = await PackageInfo.fromPlatform();
return ObjUtil.isEmpty(versionCode) ? packageInfo.version : versionCode!;
}
///
bool getEnter() {
String? openStatus = _box.get(_openStatusKey);
if (ObjUtil.isNotEmpty(openStatus)) {
try {
return jsonDecode(openStatus!)['enter'] ?? false;
} catch (e) {
LogUtil.e(e.toString());
}
}
return false;
}
/// dataVersion
Future<void> putDataVersion(String dataVersion) async {
return await _box.put(_dataVersionKey, dataVersion);
}
/// ClientVersion
String getClientVersion() {
String? dataVersion = _box.get(_dataVersionKey);
if (ObjUtil.isNotEmpty(dataVersion)) {
try {
if (ObjUtil.isNotEmpty(jsonDecode(dataVersion!)['ClientVersion'])) {
return jsonDecode(dataVersion)['ClientVersion'];
}
} catch (e) {
LogUtil.e(e.toString());
}
}
return AppConfig.clientVersion;
}
/// PlayerVersion
String getPlayerVersion() {
String? dataVersion = _box.get(_dataVersionKey);
if (ObjUtil.isNotEmpty(dataVersion)) {
try {
if (ObjUtil.isNotEmpty(jsonDecode(dataVersion!)['PlayerVersion'])) {
return jsonDecode(dataVersion)['PlayerVersion'];
}
} catch (e) {
LogUtil.e(e.toString());
}
}
return AppConfig.playerVersion;
}
/// B面
Future<void> putIsOpenedSideB(bool isOpen) async {
return await _box.put(_isOpenedSideBKey, isOpen);
}
/// B面
bool getIsOpenedSideB() {
return _box.get(_isOpenedSideBKey, defaultValue: false);
}
///
Future<void> putOpenAppEventDuration(int time) async {
return await _box.put(_openAppEventDurationKey, time);
}
///
int getOpenAppEventDuration() {
return _box.get(_openAppEventDurationKey, defaultValue: AppConfig.openAppEventDurationTime);
}
/// 广
Future<void> putInterstitialEventDuration(int time) async {
return await _box.put(_interstitialEventDurationKey, time);
}
/// 广
int getInterstitialEventDuration() {
return _box.get(_interstitialEventDurationKey, defaultValue: AppConfig.interstitialEventDuration);
}
/// ///
Future<void> putPlayMode(PlayMode playMode) { Future<void> putPlayMode(PlayMode playMode) async {
return _box.put('play_mode', playMode); return await _box.put(_playModeKey, playMode);
} }
/// ///
PlayMode getPlayMode() { PlayMode getPlayMode() {
return _box.get('play_mode') ?? PlayMode.listLoop; return _box.get(_playModeKey) ?? PlayMode.listLoop;
}
///
Future<void> putSearchHistory(String history) async {
var historyList = getAllSearchHistory();
if (!historyList.contains(history)) {
if (historyList.length >= 9) {
historyList.removeLast();
}
historyList.insert(0, history);
await _box.put(_searchHistoryKey, historyList);
}
}
///
List<String> getAllSearchHistory() {
return _box.get(_searchHistoryKey, defaultValue: <String>[]);
}
///
Future<void> deleteAllSearchHistory() async {
await _box.delete(_searchHistoryKey);
await _box.flush();
} }
} }

View File

@ -1,7 +1,8 @@
// Author: fengshengxiong // Author: fengshengxiong
// Date: 2024/5/8 // Date: 2024/5/8
// Description: // Description:
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/voice_model.dart'; import 'package:tone_snap/data/models/voice_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart'; import 'package:tone_snap/data/storage/hive_storage.dart';
@ -19,7 +20,7 @@ class MyVoiceBox {
/// ///
/// , main函数中这个盒子已经打开, /// , main函数中这个盒子已经打开,
final _box = getMyVoiceBox(); final _box = Hive.box<VoiceModel>(myVoiceBox);
/// ///
List<VoiceModel> getList() { List<VoiceModel> getList() {

View File

@ -0,0 +1,67 @@
// Author: fengshengxiong
// Date: 2024/5/8
// Description:
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart';
class OfflineBox {
OfflineBox._();
static final OfflineBox _instance = OfflineBox._();
factory OfflineBox() {
return _instance;
}
///
/// , main函数中这个盒子已经打开,
final _box = Hive.box<MusicModel>(offlineBox);
///
List<MusicModel> getList() {
return _box.values.toList();
}
///
List<MusicModel> getReversedList() {
return _box.values.toList().reversed.toList();
}
///
Future<int> add(MusicModel model) async {
return await _box.add(model);
}
///
Future<void> delete(String videoId) async {
var list = getList();
var model = list.firstWhereOrNull((e) => e.videoId == videoId);
if (model != null) {
await _box.deleteAt(list.indexOf(model));
await _box.flush();
}
}
///
Future<void> clear() async {
await _box.clear();
await _box.flush();
}
///
String? getFirstCoverUrl() {
if (getReversedList().isNotEmpty) {
return getReversedList().first.coverUrl;
}
return null;
}
///
bool checkDownloaded(String? videoId) {
var model = getList().firstWhereOrNull((e) => e.videoId == videoId);
return model != null;
}
}

View File

@ -0,0 +1,117 @@
// Author: fengshengxiong
// Date: 2024/5/8
// Description:
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:tone_snap/components/base_easyloading.dart';
import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/models/playlist_model.dart';
import 'package:tone_snap/data/storage/hive_storage.dart';
import 'package:tone_snap/utils/date_util.dart';
class PlaylistsBox {
PlaylistsBox._();
static final PlaylistsBox _instance = PlaylistsBox._();
factory PlaylistsBox() {
return _instance;
}
///
/// , main函数中这个盒子已经打开,
final _box = Hive.box<PlaylistModel>(playlistsBox);
///
List<PlaylistModel> getList() {
return _box.values.toList();
}
///
List<PlaylistModel> getReversedList() {
return _box.values.toList().reversed.toList();
}
///
Future<void> add(String title) async {
final milliseconds = DateUtil.getNowTimestamp();
final id = '$title-$milliseconds';
await _box.put(id, PlaylistModel(
id: id,
title: title,
milliseconds: milliseconds,
musicList: <MusicModel>[],
));
}
///
Future<void> delete(String id) async {
var list = getList();
final playlistModel = list.firstWhereOrNull((e) => e.id == id);
if (playlistModel != null) {
await _box.deleteAt(list.indexOf(playlistModel));
await _box.flush();
}
}
///
Future<void> clear() async {
await _box.clear();
await _box.flush();
}
PlaylistModel? getPlaylistModel(String id) {
return _box.get(id);
}
///
void editTitle(String id, String title) {
var list = getList();
final playlistModel = list.firstWhereOrNull((e) => e.id == id);
if (playlistModel != null) {
playlistModel.title = title;
_box.put(id, playlistModel);
}
}
///
Future<bool> addMusic(String id, MusicModel musicModel) async {
final playlistModel = _box.get(id);
if (playlistModel != null) {
playlistModel.musicList ??= <MusicModel>[];
if (playlistModel.musicList!.firstWhereOrNull((e) => e.videoId == musicModel.videoId) != null) {
BaseEasyLoading.toast('The current song is already in this playlist');
return false;
}
playlistModel.musicList!.insert(0, musicModel);
_box.put(id, playlistModel);
return true;
}
return false;
}
///
Future<bool> removeMusic(String id, String videoId) async {
final playlistModel = _box.get(id);
if (playlistModel != null && playlistModel.musicList != null) {
if (playlistModel.musicList!.firstWhereOrNull((e) => e.videoId == videoId) != null) {
playlistModel.musicList!.removeWhere((e) => e.videoId == videoId);
_box.put(id, playlistModel);
return true;
}
}
return false;
}
///
String? getFirstCoverUrl(String id) {
final playlistModel = _box.get(id);
if (playlistModel != null) {
if (playlistModel.musicList != null && playlistModel.musicList!.isNotEmpty) {
return playlistModel.musicList!.first.coverUrl;
}
}
return null;
}
}

View File

@ -3,14 +3,9 @@
// Description: firebase_analytics管理 // Description: firebase_analytics管理
import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
class FirebaseAnalyticsManager { class FirebaseAnalyticsManager {
/// static const homeApv = 'home_a_pv';
static Future<void> setCrashlyticsCollectionEnabled() async {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(!kDebugMode);
}
/// ///
/// name /// name

View File

@ -0,0 +1,35 @@
// Author: fengshengxiong
// Date: 2024/6/26
// Description: firebase_crashlytics管理
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:tone_snap/utils/log_util.dart';
class FirebaseCrashlyticsManager {
static Future<void> setEnabled() async {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(kReleaseMode);
}
static void recordFlutterError() {
//
FlutterError.onError = (errorDetails) {
// flutter_cache_manager
// https://github.com/Baseflow/flutter_cache_manager/issues/460
if (errorDetails.exceptionAsString().contains('No host specified in URI')) {
return;
}
LogUtil.e(errorDetails.exception);
FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
};
}
static void recordError() {
//
PlatformDispatcher.instance.onError = (error, stack) {
LogUtil.e(error);
FirebaseCrashlytics.instance.recordError(error, stack);
return true;
};
}
}

View File

@ -0,0 +1,69 @@
// Author: fengshengxiong
// Date: 2024/6/26
// Description: firebase_remote_config管理
import 'dart:convert';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:tone_snap/data/storage/music_box.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class FirebaseRemoteConfigManager {
static Future<void> getAll() async {
final remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(minutes: 1),
minimumFetchInterval: Duration.zero,
));
bool result = await remoteConfig.fetchAndActivate();
if (result) {
Map<String, RemoteConfigValue> allData = remoteConfig.getAll();
try {
if (allData.isNotEmpty) {
LogUtil.d('远程配置获取成功');
// openStatus
var openStatus = allData['openStatus']?.asString();
if (ObjUtil.isNotEmpty(openStatus)) {
await MusicBox().putOpenStatus(openStatus!);
}
// dataVersion
var dataVersion = allData['dataVersion']?.asString();
if (ObjUtil.isNotEmpty(dataVersion)) {
await MusicBox().putDataVersion(dataVersion!);
}
// openAppEventDuration
var openAppEventDuration = allData['openAppEventDuration']?.asString();
if (ObjUtil.isNotEmpty(openAppEventDuration)) {
try {
var times = jsonDecode(openAppEventDuration!)['times'];
if (ObjUtil.isNotEmpty(times)) {
await MusicBox().putOpenAppEventDuration(times);
}
} catch (e) {
LogUtil.e(e.toString());
}
}
// interstitialEventDuration
var interstitialEventDuration = allData['interstitialEventDuration']?.asString();
if (ObjUtil.isNotEmpty(interstitialEventDuration)) {
try {
var times = jsonDecode(interstitialEventDuration!)['times'];
if (ObjUtil.isNotEmpty(times)) {
await MusicBox().putInterstitialEventDuration(times);
}
} catch (e) {
LogUtil.e(e.toString());
}
}
}
} catch (e) {
LogUtil.e(e.toString());
}
}
}
}

View File

@ -44,7 +44,7 @@ class Assets {
static const String sideAHomeBg = 'assets/images/side_a/home_bg.png'; static const String sideAHomeBg = 'assets/images/side_a/home_bg.png';
static const String sideAHomeBnbBg = 'assets/images/side_a/home_bnb_bg.png'; static const String sideAHomeBnbBg = 'assets/images/side_a/home_bnb_bg.png';
static const String sideAIconChevronRight = 'assets/images/side_a/icon_chevron_right.png'; static const String sideAIconChevronRight = 'assets/images/side_a/icon_chevron_right.png';
static const String sideALaunchImage = 'assets/images/side_a/launch_image.png'; static const String sideALaunchBg = 'assets/images/side_a/launch_bg.png';
static const String sideAMore = 'assets/images/side_a/more.png'; static const String sideAMore = 'assets/images/side_a/more.png';
static const String sideANotFavorite = 'assets/images/side_a/not_favorite.png'; static const String sideANotFavorite = 'assets/images/side_a/not_favorite.png';
static const String sideANotPlayed = 'assets/images/side_a/not_played.png'; static const String sideANotPlayed = 'assets/images/side_a/not_played.png';
@ -63,8 +63,8 @@ class Assets {
static const String sideAUploadRecordSound = 'assets/images/side_a/upload_record_sound.png'; static const String sideAUploadRecordSound = 'assets/images/side_a/upload_record_sound.png';
static const String sideAUserAgreement = 'assets/images/side_a/user_agreement.png'; static const String sideAUserAgreement = 'assets/images/side_a/user_agreement.png';
static const String sideAVoiceDefault = 'assets/images/side_a/voice_default.png'; static const String sideAVoiceDefault = 'assets/images/side_a/voice_default.png';
static const String sideBAlbumTitleBg = 'assets/images/side_b/album_title_bg.png'; static const String sideBAddToPlaylist = 'assets/images/side_b/add_to_playlist.png';
static const String sideBAlbumTotal = 'assets/images/side_b/album_total.png'; static const String sideBAddToQueue = 'assets/images/side_b/add_to_queue.png';
static const String sideBArrowDownBack = 'assets/images/side_b/arrow_down_back.png'; static const String sideBArrowDownBack = 'assets/images/side_b/arrow_down_back.png';
static const String sideBArrowLeftBack = 'assets/images/side_b/arrow_left_back.png'; static const String sideBArrowLeftBack = 'assets/images/side_b/arrow_left_back.png';
static const String sideBArrowRightItem = 'assets/images/side_b/arrow_right_item.png'; static const String sideBArrowRightItem = 'assets/images/side_b/arrow_right_item.png';
@ -77,29 +77,48 @@ class Assets {
static const String sideBBnb3Unselected = 'assets/images/side_b/bnb3_unselected.png'; static const String sideBBnb3Unselected = 'assets/images/side_b/bnb3_unselected.png';
static const String sideBBnbBg = 'assets/images/side_b/bnb_bg.png'; static const String sideBBnbBg = 'assets/images/side_b/bnb_bg.png';
static const String sideBBottomSheetIndicator = 'assets/images/side_b/bottom_sheet_indicator.png'; static const String sideBBottomSheetIndicator = 'assets/images/side_b/bottom_sheet_indicator.png';
static const String sideBCollected = 'assets/images/side_b/collected.png';
static const String sideBCollectionAlbum = 'assets/images/side_b/collection_album.png'; static const String sideBCollectionAlbum = 'assets/images/side_b/collection_album.png';
static const String sideBCrossCircle = 'assets/images/side_b/cross_circle.png'; static const String sideBCrossCircle = 'assets/images/side_b/cross_circle.png';
static const String sideBDownload = 'assets/images/side_b/download.png'; static const String sideBDeleteHistory = 'assets/images/side_b/delete_history.png';
static const String sideBDeleteWhite = 'assets/images/side_b/delete_white.png';
static const String sideBDownloaded = 'assets/images/side_b/downloaded.png';
static const String sideBEmpty = 'assets/images/side_b/empty.jpg';
static const String sideBHomeBg = 'assets/images/side_b/home_bg.png'; static const String sideBHomeBg = 'assets/images/side_b/home_bg.png';
static const String sideBImgError = 'assets/images/side_b/img_error.png';
static const String sideBImgPlaceholder = 'assets/images/side_b/img_placeholder.png';
static const String sideBItemPlayer1 = 'assets/images/side_b/item_player1.png'; static const String sideBItemPlayer1 = 'assets/images/side_b/item_player1.png';
static const String sideBLineMenu = 'assets/images/side_b/line_menu.png'; static const String sideBLineMenu = 'assets/images/side_b/line_menu.png';
static const String sideBListLoop = 'assets/images/side_b/list_loop.png'; static const String sideBListLoop = 'assets/images/side_b/list_loop.png';
static const String sideBLove = 'assets/images/side_b/love.png'; static const String sideBLove = 'assets/images/side_b/love.png';
static const String sideBLoveSolid = 'assets/images/side_b/love_solid.png'; static const String sideBLoveSolid = 'assets/images/side_b/love_solid.png';
static const String sideBLoveSongsBg = 'assets/images/side_b/love_songs_bg.png';
static const String sideBMore = 'assets/images/side_b/more.png';
static const String sideBMoreEdit = 'assets/images/side_b/more_edit.png';
static const String sideBMoreRemove = 'assets/images/side_b/more_remove.png';
static const String sideBMusicBarNext = 'assets/images/side_b/music_bar_next.png';
static const String sideBMusicPlaceholder = 'assets/images/side_b/music_placeholder.png';
static const String sideBNextTrack = 'assets/images/side_b/next_track.png'; static const String sideBNextTrack = 'assets/images/side_b/next_track.png';
static const String sideBNotCollectionAlbum = 'assets/images/side_b/not_collection_album.png'; static const String sideBNotCollectionAlbum = 'assets/images/side_b/not_collection_album.png';
static const String sideBNotDownload1 = 'assets/images/side_b/not_download1.png';
static const String sideBNotDownload2 = 'assets/images/side_b/not_download2.png';
static const String sideBOfflineDownload = 'assets/images/side_b/offline_download.png'; static const String sideBOfflineDownload = 'assets/images/side_b/offline_download.png';
static const String sideBPausePlay = 'assets/images/side_b/pause_play.png'; static const String sideBPausePlay = 'assets/images/side_b/pause_play.png';
static const String sideBPersonalMusicLibraryBg = 'assets/images/side_b/personal_music_library_bg.png'; static const String sideBPersonalMusicLibraryBg = 'assets/images/side_b/personal_music_library_bg.png';
static const String sideBPlaceholderLibrary = 'assets/images/side_b/placeholder_library.png'; static const String sideBPlaceholderLibrary = 'assets/images/side_b/placeholder_library.png';
static const String sideBPlayList = 'assets/images/side_b/play_list.png'; static const String sideBPlayList = 'assets/images/side_b/play_list.png';
static const String sideBPlayListDelete = 'assets/images/side_b/play_list_delete.png'; static const String sideBPlayListDelete = 'assets/images/side_b/play_list_delete.png';
static const String sideBPlaylistPlayAll = 'assets/images/side_b/playlist_play_all.png';
static const String sideBPlaylistPlayAllRandom = 'assets/images/side_b/playlist_play_all_random.png';
static const String sideBPlaylistTitleBg = 'assets/images/side_b/playlist_title_bg.png';
static const String sideBPlaylistsAdd = 'assets/images/side_b/playlists_add.png'; static const String sideBPlaylistsAdd = 'assets/images/side_b/playlists_add.png';
static const String sideBPreviousTrack = 'assets/images/side_b/previous_track.png'; static const String sideBPreviousTrack = 'assets/images/side_b/previous_track.png';
static const String sideBPrivacyPolicy = 'assets/images/side_b/privacy_policy.png';
static const String sideBReport = 'assets/images/side_b/report.png';
static const String sideBSearch = 'assets/images/side_b/search.png';
static const String sideBSearchWhite = 'assets/images/side_b/search_white.png';
static const String sideBSettingBg = 'assets/images/side_b/setting_bg.png';
static const String sideBShufflePlayback = 'assets/images/side_b/shuffle_playback.png'; static const String sideBShufflePlayback = 'assets/images/side_b/shuffle_playback.png';
static const String sideBSingleCycle = 'assets/images/side_b/single_cycle.png'; static const String sideBSingleCycle = 'assets/images/side_b/single_cycle.png';
static const String sideBStartPlay = 'assets/images/side_b/start_play.png'; static const String sideBStartPlay = 'assets/images/side_b/start_play.png';
static const String sideBTermsOfService = 'assets/images/side_b/terms_of_service.png';
} }

View File

@ -5,13 +5,24 @@
import 'package:tone_snap/data/enum/app_side_enum.dart'; import 'package:tone_snap/data/enum/app_side_enum.dart';
class AppConfig { class AppConfig {
static const appName = 'ToneSnap'; static const String appName = 'ToneSnap';
/// App展示的一面 /// App展示的一面
static const AppSideEnum appSideEnum = AppSideEnum.sideB; static AppSideEnum appSideEnum = AppSideEnum.sideA;
/// /// /
static String defaultLocale = 'zh-CN'; static const int openAppEventDurationTime = 10;
/// 广/
static const int interstitialEventDuration = 40;
/// ClientVersion
static const String clientVersion = '1.20240618.01.00';
/// PlayerVersion
static const String playerVersion = '6.18.1';
///
static String isoCode = 'HK'; static String isoCode = 'HK';
} }

View File

@ -7,12 +7,17 @@ import 'package:tone_snap/ads/app_open_ad_manager.dart';
import 'package:tone_snap/ads/interstitial_ad_manager.dart'; import 'package:tone_snap/ads/interstitial_ad_manager.dart';
class AppLifecycleReactor { class AppLifecycleReactor {
AppLifecycleReactor(); AppLifecycleReactor._();
static final AppLifecycleReactor _instance = AppLifecycleReactor._();
factory AppLifecycleReactor() {
return _instance;
}
void listenToAppStateChanges() { void listenToAppStateChanges() {
AppStateEventNotifier.startListening(); AppStateEventNotifier.startListening();
AppStateEventNotifier.appStateStream AppStateEventNotifier.appStateStream.forEach((state) => _onAppStateChanged(state));
.forEach((state) => _onAppStateChanged(state));
} }
void _onAppStateChanged(AppState appState) { void _onAppStateChanged(AppState appState) {

View File

@ -0,0 +1,165 @@
// Author: fengshengxiong
// Date: 2024/5/10
// Description:
import 'package:background_downloader/background_downloader.dart';
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:tone_snap/components/base_easyloading.dart';
import 'package:tone_snap/data/api/music_api.dart';
import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/models/player_model.dart';
import 'package:tone_snap/data/storage/offline_box.dart';
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
import 'package:tone_snap/utils/date_util.dart';
import 'package:tone_snap/utils/local_path_util.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class DownloadManager {
static final DownloadManager _instance = DownloadManager._getInstance();
factory DownloadManager() => _instance;
static MemoryTaskQueue? tq;
List<Rx<MusicModel>> downloadList = [];
DownloadManager._getInstance() {
if (tq == null) {
tq ??= MemoryTaskQueue();
tq!.maxConcurrent = 3; // no more than 5 tasks active at any one time
tq!.maxConcurrentByHost = 3; // no more than two tasks talking to the same host at the same time
tq!.maxConcurrentByGroup = 3; // no more than three tasks from the same group active at the same time
FileDownloader().addTaskQueue(tq!); // 'connects' the TaskQueue to the FileDownloader
FileDownloader().updates.listen((update) async { // listen to updates as per usual
Rx<MusicModel>? musicModel = downloadList.firstWhereOrNull((e) => e.value.videoId == update.task.taskId);
if (musicModel == null) return;
if (update.runtimeType == TaskStatusUpdate) {
TaskStatus taskStatus = (update as TaskStatusUpdate).status;
LogUtil.d('${update.task.filename},任务状态: $taskStatus');
musicModel.update((fn) => fn?.taskStatus = taskStatus);
switch (taskStatus) {
case TaskStatus.enqueued:
break;
case TaskStatus.running:
break;
case TaskStatus.complete:
LogUtil.d('音乐下载路径:${await update.task.filePath()}');
musicModel.value.localPath = await update.task.filePath();
OfflineBox().add(musicModel.value.copyWith());
downloadList.remove(musicModel);
BaseEasyLoading.toast('Download completed');
if (Get.isRegistered<PersonalMusicLibraryController>()) {
PersonalMusicLibraryController.to.refreshOffline();
}
if (Get.isRegistered<OfflineController>()) {
OfflineController.to.getOfflineList();
}
break;
case TaskStatus.notFound:
BaseEasyLoading.toast('Download failed');
downloadList.remove(musicModel);
break;
case TaskStatus.failed:
BaseEasyLoading.toast('Download failed');
downloadList.remove(musicModel);
break;
case TaskStatus.canceled:
BaseEasyLoading.toast('Download cancelled');
downloadList.remove(musicModel);
break;
case TaskStatus.waitingToRetry:
break;
case TaskStatus.paused:
break;
}
}
if (update.runtimeType == TaskProgressUpdate) {
LogUtil.d('${update.task.filename},下载进度: $update');
musicModel.update((fn) => fn?.progress = (update as TaskProgressUpdate).progress);
}
});
}
}
///
void downloadMusic(Rx<MusicModel>? musicModel) {
if (musicModel == null) return;
musicModel.update((fn) {
fn?.taskStatus = TaskStatus.enqueued;
fn?.cancelToken = CancelToken();
});
_getMusicUrl(musicModel, (url, mimeType) async {
if (ObjUtil.isEmpty(url)) {
BaseEasyLoading.toast('Resource acquisition failed');
musicModel.update((fn) => fn?.taskStatus = TaskStatus.failed);
return;
}
String extension = mimeType ?? 'mp4';
if (ObjUtil.isNotEmpty(mimeType)) {
// mimeType
String type = mimeType!.split(';')[0].trim();
//
extension = type.split('/')[1];
}
final filename = '${musicModel.value.title}_${DateUtil.getNowTimestamp()}.$extension';
final task = DownloadTask(
taskId: musicModel.value.videoId,
url: url!,
filename: filename,
directory: LocalPathUtil.getMusicDownloadDir(),
updates: Updates.statusAndProgress,
requiresWiFi: false,
retries: 0,
allowPause: false,
metaData: '',
);
tq?.add(task);
downloadList.add(musicModel);
});
}
Future<void> _getMusicUrl(Rx<MusicModel> musicModel, Function(String? url, String? mimeType) onTap) async {
PlayerModel? playerModel = await MusicApi.player(
videoId: musicModel.value.videoId,
cancelToken: musicModel.value.cancelToken,
fail: (baseError) {
if (baseError.code == DioExceptionType.cancel.index) {
musicModel.update((fn) {
fn?.taskStatus = TaskStatus.canceled;
fn?.cancelToken = null;
});
} else {
musicModel.update((fn) {
fn?.taskStatus = TaskStatus.failed;
fn?.cancelToken = null;
});
}
}
);
if (playerModel != null) {
if (ObjUtil.isEmpty(musicModel.value.coverUrl)) {
var thumbnails = playerModel.videoDetails?.thumbnail?.thumbnails;
if (thumbnails != null && thumbnails.isNotEmpty) {
musicModel.value.coverUrl = thumbnails[0].url;
}
}
if (ObjUtil.isEmpty(musicModel.value.musicType)) {
musicModel.value.musicType = playerModel.videoDetails?.musicVideoType;
}
var formats = playerModel.streamingData?.formats;
if (formats != null && formats.isNotEmpty) {
onTap(formats[0].url, formats[0].mimeType);
}
}
}
void cancelDownload(Rx<MusicModel>? musicModel) {
if (musicModel == null || musicModel.value.videoId == null) return;
musicModel.value.cancelToken?.cancel();
FileDownloader().cancelTaskWithId(musicModel.value.videoId!);
}
}

View File

@ -0,0 +1,44 @@
// Author: fengshengxiong
// Date: 2024/7/17
// Description:
import 'dart:collection';
class DownloadQueueTask {
int maxThread = 1;
DownloadQueueTask(this.maxThread);
///
final Queue<_TaskInfo> _queue = Queue();
///
int _taskCount = 0;
void create(String taskName, Function(String name) workTask) {
_queue.add(_TaskInfo(taskName, workTask));
_exec();
}
void _exec() async {
if (_taskCount >= maxThread) return;
if (_queue.isEmpty) return;
for (int i = 0; i < maxThread; i++) {
if (_queue.isEmpty) continue;
_TaskInfo taskInfo = _queue.removeFirst();
_taskCount += 1;
await taskInfo.workTask.call(taskInfo.taskName);
_taskCount -= 1;
}
_exec();
}
}
class _TaskInfo {
String taskName;
Function(String taskName) workTask;
_TaskInfo(this.taskName, this.workTask);
}

View File

@ -8,22 +8,34 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tone_snap/ads/app_open_ad_manager.dart'; import 'package:tone_snap/ads/app_open_ad_manager.dart';
import 'package:tone_snap/ads/interstitial_ad_manager.dart'; import 'package:tone_snap/ads/interstitial_ad_manager.dart';
import 'package:tone_snap/firebase/firebase_remote_config_manager.dart';
import 'package:tone_snap/modules/launch/launch_controller.dart';
import 'package:tone_snap/utils/log_util.dart'; import 'package:tone_snap/utils/log_util.dart';
class NetworkConnectivityService extends GetxService { class NetworkConnectivityService extends GetxService {
StreamSubscription<List<ConnectivityResult>>? subscription; StreamSubscription<List<ConnectivityResult>>? subscription;
List<ConnectivityResult> recordResult = []; ///
var isExecutedTask = false;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
subscription = Connectivity().onConnectivityChanged.listen((List<ConnectivityResult> result) { subscription = Connectivity().onConnectivityChanged.listen((List<ConnectivityResult> result) {
LogUtil.d('网络连接类型变化:$result'); LogUtil.d('当前网络连接类型:$result');
if (recordResult.contains(ConnectivityResult.none) && !result.contains(ConnectivityResult.none)) { if (result.contains(ConnectivityResult.wifi) || result.contains(ConnectivityResult.mobile)) {
AppOpenAdManager().loadAd(); if (!isExecutedTask) {
InterstitialAdManager().loadAd(); isExecutedTask = true;
FirebaseRemoteConfigManager.getAll();
if (Get.isRegistered<LaunchController>()) {
LaunchController.to.getIsoCode();
}
InterstitialAdManager().loadAd();
AppOpenAdManager().loadAd();
}
} }
recordResult = result;
}); });
} }

View File

@ -1,23 +1,22 @@
import 'dart:io'; import 'dart:io';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:tone_snap/components/base_easyloading.dart'; import 'package:tone_snap/components/base_easyloading.dart';
import 'package:tone_snap/components/music_bar.dart';
import 'package:tone_snap/components/music_bar/music_bar_controller.dart';
import 'package:tone_snap/data/enum/app_side_enum.dart';
import 'package:tone_snap/data/storage/hive_storage.dart'; import 'package:tone_snap/data/storage/hive_storage.dart';
import 'package:tone_snap/firebase/firebase_crashlytics_manager.dart';
import 'package:tone_snap/firebase/firebase_options.dart'; import 'package:tone_snap/firebase/firebase_options.dart';
import 'package:tone_snap/global/app_config.dart'; import 'package:tone_snap/global/app_config.dart';
import 'package:tone_snap/global/network_connectivity_service.dart'; import 'package:tone_snap/modules/sideb/controllers/main_controller.dart';
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart'; import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
import 'package:tone_snap/modules/sideb/music_bar/music_bar.dart';
import 'package:tone_snap/modules/sideb/music_bar/music_bar_controller.dart';
import 'package:tone_snap/res/themes/app_themes.dart'; import 'package:tone_snap/res/themes/app_themes.dart';
import 'package:tone_snap/routes/app_pages.dart'; import 'package:tone_snap/routes/app_pages.dart';
import 'package:tone_snap/routes/app_routes.dart'; import 'package:tone_snap/routes/app_routes.dart';
@ -28,34 +27,19 @@ import 'package:tone_snap/utils/log_util.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isIOS) { // Firebase
// Firebase try {
try { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await Firebase.initializeApp( await FirebaseCrashlyticsManager.setEnabled();
options: DefaultFirebaseOptions.currentPlatform, FirebaseCrashlyticsManager.recordFlutterError();
); FirebaseCrashlyticsManager.recordError();
} catch (e) { } catch (e) {
LogUtil.e("Firebase initialization error: $e"); LogUtil.e("Firebase initialization error: $e");
}
//
FlutterError.onError = (errorDetails) {
FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
};
//
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: false);
return true;
};
// 广 SDK
MobileAds.instance.initialize();
//
await Get.putAsync(() async => NetworkConnectivityService());
} }
// 广 SDK
MobileAds.instance.initialize();
// Hive // Hive
await initHive(); await initHive();
@ -87,53 +71,62 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final easyLoading = EasyLoading.init(); final easyLoading = EasyLoading.init();
ThemeData appTheme;
List<NavigatorObserver> navigatorObservers = const <NavigatorObserver>[]; List<NavigatorObserver> navigatorObservers = const <NavigatorObserver>[];
if (AppConfig.appSideEnum == AppSideEnum.sideA) { navigatorObservers = [
appTheme = sideATheme; GetObserver((_) {
} else { WidgetsBinding.instance.addPostFrameCallback((_) {
appTheme = sideBTheme; if (Get.currentRoute == AppRoutes.playPage) {
navigatorObservers = [ MusicBar().hide();
GetObserver((_) { } else {
WidgetsBinding.instance.addPostFrameCallback((_) { if (Get.isRegistered<MusicPlayerController>()) {
if (Get.currentRoute == AppRoutes.playPage) { if (MusicPlayerController.to.getMusicModel()?.value.videoId != null) {
MusicBar().hide(); if (Get.isBottomSheetOpen != null && Get.isBottomSheetOpen!) {
MusicBar().hide();
} else {
MusicBar().show();
}
}
}
}
if (Get.isRegistered<MusicBarController>()) {
if (Get.currentRoute == AppRoutes.initialB) {
MusicBarController.to.riseUp();
} else { } else {
if (Get.isRegistered<MusicPlayerController>() && MusicPlayerController.to.musicModel.value.videoId != null) { MusicBarController.to.toBottom();
MusicBar().show();
}
} }
if (Get.isRegistered<MusicBarController>()) { }
if (Get.currentRoute == AppRoutes.initialB) { });
MusicBarController.to.riseUp(); }),
} else { ];
MusicBarController.to.toBottom();
}
}
});
}),
];
}
return ScreenUtilInit( return ScreenUtilInit(
// 稿 // 稿
designSize: const Size(375, 812), designSize: const Size(375, 812),
minTextAdapt: true, minTextAdapt: true,
builder: (context, child) { builder: (context, child) {
return GetMaterialApp( return KeyboardDismissOnTap(
title: AppConfig.appName, dismissOnCapturedTaps: true,
debugShowCheckedModeBanner: false, child: GetBuilder(
theme: appTheme, id: 'changeTheme',
darkTheme: appTheme, init: MainController(),
themeMode: ThemeMode.dark, builder: (logic) {
initialRoute: AppRoutes.splash, return GetMaterialApp(
getPages: AppPages.routes, title: AppConfig.appName,
navigatorObservers: navigatorObservers, debugShowCheckedModeBanner: false,
builder: (context, widget) { theme: logic.isSideBTheme.value ? sideBTheme : sideATheme,
BaseEasyLoading.configLoading(); darkTheme: logic.isSideBTheme.value ? sideBTheme : sideATheme,
widget = easyLoading(context, widget); themeMode: ThemeMode.dark,
// initialRoute: AppRoutes.launch,
return MediaQuery.withNoTextScaling(child: widget); getPages: AppPages.routes,
}, navigatorObservers: navigatorObservers,
builder: (context, widget) {
BaseEasyLoading.configLoading();
widget = easyLoading(context, widget);
//
return MediaQuery.withNoTextScaling(child: widget);
},
);
},
),
); );
}, },
); );

View File

@ -0,0 +1,9 @@
import 'package:get/get.dart';
import 'package:tone_snap/modules/launch/launch_controller.dart';
class LaunchBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => LaunchController());
}
}

View File

@ -0,0 +1,125 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:tone_snap/ads/app_open_ad_manager.dart';
import 'package:tone_snap/data/api/tikustok_api.dart';
import 'package:tone_snap/data/enum/app_side_enum.dart';
import 'package:tone_snap/data/models/base_model.dart';
import 'package:tone_snap/data/models/isocode_model.dart';
import 'package:tone_snap/data/storage/music_box.dart';
import 'package:tone_snap/global/app_config.dart';
import 'package:tone_snap/global/app_lifecycle_reactor.dart';
import 'package:tone_snap/global/network_connectivity_service.dart';
import 'package:tone_snap/modules/sideb/controllers/main_controller.dart';
import 'package:tone_snap/res/themes/app_themes.dart';
import 'package:tone_snap/routes/app_routes.dart';
import 'package:tone_snap/utils/log_util.dart';
class LaunchController extends GetxController with GetSingleTickerProviderStateMixin {
static LaunchController get to => Get.find<LaunchController>();
Timer? _timer;
///
var timeTotal = 10 * 1000;
///
var currentProcess = 0.obs;
///
var changeValue = 10;
late AppLifecycleReactor _appLifecycleReactor;
@override
void onInit() {
super.onInit();
_appLifecycleReactor = AppLifecycleReactor();
_appLifecycleReactor.listenToAppStateChanges();
//
Get.putAsync(() async => NetworkConnectivityService());
_startTimer();
}
@override
void onClose() {
_stopTimer();
super.onClose();
}
///
void _startTimer() {
_timer = Timer.periodic(Duration(milliseconds: changeValue), (Timer t) {
if (currentProcess.value + changeValue >= timeTotal) {
currentProcess.value = timeTotal;
if (currentProcess >= timeTotal) {
_stopTimer();
_checkEnter();
return;
}
}
currentProcess.value += changeValue;
});
}
///
void _stopTimer() {
_timer?.cancel();
_timer = null;
}
///
void editChangeValue() {
changeValue = 3000;
}
/// A还是B
Future<void> _checkEnter() async {
bool isOpenedSideB = MusicBox().getIsOpenedSideB();
if (isOpenedSideB) {
LogUtil.d('进入过B面');
_openSideB();
} else {
bool enter = MusicBox().getEnter();
String versionCode = await MusicBox().getVersionCode();
final packageInfo = await PackageInfo.fromPlatform();
if (versionCode != packageInfo.version) {
LogUtil.d('版本不相同进入B面');
_openSideB();
} else {
if (enter) {
LogUtil.d('开关:打开');
_openSideB();
} else {
LogUtil.d('开关:关闭');
_openSideA();
}
}
}
}
void _openSideA() {
AppOpenAdManager().showAdIfAvailable(onTap: () {
AppConfig.appSideEnum = AppSideEnum.sideA;
Get.offNamed(AppRoutes.initialA);
});
}
void _openSideB() {
AppOpenAdManager().showAdIfAvailable(onTap: () {
AppConfig.appSideEnum = AppSideEnum.sideB;
MainController.to.changeTheme();
Get.offNamed(AppRoutes.initialB);
MusicBox().putIsOpenedSideB(true);
});
}
///
Future<void> getIsoCode() async {
BaseModel<IsoCodeModel>? model = await TikUsTokApi.getIsoCode();
if (model != null && model.success && model.data?.isoCode != null) {
AppConfig.isoCode = model.data!.isoCode!;
}
}
}

View File

@ -1,14 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:step_progress_indicator/step_progress_indicator.dart';
import 'package:tone_snap/generated/assets.dart'; import 'package:tone_snap/generated/assets.dart';
import 'package:tone_snap/modules/splash/splash_controller.dart'; import 'package:tone_snap/modules/launch/launch_controller.dart';
import 'package:tone_snap/res/themes/app_colors.dart'; import 'package:tone_snap/res/themes/app_colors.dart';
class SplashView extends StatelessWidget { class LaunchView extends StatelessWidget {
SplashView({super.key}); LaunchView({super.key});
final controller = Get.find<SplashController>(); final controller = Get.find<LaunchController>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,7 +25,7 @@ class SplashView extends StatelessWidget {
Widget _buildImageBg() { Widget _buildImageBg() {
return Image.asset( return Image.asset(
Assets.sideALaunchImage, Assets.sideALaunchBg,
width: 1.sw, width: 1.sw,
height: 1.sh, height: 1.sh,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -39,17 +40,31 @@ class SplashView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
SizedBox( SizedBox(
width: 0.5.sw, width: 0.6.sw,
child: Obx(() { child: Obx(() {
return LinearProgressIndicator( return ClipRRect(
value: controller.processValue.value,
backgroundColor: Colors.white,
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
borderRadius: BorderRadius.circular(8).r, borderRadius: BorderRadius.circular(8).r,
child: StepProgressIndicator(
totalSteps: controller.timeTotal,
currentStep: controller.currentProcess.value,
size: 6,
padding: 0,
unselectedColor: Colors.white,
roundedEdges: const Radius.circular(8).r,
selectedGradientColor: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xffAC42FF),
Color(0xff5738D3),
Color(0xffC1ED02),
],
),
),
); );
}), }),
), ),
SizedBox(height: 14.h), SizedBox(height: 10.h),
Text( Text(
'Resource Loading...', 'Resource Loading...',
style: TextStyle( style: TextStyle(

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_kit.dart'; import 'package:ffmpeg_kit_flutter_audio/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_audio/ffmpeg_session.dart'; import 'package:ffmpeg_kit_flutter_audio/ffmpeg_session.dart';
import 'package:ffmpeg_kit_flutter_audio/return_code.dart'; import 'package:ffmpeg_kit_flutter_audio/return_code.dart';
@ -48,9 +46,7 @@ class ChangeVoiceController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
if (Platform.isIOS) { InterstitialAdManager().loadAd();
InterstitialAdManager().loadAd();
}
filePath = Get.arguments; filePath = Get.arguments;
// playerController.setFilePath(filePath); // playerController.setFilePath(filePath);
@ -108,122 +104,65 @@ class ChangeVoiceController extends GetxController {
/// ///
Future<void> save() async { Future<void> save() async {
if (Platform.isIOS) { // 广
// 广 InterstitialAdManager().showAdIfAvailable(
InterstitialAdManager().showAdIfAvailable( onTap: () async {
onTap: () async { //
// if (playerController.isPlaying.value) playerController.stopPlay();
if (playerController.isPlaying.value) playerController.stopPlay(); BaseEasyLoading.loading();
BaseEasyLoading.loading();
try { try {
// assets路径 // assets路径
if (filePath.contains('assets')) { if (filePath.contains('assets')) {
filePath = await FileUtil.getAssetsToFilePath(filePath); filePath = await FileUtil.getAssetsToFilePath(filePath);
}
//
final outputDir = await LocalPathUtil.getVoiceChangeOutputDir();
String fileName = '${DateUtil.getNowTimeStr().replaceAll(' ', '_')}_output.mp3';
String outputPath = '${outputDir.path}/$fileName';
var filter = "";
// var timber = timberList.firstWhereOrNull((e) => e.check);
// if (timber != null) {
// int index = timberList.indexOf(timber);
// if (index == 5) filter = ",afftdn=nf=-30";
// }
if (currentIndex.value == 5) filter = ",afftdn=nf=-30,aecho=0.8:0.88:60:0.4";
//
String sampleRate = await _getSampleRate() ?? '24000';
// FFmpeg
final String command = '-i $filePath -af "asetrate=$sampleRate*${toneValue.value},atempo=${soundSpeedValue.value}$filter" $outputPath';
// FFmpeg
FFmpegSession session = await FFmpegKit.execute(command);
//
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
LogUtil.d('Audio processing successful');
try {
await MyVoiceBox().addData(VoiceModel(name: fileName, path: outputPath));
BaseEasyLoading.toast('Save successful');
// -
Get.until((route) => route.settings.name == AppRoutes.initialA);
InitialController.to.onBottomAppBarItemChanged(2);
} catch (e) {
BaseEasyLoading.toast('Save failed');
} }
} else {
// LogUtil.d('Audio processing failed');
final outputDir = await LocalPathUtil.getVoiceChangeOutputDir();
String fileName = '${DateUtil.getNowTimeStr().replaceAll(' ', '_')}_output.mp3';
String outputPath = '${outputDir.path}/$fileName';
var filter = "";
// var timber = timberList.firstWhereOrNull((e) => e.check);
// if (timber != null) {
// int index = timberList.indexOf(timber);
// if (index == 5) filter = ",afftdn=nf=-30";
// }
if (currentIndex.value == 5) filter = ",afftdn=nf=-30,aecho=0.8:0.88:60:0.4";
//
String sampleRate = await _getSampleRate() ?? '24000';
// FFmpeg
final String command = '-i $filePath -af "asetrate=$sampleRate*${toneValue.value},atempo=${soundSpeedValue.value}$filter" $outputPath';
// FFmpeg
FFmpegSession session = await FFmpegKit.execute(command);
//
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
LogUtil.d('Audio processing successful');
try {
await MyVoiceBox().addData(VoiceModel(name: fileName, path: outputPath));
BaseEasyLoading.toast('Save successful');
// -
Get.until((route) => route.settings.name == AppRoutes.initialA);
InitialController.to.onBottomAppBarItemChanged(2);
} catch (e) {
BaseEasyLoading.toast('Save failed');
}
} else {
LogUtil.d('Audio processing failed');
BaseEasyLoading.toast('Audio processing failed');
}
} catch (e) {
BaseEasyLoading.toast('Audio processing failed'); BaseEasyLoading.toast('Audio processing failed');
} }
}, } catch (e) {
);
} else {
//
if (playerController.isPlaying.value) playerController.stopPlay();
BaseEasyLoading.loading();
try {
// assets路径
if (filePath.contains('assets')) {
filePath = await FileUtil.getAssetsToFilePath(filePath);
}
//
final outputDir = await LocalPathUtil.getVoiceChangeOutputDir();
String fileName = '${DateUtil.getNowTimeStr().replaceAll(' ', '_')}_output.mp3';
String outputPath = '${outputDir.path}/$fileName';
var filter = "";
// var timber = timberList.firstWhereOrNull((e) => e.check);
// if (timber != null) {
// int index = timberList.indexOf(timber);
// if (index == 5) filter = ",afftdn=nf=-30";
// }
if (currentIndex.value == 5) filter = ",afftdn=nf=-30,aecho=0.8:0.88:60:0.4";
//
String sampleRate = await _getSampleRate() ?? '24000';
// FFmpeg
final String command = '-i $filePath -af "asetrate=$sampleRate*${toneValue.value},atempo=${soundSpeedValue.value}$filter" $outputPath';
// FFmpeg
FFmpegSession session = await FFmpegKit.execute(command);
//
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
LogUtil.d('Audio processing successful');
try {
await MyVoiceBox().addData(VoiceModel(name: fileName, path: outputPath));
BaseEasyLoading.toast('Save successful');
// -
Get.until((route) => route.settings.name == AppRoutes.initialA);
InitialController.to.onBottomAppBarItemChanged(2);
} catch (e) {
BaseEasyLoading.toast('Save failed');
}
} else {
LogUtil.d('Audio processing failed');
BaseEasyLoading.toast('Audio processing failed'); BaseEasyLoading.toast('Audio processing failed');
} }
} catch (e) { },
BaseEasyLoading.toast('Audio processing failed'); );
}
}
} }
/// ///

View File

@ -57,7 +57,6 @@ class FavouriteController extends GetxController {
void onTapDelete(VoiceModel item) { void onTapDelete(VoiceModel item) {
Get.dialog( Get.dialog(
barrierDismissible: false,
RemindDialog( RemindDialog(
content: 'Are you sure to delete it?', content: 'Are you sure to delete it?',
confirmOnTap: () async { confirmOnTap: () async {

View File

@ -1,9 +0,0 @@
import 'package:get/get.dart';
import 'package:tone_snap/modules/sidea/home/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
}
}

View File

@ -14,7 +14,8 @@ class HomeView extends GetView<HomeController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.find<HomeController>(); Get.put(HomeController());
return Column( return Column(
children: [ children: [
HeadLabel( HeadLabel(
@ -71,7 +72,7 @@ class HomeView extends GetView<HomeController> {
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
Visibility( Visibility(
visible: ObjUtil.isNotEmptyStr(item.cover), visible: ObjUtil.isNotEmpty(item.cover),
child: ClipPath( child: ClipPath(
clipper: CircularNotchClipper( clipper: CircularNotchClipper(
notchRadius: 20, notchRadius: 20,

View File

@ -1,12 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tone_snap/data/models/voice_model.dart'; import 'package:tone_snap/data/models/voice_model.dart';
import 'package:tone_snap/data/storage/favorite_box.dart'; import 'package:tone_snap/data/storage/favorite_box.dart';
import 'package:tone_snap/firebase/firebase_analytics_manager.dart'; import 'package:tone_snap/firebase/firebase_analytics_manager.dart';
import 'package:tone_snap/generated/assets.dart'; import 'package:tone_snap/generated/assets.dart';
import 'package:tone_snap/global/app_lifecycle_reactor.dart'; import 'package:tone_snap/global/app_tracking_transparency_manager.dart';
import 'package:tone_snap/modules/sidea/controllers/player_controller.dart'; import 'package:tone_snap/modules/sidea/controllers/player_controller.dart';
import 'package:tone_snap/modules/sidea/favourite/favourite_controller.dart'; import 'package:tone_snap/modules/sidea/favourite/favourite_controller.dart';
import 'package:tone_snap/modules/sidea/home/home_view.dart'; import 'package:tone_snap/modules/sidea/home/home_view.dart';
@ -28,25 +26,20 @@ class InitialController extends GetxController {
var currentIndex = 0.obs; var currentIndex = 0.obs;
Rx<VoiceModel?> currentPlayVoiceModel = Rx<VoiceModel?>(null); Rx<VoiceModel?> currentPlayVoiceModel = Rx<VoiceModel?>(null);
/// ///
var isFavourite = false.obs; var isFavourite = false.obs;
late AppLifecycleReactor _appLifecycleReactor;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// _appLifecycleReactor = AppLifecycleReactor(); AppTrackingTransparencyManager().requestATT();
// _appLifecycleReactor.listenToAppStateChanges();
pageController = PageController(initialPage: currentIndex.value); pageController = PageController(initialPage: currentIndex.value);
} }
@override @override
void onReady() async { void onReady() async {
super.onReady(); super.onReady();
// await FirebaseAnalyticsManager.setCrashlyticsCollectionEnabled(); _addEventLog();
// _addEventLog();
} }
@override @override
@ -60,9 +53,9 @@ class InitialController extends GetxController {
await PlayerController.to.stopPlay(); await PlayerController.to.stopPlay();
Get.toNamed(AppRoutes.uploadMethod); Get.toNamed(AppRoutes.uploadMethod);
} else { } else {
// if (index == 0) { if (index == 0) {
// _addEventLog(); _addEventLog();
// } }
if (index == 2) _refreshMe(); if (index == 2) _refreshMe();
currentIndex.value = index; currentIndex.value = index;
pageController.jumpToPage(index); pageController.jumpToPage(index);
@ -105,7 +98,7 @@ class InitialController extends GetxController {
/// ///
void _addEventLog() { void _addEventLog() {
FirebaseAnalyticsManager.logEvent('home_a_pv'); FirebaseAnalyticsManager.logEvent(FirebaseAnalyticsManager.homeApv);
} }
} }

View File

@ -1,9 +0,0 @@
import 'package:get/get.dart';
import 'package:tone_snap/modules/sidea/me/me_controller.dart';
class MeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => MeController());
}
}

View File

@ -12,7 +12,8 @@ class MeView extends GetView<MeController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.find<MeController>(); Get.put(MeController());
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

Some files were not shown because too many files have changed in this diff Show More