1.添加通知栏媒体控制

2.下一首上一首增加防抖
3.集成fb
4.首页单曲增加下载
5.增加后台播放(iOS仍有问题)
6.修改系统状态栏颜色
This commit is contained in:
fengshengxiong 2024-08-19 13:51:10 +08:00
parent caded892d9
commit 063e3d7c91
22 changed files with 610 additions and 361 deletions

View File

@ -9,8 +9,14 @@
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"
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
android:label="ToneSnap"
android:label="@string/app_name"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon"
android:usesCleartextTraffic="true"
@ -25,7 +31,8 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
tools:ignore="Instantiatable">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@ -39,6 +46,26 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</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.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
@ -49,6 +76,10 @@
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
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>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and

View File

@ -1,5 +1,5 @@
package com.tone.music.offline
import io.flutter.embedding.android.FlutterActivity
import com.ryanheise.audioservice.AudioServiceActivity
class MainActivity: FlutterActivity()
class MainActivity: AudioServiceActivity()

View 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>

View File

@ -490,7 +490,7 @@
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
ENABLE_BITCODE = NO;
@ -689,7 +689,7 @@
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
ENABLE_BITCODE = NO;
@ -723,7 +723,7 @@
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 17;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = UH427LWP22;
ENABLE_BITCODE = NO;

View File

@ -36,205 +36,21 @@
<string>We need access to the photo library to pick audio files.</string>
<key>NSUserTrackingUsageDescription</key>
<string>We need your permission to access the advertising identifier to provide better ad services.</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>
<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>
@ -259,5 +75,204 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<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>
</plist>

View File

