feat: sync catalog, server, and MusicFree updates

This commit is contained in:
2026-07-16 18:43:22 +08:00
parent 069af30dba
commit e9bb3df906
58 changed files with 4409 additions and 206 deletions
@@ -0,0 +1,168 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="fun.upup.musicfree">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
android:extractNativeLibs="true"
>
<activity
android:name=".MainActivity"
android:theme="@style/Theme.App.SplashScreen"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="musicfree" android:host="app"/>
<data android:scheme="musicfree" android:host="install"/>
</intent-filter>
<!-- 处理音频文件 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="audio/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="audio/*" />
</intent-filter>
<!-- 处理特定音频格式 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.mp3" />
<data android:pathPattern=".*\\.MP3" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.flac" />
<data android:pathPattern=".*\\.FLAC" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.m4a" />
<data android:pathPattern=".*\\.M4A" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.wav" />
<data android:pathPattern=".*\\.WAV" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.mp3" />
<data android:pathPattern=".*\\.MP3" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.flac" />
<data android:pathPattern=".*\\.FLAC" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.m4a" />
<data android:pathPattern=".*\\.M4A" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.wav" />
<data android:pathPattern=".*\\.WAV" />
</intent-filter>
<!-- 处理JavaScript文件 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="text/javascript" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="text/javascript" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.js" />
<data android:pathPattern=".*\\.JS" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.js" />
<data android:pathPattern=".*\\.JS" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -11,7 +11,12 @@ import android.provider.Settings
import android.util.DisplayMetrics
import android.view.WindowInsets
import android.view.WindowManager
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import `fun`.upup.musicfree.MainActivity
import `fun`.upup.musicfree.R
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
@@ -23,6 +28,8 @@ import kotlin.system.exitProcess
class UtilsModule(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
private val reactContext: ReactApplicationContext = context;
private val playbackDiagnosticChannelId = "musicfree_playback_diag"
private val playbackDiagnosticNotificationId = 42042
override fun getName() = "NativeUtils"
@@ -96,6 +103,61 @@ class UtilsModule(context: ReactApplicationContext) : ReactContextBaseJavaModule
}
}
@ReactMethod
fun showPlaybackDiagnosticNotification(title: String, message: String, promise: Promise) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val hasPermission = ContextCompat.checkSelfPermission(
reactContext,
Manifest.permission.POST_NOTIFICATIONS,
) == PackageManager.PERMISSION_GRANTED
if (!hasPermission) {
promise.resolve(null)
return
}
}
val manager = NotificationManagerCompat.from(reactContext)
val channel = NotificationChannelCompat.Builder(
playbackDiagnosticChannelId,
NotificationManagerCompat.IMPORTANCE_HIGH,
)
.setName("Playback Diagnostics")
.setDescription("Playback error and stall diagnostics")
.build()
manager.createNotificationChannel(channel)
val intent = Intent(reactContext, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
val pendingIntentFlags =
android.app.PendingIntent.FLAG_UPDATE_CURRENT or
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) android.app.PendingIntent.FLAG_IMMUTABLE else 0
val pendingIntent = android.app.PendingIntent.getActivity(
reactContext,
0,
intent,
pendingIntentFlags,
)
val notification = NotificationCompat.Builder(reactContext, playbackDiagnosticChannelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build()
manager.notify(playbackDiagnosticNotificationId, notification)
promise.resolve(null)
} catch (e: Exception) {
promise.reject("show_playback_diagnostic_notification_failed", e)
}
}
@ReactMethod(isBlockingSynchronousMethod = true)
fun getWindowDimensions(): WritableMap {
val windowManager = reactApplicationContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager