1.添加通知栏媒体控制
2.下一首上一首增加防抖 3.集成fb 4.首页单曲增加下载 5.增加后台播放(iOS仍有问题) 6.修改系统状态栏颜色
This commit is contained in:
parent
caded892d9
commit
063e3d7c91
@ -9,8 +9,14 @@
|
|||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"
|
||||||
android:minSdkVersion = "33"/>
|
android:minSdkVersion = "33"/>
|
||||||
|
|
||||||
|
<!-- ADD THESE TWO PERMISSIONS -->
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
<!-- ALSO ADD THIS PERMISSION IF TARGETING SDK 34 -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="ToneSnap"
|
android:label="@string/app_name"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/launcher_icon"
|
android:icon="@mipmap/launcher_icon"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
@ -25,7 +31,8 @@
|
|||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
tools:ignore="Instantiatable">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
@ -39,6 +46,26 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- ADD THIS "SERVICE" element -->
|
||||||
|
<service android:name="com.ryanheise.audioservice.AudioService"
|
||||||
|
android:foregroundServiceType="mediaPlayback"
|
||||||
|
android:exported="true"
|
||||||
|
tools:ignore="Instantiatable">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- ADD THIS "RECEIVER" element -->
|
||||||
|
<receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
tools:ignore="Instantiatable">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -49,6 +76,10 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||||
android:value="ca-app-pub-5684307632319406~5753747604"/>
|
android:value="ca-app-pub-5684307632319406~5753747604"/>
|
||||||
|
|
||||||
|
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
|
||||||
|
<meta-data android:name="com.facebook.sdk.ClientToken" android:value="@string/facebook_client_token"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
<!-- Required to query activities that can process text, see:
|
<!-- Required to query activities that can process text, see:
|
||||||
https://developer.android.com/training/package-visibility and
|
https://developer.android.com/training/package-visibility and
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package com.tone.music.offline
|
package com.tone.music.offline
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import com.ryanheise.audioservice.AudioServiceActivity
|
||||||
|
|
||||||
class MainActivity: FlutterActivity()
|
class MainActivity: AudioServiceActivity()
|
||||||
|
|||||||
6
android/app/src/main/res/values/strings.xml
Normal file
6
android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">ToneSnap</string>
|
||||||
|
<string name="facebook_app_id">1058126742561467</string>
|
||||||
|
<string name="facebook_client_token">d37b13dd56db288fe976782b2a10b4a7</string>
|
||||||
|
</resources>
|
||||||
@ -490,7 +490,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 15;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@ -689,7 +689,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 15;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@ -723,7 +723,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 15;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
|||||||
@ -36,6 +36,45 @@
|
|||||||
<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>NSUserTrackingUsageDescription</key>
|
<key>NSUserTrackingUsageDescription</key>
|
||||||
<string>We need your permission to access the advertising identifier to provide better ad services.</string>
|
<string>We need your permission to access the advertising identifier to provide better ad services.</string>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>fb1044438596577354</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>FacebookAppID</key>
|
||||||
|
<string>1044438596577354</string>
|
||||||
|
<key>FacebookClientToken</key>
|
||||||
|
<string>4d24452ca73a8fc8157f7a46d4d1930b</string>
|
||||||
|
<key>FacebookDisplayName</key>
|
||||||
|
<string>ToneSnap-ios</string>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
<string>fetch</string>
|
||||||
|
</array>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
<key>SKAdNetworkItems</key>
|
<key>SKAdNetworkItems</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@ -235,29 +274,5 @@
|
|||||||
<string>3qcr597p9d.skadnetwork</string>
|
<string>3qcr597p9d.skadnetwork</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIBackgroundModes</key>
|
|
||||||
<array>
|
|
||||||
<string>audio</string>
|
|
||||||
<string>fetch</string>
|
|
||||||
</array>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>LaunchScreen</string>
|
|
||||||
<key>UIMainStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import 'package:tone_snap/data/models/ad_config_model.dart';
|
|||||||
import 'package:tone_snap/data/storage/music_box.dart';
|
import 'package:tone_snap/data/storage/music_box.dart';
|
||||||
import 'package:tone_snap/firebase/firebase_analytics_manager.dart';
|
import 'package:tone_snap/firebase/firebase_analytics_manager.dart';
|
||||||
import 'package:tone_snap/modules/launch/launch_controller.dart';
|
import 'package:tone_snap/modules/launch/launch_controller.dart';
|
||||||
|
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
||||||
import 'package:tone_snap/utils/log_util.dart';
|
import 'package:tone_snap/utils/log_util.dart';
|
||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
@ -35,6 +36,9 @@ class InterstitialAdManager {
|
|||||||
/// 是否显示
|
/// 是否显示
|
||||||
bool isShowingAd = false;
|
bool isShowingAd = false;
|
||||||
|
|
||||||
|
/// 广告显示前的,音乐是否在播放
|
||||||
|
bool showAdFrontIsPlaying = false;
|
||||||
|
|
||||||
/// 加载所有插页广告位
|
/// 加载所有插页广告位
|
||||||
void loadAdAll() {
|
void loadAdAll() {
|
||||||
AdConfigModel adConfigModel = MusicBox().getAdConfig();
|
AdConfigModel adConfigModel = MusicBox().getAdConfig();
|
||||||
@ -90,6 +94,11 @@ class InterstitialAdManager {
|
|||||||
|
|
||||||
/// 加载失败处理
|
/// 加载失败处理
|
||||||
void _loadFailHandle(String adUnitId, String adScenes) {
|
void _loadFailHandle(String adUnitId, String adScenes) {
|
||||||
|
int interval = 0;
|
||||||
|
if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) {
|
||||||
|
interval = 15;
|
||||||
|
}
|
||||||
|
Future.delayed(Duration(seconds: interval), () {
|
||||||
AdConfigModel adConfigModel = MusicBox().getAdConfig();
|
AdConfigModel adConfigModel = MusicBox().getAdConfig();
|
||||||
var adList = _getAdList(adScenes);
|
var adList = _getAdList(adScenes);
|
||||||
if (adList != null) {
|
if (adList != null) {
|
||||||
@ -117,6 +126,7 @@ class InterstitialAdManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AdModel>? _getAdList(String adScenes) {
|
List<AdModel>? _getAdList(String adScenes) {
|
||||||
@ -197,6 +207,11 @@ class InterstitialAdManager {
|
|||||||
onAdShowedFullScreenContent: (ad) {
|
onAdShowedFullScreenContent: (ad) {
|
||||||
LogUtil.d('$ad onAdShowedFullScreenContent');
|
LogUtil.d('$ad onAdShowedFullScreenContent');
|
||||||
isShowingAd = true;
|
isShowingAd = true;
|
||||||
|
|
||||||
|
showAdFrontIsPlaying = MusicPlayerController.to.isPlaying.value;
|
||||||
|
if (showAdFrontIsPlaying) {
|
||||||
|
MusicPlayerController.to.pause();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// 更适合用于跟踪广告展示的次数和效果,以及执行与广告展示相关的操作
|
// 更适合用于跟踪广告展示的次数和效果,以及执行与广告展示相关的操作
|
||||||
onAdImpression: (ad) {
|
onAdImpression: (ad) {
|
||||||
@ -217,11 +232,21 @@ class InterstitialAdManager {
|
|||||||
onAdDismissedFullScreenContent: (ad) {
|
onAdDismissedFullScreenContent: (ad) {
|
||||||
LogUtil.d('$ad onAdDismissedFullScreenContent');
|
LogUtil.d('$ad onAdDismissedFullScreenContent');
|
||||||
isShowingAd = false;
|
isShowingAd = false;
|
||||||
|
if (showAdFrontIsPlaying) {
|
||||||
|
MusicPlayerController.to.play();
|
||||||
|
}
|
||||||
_closeDateTime = DateTime.now();
|
_closeDateTime = DateTime.now();
|
||||||
ad.dispose();
|
ad.dispose();
|
||||||
|
|
||||||
|
int interval = 0;
|
||||||
|
if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) {
|
||||||
|
interval = 15;
|
||||||
|
}
|
||||||
|
Future.delayed(Duration(seconds: interval), () {
|
||||||
loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes);
|
loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes);
|
||||||
_interstitialAdMap[adScenes] = null;
|
_interstitialAdMap[adScenes] = null;
|
||||||
_interstitialAdMap.remove(adScenes);
|
_interstitialAdMap.remove(adScenes);
|
||||||
|
});
|
||||||
|
|
||||||
if(onTap != null) onTap();
|
if(onTap != null) onTap();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,14 +17,16 @@ class ViewStateWidget extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.viewState,
|
required this.viewState,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.cpiBgColor,
|
this.loadColor,
|
||||||
|
this.loadBgColor,
|
||||||
this.showTryAgain = false,
|
this.showTryAgain = false,
|
||||||
this.onTapTryAgain,
|
this.onTapTryAgain,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewState viewState;
|
final ViewState viewState;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Color? cpiBgColor;
|
final Color? loadColor;
|
||||||
|
final Color? loadBgColor;
|
||||||
final bool showTryAgain;
|
final bool showTryAgain;
|
||||||
final Function()? onTapTryAgain;
|
final Function()? onTapTryAgain;
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ class ViewStateWidget extends StatelessWidget {
|
|||||||
case ViewState.normal:
|
case ViewState.normal:
|
||||||
return child;
|
return child;
|
||||||
case ViewState.loading:
|
case ViewState.loading:
|
||||||
return loadingView(backgroundColor: cpiBgColor);
|
return loadingView(loadColor: loadColor, loadBgColor: loadBgColor);
|
||||||
case ViewState.empty:
|
case ViewState.empty:
|
||||||
return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB(
|
return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB(
|
||||||
showTryAgain: showTryAgain,
|
showTryAgain: showTryAgain,
|
||||||
@ -48,14 +50,14 @@ class ViewStateWidget extends StatelessWidget {
|
|||||||
|
|
||||||
/// 加载中视图
|
/// 加载中视图
|
||||||
Widget loadingView({
|
Widget loadingView({
|
||||||
Color? color,
|
Color? loadColor,
|
||||||
Color? backgroundColor,
|
Color? loadBgColor,
|
||||||
}) {
|
}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2.w,
|
strokeWidth: 2.w,
|
||||||
color: AppConfig.appSideEnum == AppSideEnum.sideA ? color : seedColor,
|
color: AppConfig.appSideEnum == AppSideEnum.sideB ? loadColor ?? seedColor : loadColor,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: loadBgColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
2
lib/data/cache/music_cache_manager.dart
vendored
2
lib/data/cache/music_cache_manager.dart
vendored
@ -11,7 +11,7 @@ class MusicCacheManager {
|
|||||||
Config(
|
Config(
|
||||||
key,
|
key,
|
||||||
stalePeriod: const Duration(days: 7),
|
stalePeriod: const Duration(days: 7),
|
||||||
maxNrOfCacheObjects: 30,
|
maxNrOfCacheObjects: 100,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
17
lib/facebook/facebook_manager.dart
Normal file
17
lib/facebook/facebook_manager.dart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/6/26
|
||||||
|
// Description: facebook管理
|
||||||
|
|
||||||
|
import 'package:facebook_app_events/facebook_app_events.dart';
|
||||||
|
import 'package:tone_snap/utils/log_util.dart';
|
||||||
|
|
||||||
|
class FacebookManager {
|
||||||
|
static Future<void> init() async {
|
||||||
|
try {
|
||||||
|
await FacebookAppEvents().setAdvertiserTracking(enabled: true);
|
||||||
|
await FacebookAppEvents().setAutoLogAppEventsEnabled(true);
|
||||||
|
} catch (e) {
|
||||||
|
LogUtil.d(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,10 +10,10 @@ import 'package:tone_snap/utils/log_util.dart';
|
|||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class FirebaseRemoteConfigManager {
|
class FirebaseRemoteConfigManager {
|
||||||
static Future<void> getAll() async {
|
static Future<void> getAllConfig() async {
|
||||||
final remoteConfig = FirebaseRemoteConfig.instance;
|
final remoteConfig = FirebaseRemoteConfig.instance;
|
||||||
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
||||||
fetchTimeout: const Duration(minutes: 1),
|
fetchTimeout: const Duration(seconds: 30),
|
||||||
minimumFetchInterval: Duration.zero,
|
minimumFetchInterval: Duration.zero,
|
||||||
));
|
));
|
||||||
bool result = await remoteConfig.fetchAndActivate();
|
bool result = await remoteConfig.fetchAndActivate();
|
||||||
|
|||||||
@ -30,13 +30,13 @@ class AppConfig {
|
|||||||
"coldLoading": [
|
"coldLoading": [
|
||||||
{
|
{
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"identifier": "ca-app-pub-5684307632319406/5757639118",
|
"identifier": "",
|
||||||
"ad": "AdMob",
|
"ad": "AdMob",
|
||||||
"type": "Insert"
|
"type": "Insert"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"identifier": "ca-app-pub-5684307632319406/5852437064",
|
"identifier": "",
|
||||||
"ad": "AdMob",
|
"ad": "AdMob",
|
||||||
"type": "Insert"
|
"type": "Insert"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class NetworkConnectivityService extends GetxService {
|
|||||||
if (!isExecutedTask) {
|
if (!isExecutedTask) {
|
||||||
isExecutedTask = true;
|
isExecutedTask = true;
|
||||||
|
|
||||||
FirebaseRemoteConfigManager.getAll();
|
FirebaseRemoteConfigManager.getAllConfig();
|
||||||
|
|
||||||
InterstitialAdManager().loadAdAll();
|
InterstitialAdManager().loadAdAll();
|
||||||
// LibraryNativeAdManager().loadNativeAd();
|
// LibraryNativeAdManager().loadNativeAd();
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -8,8 +6,10 @@ 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:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
import 'package:tone_snap/data/storage/hive_storage.dart';
|
import 'package:tone_snap/data/storage/hive_storage.dart';
|
||||||
|
import 'package:tone_snap/facebook/facebook_manager.dart';
|
||||||
import 'package:tone_snap/firebase/firebase_crashlytics_manager.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';
|
||||||
@ -25,6 +25,9 @@ import 'package:tone_snap/utils/log_util.dart';
|
|||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
// 初始化Hive
|
||||||
|
await initHive();
|
||||||
|
|
||||||
// 初始化Firebase
|
// 初始化Firebase
|
||||||
try {
|
try {
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
@ -35,6 +38,8 @@ void main() async {
|
|||||||
LogUtil.e("Firebase initialization error: $e");
|
LogUtil.e("Firebase initialization error: $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await FacebookManager.init();
|
||||||
|
|
||||||
// 初始化广告 SDK
|
// 初始化广告 SDK
|
||||||
try {
|
try {
|
||||||
MobileAds.instance.initialize();
|
MobileAds.instance.initialize();
|
||||||
@ -42,10 +47,17 @@ void main() async {
|
|||||||
LogUtil.e("MobileAds initialization error: $e");
|
LogUtil.e("MobileAds initialization error: $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化Hive
|
try {
|
||||||
await initHive();
|
await JustAudioBackground.init(
|
||||||
|
androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
|
||||||
runApp(const MyApp());
|
androidNotificationChannelName: 'Audio playback',
|
||||||
|
androidNotificationIcon: 'mipmap/launcher_icon',
|
||||||
|
// androidNotificationOngoing: true,
|
||||||
|
androidStopForegroundOnPause: false,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
LogUtil.e(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
// 竖屏
|
// 竖屏
|
||||||
SystemChrome.setPreferredOrientations([
|
SystemChrome.setPreferredOrientations([
|
||||||
@ -53,14 +65,16 @@ void main() async {
|
|||||||
DeviceOrientation.portraitDown,
|
DeviceOrientation.portraitDown,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 设置安卓状态栏背景色透明、底部导航栏颜色和导航栏图标亮度
|
// 状态栏设置
|
||||||
if (Platform.isAndroid) {
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarBrightness: Brightness.dark,
|
||||||
|
statusBarIconBrightness: Brightness.dark,
|
||||||
systemNavigationBarColor: Colors.black,
|
systemNavigationBarColor: Colors.black,
|
||||||
systemNavigationBarIconBrightness: Brightness.light,
|
systemNavigationBarIconBrightness: Brightness.light,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
|||||||
@ -92,9 +92,9 @@ class LaunchController extends GetxController with GetSingleTickerProviderStateM
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
AppConfig.appSideEnum = AppSideEnum.sideA;
|
AppConfig.appSideEnum = AppSideEnum.sideA;
|
||||||
Get.offNamed(AppRoutes.initialA);
|
Get.offNamed(AppRoutes.initialA);
|
||||||
|
FirebaseAnalyticsManager.logJumpEvent('A', '版本相同,开关关闭');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
FirebaseAnalyticsManager.logJumpEvent('A', '版本相同,开关关闭');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openSideB(String reason) {
|
void _openSideB(String reason) {
|
||||||
@ -105,8 +105,8 @@ class LaunchController extends GetxController with GetSingleTickerProviderStateM
|
|||||||
MainController.to.changeTheme();
|
MainController.to.changeTheme();
|
||||||
Get.offNamed(AppRoutes.initialB);
|
Get.offNamed(AppRoutes.initialB);
|
||||||
MusicBox().putIsOpenedSideB(true);
|
MusicBox().putIsOpenedSideB(true);
|
||||||
|
FirebaseAnalyticsManager.logJumpEvent('B', reason);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
FirebaseAnalyticsManager.logJumpEvent('B', reason);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,12 @@ import 'dart:io';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
import 'package:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
|
import 'package:tone_snap/global/app_config.dart';
|
||||||
import 'package:tone_snap/utils/audio_util.dart';
|
import 'package:tone_snap/utils/audio_util.dart';
|
||||||
import 'package:tone_snap/utils/log_util.dart';
|
import 'package:tone_snap/utils/log_util.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class PlayerController extends GetxController {
|
class PlayerController extends GetxController {
|
||||||
static PlayerController get to => Get.put(PlayerController());
|
static PlayerController get to => Get.put(PlayerController());
|
||||||
@ -45,11 +48,15 @@ class PlayerController extends GetxController {
|
|||||||
if (this.filePath != filePath) {
|
if (this.filePath != filePath) {
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
try {
|
try {
|
||||||
|
final mediaItem = MediaItem(
|
||||||
|
id: filePath,
|
||||||
|
title: AppConfig.appName,
|
||||||
|
);
|
||||||
isReady = false;
|
isReady = false;
|
||||||
if (filePath.contains('assets')) {
|
if (filePath.contains('assets')) {
|
||||||
duration.value = await _player.setAsset(filePath) ?? Duration.zero;
|
duration.value = await _player.setAsset(filePath, tag: mediaItem) ?? Duration.zero;
|
||||||
} else {
|
} else {
|
||||||
duration.value = await _player.setFilePath(filePath) ?? Duration.zero;
|
duration.value = await _player.setFilePath(filePath, tag: mediaItem) ?? Duration.zero;
|
||||||
}
|
}
|
||||||
isReady = true;
|
isReady = true;
|
||||||
} on PlayerException catch (e) {
|
} on PlayerException catch (e) {
|
||||||
|
|||||||
@ -5,8 +5,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
import 'package:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
import 'package:tone_snap/data/api/music_api.dart';
|
import 'package:tone_snap/data/api/music_api.dart';
|
||||||
import 'package:tone_snap/data/cache/music_cache_manager.dart';
|
import 'package:tone_snap/data/cache/music_cache_manager.dart';
|
||||||
@ -17,7 +19,6 @@ import 'package:tone_snap/data/storage/music_box.dart';
|
|||||||
import 'package:tone_snap/data/storage/offline_box.dart';
|
import 'package:tone_snap/data/storage/offline_box.dart';
|
||||||
import 'package:tone_snap/firebase/firebase_analytics_manager.dart';
|
import 'package:tone_snap/firebase/firebase_analytics_manager.dart';
|
||||||
import 'package:tone_snap/modules/sideb/music_bar/music_bar.dart';
|
import 'package:tone_snap/modules/sideb/music_bar/music_bar.dart';
|
||||||
import 'package:tone_snap/routes/app_routes.dart';
|
|
||||||
import 'package:tone_snap/utils/audio_util.dart';
|
import 'package:tone_snap/utils/audio_util.dart';
|
||||||
import 'package:tone_snap/utils/log_util.dart';
|
import 'package:tone_snap/utils/log_util.dart';
|
||||||
import 'package:tone_snap/utils/num_util.dart';
|
import 'package:tone_snap/utils/num_util.dart';
|
||||||
@ -106,8 +107,15 @@ class MusicPlayerController extends GetxController {
|
|||||||
if (playlist.isNotEmpty) {
|
if (playlist.isNotEmpty) {
|
||||||
FirebaseAnalyticsManager.logPlayerBpv(getMusicModel()?.videoId, getMusicModel()?.title, getMusicModel()?.subtitle);
|
FirebaseAnalyticsManager.logPlayerBpv(getMusicModel()?.videoId, getMusicModel()?.title, getMusicModel()?.subtitle);
|
||||||
resetPlaybackStatus();
|
resetPlaybackStatus();
|
||||||
Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
|
// Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
|
||||||
try {
|
try {
|
||||||
|
final mediaItem = MediaItem(
|
||||||
|
id: ObjUtil.getStr(getMusicModel()?.videoId),
|
||||||
|
artist: ObjUtil.getStr(getMusicModel()?.subtitle),
|
||||||
|
title: ObjUtil.getStr(getMusicModel()?.title),
|
||||||
|
artUri: Uri.parse(ObjUtil.getStr(getMusicModel()?.coverUrl)),
|
||||||
|
);
|
||||||
|
|
||||||
DateTime startLoadDateTime = DateTime.now();
|
DateTime startLoadDateTime = DateTime.now();
|
||||||
var model = OfflineBox().getList().firstWhereOrNull((e) => e.videoId == getMusicModel()?.videoId);
|
var model = OfflineBox().getList().firstWhereOrNull((e) => e.videoId == getMusicModel()?.videoId);
|
||||||
if (model != null && ObjUtil.isNotEmpty(model.localPath)) {
|
if (model != null && ObjUtil.isNotEmpty(model.localPath)) {
|
||||||
@ -118,7 +126,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
FirebaseAnalyticsManager.logPlayerBFailAction('本地文件不存在');
|
FirebaseAnalyticsManager.logPlayerBFailAction('本地文件不存在');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _player.setFilePath(model.localPath!);
|
await _player.setFilePath(model.localPath!, tag: mediaItem);
|
||||||
} else {
|
} else {
|
||||||
// 无下载
|
// 无下载
|
||||||
var fileInfo = await MusicCacheManager.checkCache(getMusicModel()?.videoId);
|
var fileInfo = await MusicCacheManager.checkCache(getMusicModel()?.videoId);
|
||||||
@ -126,14 +134,14 @@ class MusicPlayerController extends GetxController {
|
|||||||
// 有缓存
|
// 有缓存
|
||||||
LogUtil.d('读取缓存路径=${fileInfo.file.path}');
|
LogUtil.d('读取缓存路径=${fileInfo.file.path}');
|
||||||
// 如果有缓存,使用缓存文件
|
// 如果有缓存,使用缓存文件
|
||||||
await _player.setFilePath(fileInfo.file.path);
|
await _player.setFilePath(fileInfo.file.path, tag: mediaItem);
|
||||||
} else {
|
} else {
|
||||||
// 无缓存
|
// 无缓存
|
||||||
final url = await _getMusicUrl();
|
final url = await _getMusicUrl();
|
||||||
if (ObjUtil.isEmpty(url)) {
|
if (ObjUtil.isEmpty(url)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _player.setUrl(url!);
|
await _player.setUrl(url!, tag: mediaItem);
|
||||||
// 同时启动缓存下载
|
// 同时启动缓存下载
|
||||||
_cacheManager.downloadFile(url, key: MusicCacheManager.getCacheKey(getMusicModel()!.videoId!)).then((fileInfo) {
|
_cacheManager.downloadFile(url, key: MusicCacheManager.getCacheKey(getMusicModel()!.videoId!)).then((fileInfo) {
|
||||||
LogUtil.d('缓存下载路径=${fileInfo.file.path}');
|
LogUtil.d('缓存下载路径=${fileInfo.file.path}');
|
||||||
@ -175,6 +183,10 @@ class MusicPlayerController extends GetxController {
|
|||||||
|
|
||||||
/// 监听
|
/// 监听
|
||||||
void _addListening() {
|
void _addListening() {
|
||||||
|
_player.playbackEventStream.listen((event) {},
|
||||||
|
onError: (Object e, StackTrace stackTrace) {
|
||||||
|
LogUtil.e('A stream error occurred: $e');
|
||||||
|
});
|
||||||
_durationSubscription = _player.durationStream.listen((duration) {
|
_durationSubscription = _player.durationStream.listen((duration) {
|
||||||
totalDuration.value = duration ?? Duration.zero;
|
totalDuration.value = duration ?? Duration.zero;
|
||||||
});
|
});
|
||||||
@ -265,18 +277,33 @@ class MusicPlayerController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 播放/暂停
|
/// 播放/暂停
|
||||||
Future<void> playPause() async {
|
void playPause() {
|
||||||
if (_player.playing) {
|
if (_player.playing) {
|
||||||
_player.pause();
|
pause();
|
||||||
} else {
|
} else {
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 播放
|
||||||
|
void play() {
|
||||||
if (processingState.value == ProcessingState.ready) {
|
if (processingState.value == ProcessingState.ready) {
|
||||||
_player.play();
|
_player.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 暂停
|
||||||
|
void pause() {
|
||||||
|
_player.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 上一首
|
/// 上一首
|
||||||
Future<void> previousTrack() async {
|
Future<void> previousTrack() async {
|
||||||
|
// 通过防抖防止在音频源之间快速切换会导致PlatformException
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'cutTheSong',
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
() {
|
||||||
if (playlist.length > 1) {
|
if (playlist.length > 1) {
|
||||||
switch(playMode.value) {
|
switch(playMode.value) {
|
||||||
case PlayMode.listLoop:
|
case PlayMode.listLoop:
|
||||||
@ -308,10 +335,17 @@ class MusicPlayerController extends GetxController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 下一首
|
/// 下一首
|
||||||
Future<void> nextTrack({bool manualSwitch = true}) async {
|
Future<void> nextTrack({bool manualSwitch = true}) async {
|
||||||
|
// 通过防抖防止在音频源之间快速切换会导致PlatformException
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'cutTheSong',
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
() {
|
||||||
if (playlist.length > 1) {
|
if (playlist.length > 1) {
|
||||||
switch(playMode.value) {
|
switch(playMode.value) {
|
||||||
case PlayMode.listLoop:
|
case PlayMode.listLoop:
|
||||||
@ -332,6 +366,8 @@ class MusicPlayerController extends GetxController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 在列表范围内生成一个不包括当前索引的随机数
|
/// 在列表范围内生成一个不包括当前索引的随机数
|
||||||
|
|||||||
@ -100,26 +100,34 @@ class MusicBarView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 13.w),
|
SizedBox(width: 3.w),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: musicPlayerController.playPause,
|
onTap: musicPlayerController.playPause,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10).w,
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
musicPlayerController.isPlaying.value ? Assets.sideBPausePlay : Assets.sideBStartPlay,
|
musicPlayerController.isPlaying.value ? Assets.sideBPausePlay : Assets.sideBStartPlay,
|
||||||
width: 24.w,
|
width: 24.w,
|
||||||
height: 24.w,
|
height: 24.w,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
SizedBox(width: 26.w),
|
SizedBox(width: 6.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: musicPlayerController.nextTrack,
|
onTap: musicPlayerController.nextTrack,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10).w,
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
Assets.sideBMusicBarNext,
|
Assets.sideBMusicBarNext,
|
||||||
width: 24.w,
|
width: 24.w,
|
||||||
height: 24.w,
|
height: 24.w,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -108,15 +108,15 @@ class SearchMusicView extends GetView<SearchMusicController> {
|
|||||||
spacing: 8.w,
|
spacing: 8.w,
|
||||||
runSpacing: 8.h,
|
runSpacing: 8.h,
|
||||||
children: controller.historyList.map((history) {
|
children: controller.historyList.map((history) {
|
||||||
return InkWell(
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(30).r,
|
||||||
|
child: Material(
|
||||||
|
color: const Color(0xFF1F1F1F),
|
||||||
|
child: InkWell(
|
||||||
onTap: () => controller.onTapHistoryItem(history),
|
onTap: () => controller.onTapHistoryItem(history),
|
||||||
child: IntrinsicWidth(
|
child: IntrinsicWidth(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF1F1F1F),
|
|
||||||
borderRadius: BorderRadius.circular(30).r,
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
history,
|
history,
|
||||||
@ -127,6 +127,8 @@ class SearchMusicView extends GetView<SearchMusicController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,13 +2,23 @@
|
|||||||
// Date: 2024/6/21
|
// Date: 2024/6/21
|
||||||
// Description: 首页单曲Item
|
// Description: 首页单曲Item
|
||||||
|
|
||||||
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
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:tone_snap/components/base_easyloading.dart';
|
||||||
|
import 'package:tone_snap/components/dialog/remind_dialog.dart';
|
||||||
import 'package:tone_snap/components/network_image_widget.dart';
|
import 'package:tone_snap/components/network_image_widget.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/offline_box.dart';
|
||||||
import 'package:tone_snap/generated/assets.dart';
|
import 'package:tone_snap/generated/assets.dart';
|
||||||
|
import 'package:tone_snap/global/download_manager.dart';
|
||||||
|
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/more_bottom_sheet/more_bottom_sheet_view.dart';
|
import 'package:tone_snap/modules/sideb/more_bottom_sheet/more_bottom_sheet_view.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/modules/sideb/widgets/music_item_marquee_text.dart';
|
||||||
|
import 'package:tone_snap/res/themes/app_colors.dart';
|
||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class BrowseItemAtv extends StatelessWidget {
|
class BrowseItemAtv extends StatelessWidget {
|
||||||
@ -31,6 +41,7 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_buildCover(),
|
_buildCover(),
|
||||||
_buildContent(),
|
_buildContent(),
|
||||||
|
_buildDownload(),
|
||||||
_buildMore(),
|
_buildMore(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -57,31 +68,68 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Obx(() {
|
||||||
ObjUtil.getStr(musicModel.title),
|
return MusicItemMarqueeText(
|
||||||
maxLines: 1,
|
text: musicModel.title,
|
||||||
overflow: TextOverflow.ellipsis,
|
showMarquee: MusicPlayerController.to.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
|
||||||
style: TextStyle(
|
);
|
||||||
color: Colors.white,
|
}),
|
||||||
fontSize: 14.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 4.h),
|
SizedBox(height: 4.h),
|
||||||
Text(
|
Obx(() {
|
||||||
ObjUtil.getStr(musicModel.subtitle),
|
return MusicItemMarqueeText(
|
||||||
maxLines: 1,
|
text: musicModel.subtitle,
|
||||||
overflow: TextOverflow.ellipsis,
|
isTitle: false,
|
||||||
style: TextStyle(
|
showMarquee: MusicPlayerController.to.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
|
||||||
color: const Color(0xFF666666),
|
);
|
||||||
fontSize: 12.sp,
|
}),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDownload() {
|
||||||
|
return ClipOval(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTapDownload,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4).w,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 24.w,
|
||||||
|
height: 24.w,
|
||||||
|
child: GetBuilder<DownloadManager>(
|
||||||
|
id: DownloadManager.to.downloadStateId,
|
||||||
|
builder: (_) {
|
||||||
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
|
return Image.asset(Assets.sideBDownloaded);
|
||||||
|
} else {
|
||||||
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
color: seedColor,
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
value: DownloadManager.to.getMusicModel(musicModel.videoId)?.progress,
|
||||||
|
backgroundColor: seedColor.withOpacity(0.2),
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Image.asset(Assets.sideBNotDownload2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildMore() {
|
Widget _buildMore() {
|
||||||
return ClipOval(
|
return ClipOval(
|
||||||
child: Material(
|
child: Material(
|
||||||
@ -101,6 +149,34 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onTapDownload() {
|
||||||
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
|
Get.dialog(
|
||||||
|
RemindDialog(
|
||||||
|
content: 'Confirm to remove this song?',
|
||||||
|
confirmOnTap: () async {
|
||||||
|
await OfflineBox().delete(musicModel.videoId!);
|
||||||
|
DownloadManager.to.updateDownloadState();
|
||||||
|
BaseEasyLoading.toast('Removed');
|
||||||
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
|
PersonalMusicLibraryController.to.refreshOffline();
|
||||||
|
}
|
||||||
|
if (Get.isRegistered<OfflineController>()) {
|
||||||
|
OfflineController.to.getOfflineList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued
|
||||||
|
|| DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
DownloadManager.to.cancelDownload(musicModel.videoId);
|
||||||
|
} else {
|
||||||
|
DownloadManager.to.downloadMusic(musicModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onTapMore() {
|
void onTapMore() {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
MoreBottomSheetView(
|
MoreBottomSheetView(
|
||||||
|
|||||||
@ -18,7 +18,8 @@ class WebPageView extends StatelessWidget {
|
|||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
return ViewStateWidget(
|
return ViewStateWidget(
|
||||||
viewState: controller.viewState.value,
|
viewState: controller.viewState.value,
|
||||||
cpiBgColor: Colors.white,
|
loadColor: Colors.black,
|
||||||
|
loadBgColor: Colors.white,
|
||||||
child: WebViewWidget(controller: controller.webViewController),
|
child: WebViewWidget(controller: controller.webViewController),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -8,6 +8,7 @@ class AudioUtil {
|
|||||||
static Future<void> configAudioSession() async {
|
static Future<void> configAudioSession() async {
|
||||||
final session = await AudioSession.instance;
|
final session = await AudioSession.instance;
|
||||||
await session.configure(const AudioSessionConfiguration.music());
|
await session.configure(const AudioSessionConfiguration.music());
|
||||||
|
await session.setActive(true);
|
||||||
// await session.configure(AudioSessionConfiguration(
|
// await session.configure(AudioSessionConfiguration(
|
||||||
// avAudioSessionCategory: AVAudioSessionCategory.playback,
|
// avAudioSessionCategory: AVAudioSessionCategory.playback,
|
||||||
// avAudioSessionCategoryOptions:
|
// avAudioSessionCategoryOptions:
|
||||||
|
|||||||
10
pubspec.yaml
10
pubspec.yaml
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.5+15
|
version: 1.0.6+17
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.4.1 <4.0.0'
|
sdk: '>=3.4.1 <4.0.0'
|
||||||
@ -77,6 +77,7 @@ dependencies:
|
|||||||
audio_session: ^0.1.21
|
audio_session: ^0.1.21
|
||||||
flutter_sound: ^9.4.18
|
flutter_sound: ^9.4.18
|
||||||
just_audio: ^0.9.39
|
just_audio: ^0.9.39
|
||||||
|
just_audio_background: ^0.0.1-beta.13
|
||||||
# ffmpeg_kit_flutter: ^6.0.3
|
# ffmpeg_kit_flutter: ^6.0.3
|
||||||
ffmpeg_kit_flutter_audio: 6.0.3-LTS
|
ffmpeg_kit_flutter_audio: 6.0.3-LTS
|
||||||
|
|
||||||
@ -105,6 +106,9 @@ dependencies:
|
|||||||
firebase_crashlytics: ^4.0.3
|
firebase_crashlytics: ^4.0.3
|
||||||
firebase_remote_config: ^5.0.3
|
firebase_remote_config: ^5.0.3
|
||||||
|
|
||||||
|
# facebook
|
||||||
|
facebook_app_events: ^0.19.2
|
||||||
|
|
||||||
# 网络
|
# 网络
|
||||||
connectivity_plus: ^6.0.4
|
connectivity_plus: ^6.0.4
|
||||||
|
|
||||||
@ -114,8 +118,12 @@ dependencies:
|
|||||||
# 对键盘可见性变化做出反应
|
# 对键盘可见性变化做出反应
|
||||||
flutter_keyboard_visibility: ^6.0.0
|
flutter_keyboard_visibility: ^6.0.0
|
||||||
|
|
||||||
|
# 进度条
|
||||||
step_progress_indicator: ^1.0.2
|
step_progress_indicator: ^1.0.2
|
||||||
|
|
||||||
|
# 防抖、节流
|
||||||
|
easy_debounce: ^2.0.3
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
ios: true
|
ios: true
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user