@ -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/firebase/firebase_analytics_manager.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/obj_util.dart';
@ -35,6 +36,9 @@ class InterstitialAdManager {
///
bool isShowingAd = false;
/// 广
bool showAdFrontIsPlaying = false;
/// 广
void loadAdAll() {
AdConfigModel adConfigModel = MusicBox().getAdConfig();
@ -90,33 +94,39 @@ class InterstitialAdManager {
///
void _loadFailHandle(String adUnitId, String adScenes) {
AdConfigModel adConfigModel = MusicBox().getAdConfig();
var adList = _getAdList(adScenes);
if (adList != null) {
var currentItem = adList.firstWhereOrNull((e) => e.identifier == adUnitId);
if (currentItem != null) {
var nextItem = adList.firstWhereOrNull((e) => e.level == currentItem.level! + 1);
if (nextItem != null) {
// id加载失败id
loadAd(nextItem.identifier, adScenes);
} else {
// id都加载失败id
_loadSingleAd(adConfigModel.backup, adScenes);
}
} else {
var currentItem = adConfigModel.backup?.firstWhereOrNull((e) => e.identifier == adUnitId);
int interval = 0;
if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) {
interval = 15;
}
Future.delayed(Duration(seconds: interval), () {
AdConfigModel adConfigModel = MusicBox().getAdConfig();
var adList = _getAdList(adScenes);
if (adList != null) {
var currentItem = adList.firstWhereOrNull((e) => e.identifier == adUnitId);
if (currentItem != null) {
var nextItem = adConfigModel.backup?.firstWhereOrNull((e) => e.level == currentItem.level! + 1);
var nextItem = adList.firstWhereOrNull((e) => e.level == currentItem.level! + 1);
if (nextItem != null) {
// id加载失败id
loadAd(nextItem.identifier, adScenes);
} else {
if (adList.isNotEmpty) {
_loadSingleAd(adList, adScenes);
// id都加载失败id
_loadSingleAd(adConfigModel.backup, adScenes);
}
} else {
var currentItem = adConfigModel.backup?.firstWhereOrNull((e) => e.identifier == adUnitId);
if (currentItem != null) {
var nextItem = adConfigModel.backup?.firstWhereOrNull((e) => e.level == currentItem.level! + 1);
if (nextItem != null) {
loadAd(nextItem.identifier, adScenes);
} else {
if (adList.isNotEmpty) {
_loadSingleAd(adList, adScenes);
}
}
}
}
}
}
});
}
List<AdModel>? _getAdList(String adScenes) {
@ -197,6 +207,11 @@ class InterstitialAdManager {
onAdShowedFullScreenContent: (ad) {
LogUtil.d('$ad onAdShowedFullScreenContent');
isShowingAd = true;
showAdFrontIsPlaying = MusicPlayerController.to.isPlaying.value;
if (showAdFrontIsPlaying) {
MusicPlayerController.to.pause();
}
},
// 广广
onAdImpression: (ad) {
@ -217,11 +232,21 @@ class InterstitialAdManager {
onAdDismissedFullScreenContent: (ad) {
LogUtil.d('$ad onAdDismissedFullScreenContent');
isShowingAd = false;
if (showAdFrontIsPlaying) {
MusicPlayerController.to.play();
}
_closeDateTime = DateTime.now();
ad.dispose();
loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes);
_interstitialAdMap[adScenes] = null;
_interstitialAdMap.remove(adScenes);
int interval = 0;
if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) {
interval = 15;
}
Future.delayed(Duration(seconds: interval), () {
loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes);
_interstitialAdMap[adScenes] = null;
_interstitialAdMap.remove(adScenes);
});
if(onTap != null) onTap();
},

View File

@ -17,14 +17,16 @@ class ViewStateWidget extends StatelessWidget {
super.key,
required this.viewState,
required this.child,
this.cpiBgColor,
this.loadColor,
this.loadBgColor,
this.showTryAgain = false,
this.onTapTryAgain,
});
final ViewState viewState;
final Widget child;
final Color? cpiBgColor;
final Color? loadColor;
final Color? loadBgColor;
final bool showTryAgain;
final Function()? onTapTryAgain;
@ -34,7 +36,7 @@ class ViewStateWidget extends StatelessWidget {
case ViewState.normal:
return child;
case ViewState.loading:
return loadingView(backgroundColor: cpiBgColor);
return loadingView(loadColor: loadColor, loadBgColor: loadBgColor);
case ViewState.empty:
return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB(
showTryAgain: showTryAgain,
@ -48,14 +50,14 @@ class ViewStateWidget extends StatelessWidget {
///
Widget loadingView({
Color? color,
Color? backgroundColor,
Color? loadColor,
Color? loadBgColor,
}) {
return Center(
child: CircularProgressIndicator(
strokeWidth: 2.w,
color: AppConfig.appSideEnum == AppSideEnum.sideA ? color : seedColor,
backgroundColor: backgroundColor,
color: AppConfig.appSideEnum == AppSideEnum.sideB ? loadColor ?? seedColor : loadColor,
backgroundColor: loadBgColor,
),
);
}

View File

@ -11,7 +11,7 @@ class MusicCacheManager {
Config(
key,
stalePeriod: const Duration(days: 7),
maxNrOfCacheObjects: 30,
maxNrOfCacheObjects: 100,
),
);

View 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());
}
}
}

View File

@ -10,10 +10,10 @@ import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class FirebaseRemoteConfigManager {
static Future<void> getAll() async {
static Future<void> getAllConfig() async {
final remoteConfig = FirebaseRemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(minutes: 1),
fetchTimeout: const Duration(seconds: 30),
minimumFetchInterval: Duration.zero,
));
bool result = await remoteConfig.fetchAndActivate();

View File

@ -30,13 +30,13 @@ class AppConfig {
"coldLoading": [
{
"level": 1,
"identifier": "ca-app-pub-5684307632319406/5757639118",
"identifier": "",
"ad": "AdMob",
"type": "Insert"
},
{
"level": 2,
"identifier": "ca-app-pub-5684307632319406/5852437064",
"identifier": "",
"ad": "AdMob",
"type": "Insert"
}

View File

@ -25,7 +25,7 @@ class NetworkConnectivityService extends GetxService {
if (!isExecutedTask) {
isExecutedTask = true;
FirebaseRemoteConfigManager.getAll();
FirebaseRemoteConfigManager.getAllConfig();
InterstitialAdManager().loadAdAll();
// LibraryNativeAdManager().loadNativeAd();

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.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:get/get.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/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_options.dart';
import 'package:tone_snap/global/app_config.dart';
@ -25,6 +25,9 @@ import 'package:tone_snap/utils/log_util.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Hive
await initHive();
// Firebase
try {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@ -35,6 +38,8 @@ void main() async {
LogUtil.e("Firebase initialization error: $e");
}
await FacebookManager.init();
// 广 SDK
try {
MobileAds.instance.initialize();
@ -42,10 +47,17 @@ void main() async {
LogUtil.e("MobileAds initialization error: $e");
}
// Hive
await initHive();
runApp(const MyApp());
try {
await JustAudioBackground.init(
androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
androidNotificationChannelName: 'Audio playback',
androidNotificationIcon: 'mipmap/launcher_icon',
// androidNotificationOngoing: true,
androidStopForegroundOnPause: false,
);
} catch (e) {
LogUtil.e(e.toString());
}
//
SystemChrome.setPreferredOrientations([
@ -53,14 +65,16 @@ void main() async {
DeviceOrientation.portraitDown,
]);
//
if (Platform.isAndroid) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.black,
systemNavigationBarIconBrightness: Brightness.light,
));
}
//
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.black,
systemNavigationBarIconBrightness: Brightness.light,
));
runApp(const MyApp());
}
class MyApp extends StatelessWidget {

View File

@ -92,9 +92,9 @@ class LaunchController extends GetxController with GetSingleTickerProviderStateM
onTap: () {
AppConfig.appSideEnum = AppSideEnum.sideA;
Get.offNamed(AppRoutes.initialA);
FirebaseAnalyticsManager.logJumpEvent('A', '版本相同,开关关闭');
},
);
FirebaseAnalyticsManager.logJumpEvent('A', '版本相同,开关关闭');
}
void _openSideB(String reason) {
@ -105,8 +105,8 @@ class LaunchController extends GetxController with GetSingleTickerProviderStateM
MainController.to.changeTheme();
Get.offNamed(AppRoutes.initialB);
MusicBox().putIsOpenedSideB(true);
FirebaseAnalyticsManager.logJumpEvent('B', reason);
},
);
FirebaseAnalyticsManager.logJumpEvent('B', reason);
}
}

View File

@ -8,9 +8,12 @@ import 'dart:io';
import 'package:flutter/services.dart';
import 'package:get/get.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/global/app_config.dart';
import 'package:tone_snap/utils/audio_util.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class PlayerController extends GetxController {
static PlayerController get to => Get.put(PlayerController());
@ -45,11 +48,15 @@ class PlayerController extends GetxController {
if (this.filePath != filePath) {
this.filePath = filePath;
try {
final mediaItem = MediaItem(
id: filePath,
title: AppConfig.appName,
);
isReady = false;
if (filePath.contains('assets')) {
duration.value = await _player.setAsset(filePath) ?? Duration.zero;
duration.value = await _player.setAsset(filePath, tag: mediaItem) ?? Duration.zero;
} else {
duration.value = await _player.setFilePath(filePath) ?? Duration.zero;
duration.value = await _player.setFilePath(filePath, tag: mediaItem) ?? Duration.zero;
}
isReady = true;
} on PlayerException catch (e) {

View File

@ -5,8 +5,10 @@
import 'dart:async';
import 'dart:io';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:get/get.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/data/api/music_api.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/firebase/firebase_analytics_manager.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/log_util.dart';
import 'package:tone_snap/utils/num_util.dart';
@ -106,8 +107,15 @@ class MusicPlayerController extends GetxController {
if (playlist.isNotEmpty) {
FirebaseAnalyticsManager.logPlayerBpv(getMusicModel()?.videoId, getMusicModel()?.title, getMusicModel()?.subtitle);
resetPlaybackStatus();
Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
// Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
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();
var model = OfflineBox().getList().firstWhereOrNull((e) => e.videoId == getMusicModel()?.videoId);
if (model != null && ObjUtil.isNotEmpty(model.localPath)) {
@ -118,7 +126,7 @@ class MusicPlayerController extends GetxController {
FirebaseAnalyticsManager.logPlayerBFailAction('本地文件不存在');
return;
}
await _player.setFilePath(model.localPath!);
await _player.setFilePath(model.localPath!, tag: mediaItem);
} else {
//
var fileInfo = await MusicCacheManager.checkCache(getMusicModel()?.videoId);
@ -126,14 +134,14 @@ class MusicPlayerController extends GetxController {
//
LogUtil.d('读取缓存路径=${fileInfo.file.path}');
// 使
await _player.setFilePath(fileInfo.file.path);
await _player.setFilePath(fileInfo.file.path, tag: mediaItem);
} else {
//
final url = await _getMusicUrl();
if (ObjUtil.isEmpty(url)) {
return;
}
await _player.setUrl(url!);
await _player.setUrl(url!, tag: mediaItem);
//
_cacheManager.downloadFile(url, key: MusicCacheManager.getCacheKey(getMusicModel()!.videoId!)).then((fileInfo) {
LogUtil.d('缓存下载路径=${fileInfo.file.path}');
@ -175,6 +183,10 @@ class MusicPlayerController extends GetxController {
///
void _addListening() {
_player.playbackEventStream.listen((event) {},
onError: (Object e, StackTrace stackTrace) {
LogUtil.e('A stream error occurred: $e');
});
_durationSubscription = _player.durationStream.listen((duration) {
totalDuration.value = duration ?? Duration.zero;
});
@ -265,73 +277,97 @@ class MusicPlayerController extends GetxController {
}
/// /
Future<void> playPause() async {
void playPause() {
if (_player.playing) {
_player.pause();
pause();
} else {
if (processingState.value == ProcessingState.ready) {
_player.play();
}
play();
}
}
///
void play() {
if (processingState.value == ProcessingState.ready) {
_player.play();
}
}
///
void pause() {
_player.pause();
}
///
Future<void> previousTrack() async {
if (playlist.length > 1) {
switch(playMode.value) {
case PlayMode.listLoop:
currentIndex.value = (currentIndex.value - 1 + playlist.length) % playlist.length;
_startPlay();
break;
case PlayMode.random:
bool historyExist = false;
for (var i = _playHistory.length - 1; i >= 0; --i) {
var history = _playHistory[i];
MusicModel? model = playlist.firstWhereOrNull((e) => e.videoId == history);
if (model != null) {
currentIndex.value = playlist.indexOf(model);
_playHistory.remove(history);
historyExist = true;
// PlatformException
EasyDebounce.debounce(
'cutTheSong',
const Duration(milliseconds: 300),
() {
if (playlist.length > 1) {
switch(playMode.value) {
case PlayMode.listLoop:
currentIndex.value = (currentIndex.value - 1 + playlist.length) % playlist.length;
_startPlay();
break;
case PlayMode.random:
bool historyExist = false;
for (var i = _playHistory.length - 1; i >= 0; --i) {
var history = _playHistory[i];
MusicModel? model = playlist.firstWhereOrNull((e) => e.videoId == history);
if (model != null) {
currentIndex.value = playlist.indexOf(model);
_playHistory.remove(history);
historyExist = true;
break;
} else {
_playHistory.remove(history);
}
}
if (!historyExist) {
currentIndex.value = _getRandomNumber();
}
_startPlay();
break;
case PlayMode.singleCycle:
currentIndex.value = (currentIndex.value - 1 + playlist.length) % playlist.length;
_startPlay();
break;
} else {
_playHistory.remove(history);
}
}
if (!historyExist) {
currentIndex.value = _getRandomNumber();
}
_startPlay();
break;
case PlayMode.singleCycle:
currentIndex.value = (currentIndex.value - 1 + playlist.length) % playlist.length;
_startPlay();
break;
}
}
}
},
);
}
///
Future<void> nextTrack({bool manualSwitch = true}) async {
if (playlist.length > 1) {
switch(playMode.value) {
case PlayMode.listLoop:
currentIndex.value = (currentIndex.value + 1) % playlist.length;
_startPlay();
break;
case PlayMode.random:
//
_playHistory.add(getMusicModel()!.videoId!);
currentIndex.value = _getRandomNumber();
_startPlay();
break;
case PlayMode.singleCycle:
if (manualSwitch) {
currentIndex.value = (currentIndex.value + 1) % playlist.length;
_startPlay();
// PlatformException
EasyDebounce.debounce(
'cutTheSong',
const Duration(milliseconds: 300),
() {
if (playlist.length > 1) {
switch(playMode.value) {
case PlayMode.listLoop:
currentIndex.value = (currentIndex.value + 1) % playlist.length;
_startPlay();
break;
case PlayMode.random:
//
_playHistory.add(getMusicModel()!.videoId!);
currentIndex.value = _getRandomNumber();
_startPlay();
break;
case PlayMode.singleCycle:
if (manualSwitch) {
currentIndex.value = (currentIndex.value + 1) % playlist.length;
_startPlay();
}
break;
}
break;
}
}
}
},
);
}
///

View File

@ -100,24 +100,32 @@ class MusicBarView extends StatelessWidget {
],
),
),
SizedBox(width: 13.w),
SizedBox(width: 3.w),
Obx(() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: musicPlayerController.playPause,
child: Image.asset(
musicPlayerController.isPlaying.value ? Assets.sideBPausePlay : Assets.sideBStartPlay,
width: 24.w,
height: 24.w,
child: Padding(
padding: const EdgeInsets.all(10).w,
child: Image.asset(
musicPlayerController.isPlaying.value ? Assets.sideBPausePlay : Assets.sideBStartPlay,
width: 24.w,
height: 24.w,
),
),
);
}),
SizedBox(width: 26.w),
SizedBox(width: 6.w),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: musicPlayerController.nextTrack,
child: Image.asset(
Assets.sideBMusicBarNext,
width: 24.w,
height: 24.w,
child: Padding(
padding: const EdgeInsets.all(10).w,
child: Image.asset(
Assets.sideBMusicBarNext,
width: 24.w,
height: 24.w,
),
),
),
],

View File

@ -108,21 +108,23 @@ class SearchMusicView extends GetView<SearchMusicController> {
spacing: 8.w,
runSpacing: 8.h,
children: controller.historyList.map((history) {
return InkWell(
onTap: () => controller.onTapHistoryItem(history),
child: IntrinsicWidth(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
decoration: BoxDecoration(
color: const Color(0xFF1F1F1F),
borderRadius: BorderRadius.circular(30).r,
),
alignment: Alignment.center,
child: Text(
history,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
return ClipRRect(
borderRadius: BorderRadius.circular(30).r,
child: Material(
color: const Color(0xFF1F1F1F),
child: InkWell(
onTap: () => controller.onTapHistoryItem(history),
child: IntrinsicWidth(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
alignment: Alignment.center,
child: Text(
history,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
),
),
),

View File

@ -2,13 +2,23 @@
// Date: 2024/6/21
// Description: Item
import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.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/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/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/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';
class BrowseItemAtv extends StatelessWidget {
@ -31,6 +41,7 @@ class BrowseItemAtv extends StatelessWidget {
children: [
_buildCover(),
_buildContent(),
_buildDownload(),
_buildMore(),
],
),
@ -57,26 +68,63 @@ class BrowseItemAtv extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ObjUtil.getStr(musicModel.title),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 14.sp,
),
),
Obx(() {
return MusicItemMarqueeText(
text: musicModel.title,
showMarquee: MusicPlayerController.to.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
);
}),
SizedBox(height: 4.h),
Text(
ObjUtil.getStr(musicModel.subtitle),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: const Color(0xFF666666),
fontSize: 12.sp,
Obx(() {
return MusicItemMarqueeText(
text: musicModel.subtitle,
isTitle: false,
showMarquee: MusicPlayerController.to.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
);
}),
],
),
),
);
}
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);
}
}
},
),
),
],
),
),
),
);
@ -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() {
Get.bottomSheet(
MoreBottomSheetView(

View File

@ -18,7 +18,8 @@ class WebPageView extends StatelessWidget {
body: Obx(() {
return ViewStateWidget(
viewState: controller.viewState.value,
cpiBgColor: Colors.white,
loadColor: Colors.black,
loadBgColor: Colors.white,
child: WebViewWidget(controller: controller.webViewController),
);
}),

View File

@ -8,6 +8,7 @@ class AudioUtil {
static Future<void> configAudioSession() async {
final session = await AudioSession.instance;
await session.configure(const AudioSessionConfiguration.music());
await session.setActive(true);
// await session.configure(AudioSessionConfiguration(
// avAudioSessionCategory: AVAudioSessionCategory.playback,
// avAudioSessionCategoryOptions:

View File

@ -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
# 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.
version: 1.0.5+15
version: 1.0.6+17
environment:
sdk: '>=3.4.1 <4.0.0'
@ -77,6 +77,7 @@ dependencies:
audio_session: ^0.1.21
flutter_sound: ^9.4.18
just_audio: ^0.9.39
just_audio_background: ^0.0.1-beta.13
# ffmpeg_kit_flutter: ^6.0.3
ffmpeg_kit_flutter_audio: 6.0.3-LTS
@ -105,6 +106,9 @@ dependencies:
firebase_crashlytics: ^4.0.3
firebase_remote_config: ^5.0.3
# facebook
facebook_app_events: ^0.19.2
# 网络
connectivity_plus: ^6.0.4
@ -114,8 +118,12 @@ dependencies:
# 对键盘可见性变化做出反应
flutter_keyboard_visibility: ^6.0.0
# 进度条
step_progress_indicator: ^1.0.2
# 防抖、节流
easy_debounce: ^2.0.3
flutter_launcher_icons:
android: "launcher_icon"
ios: true