diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 15e590af4..122b870eb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ - diff --git a/app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt b/app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt index 250b409b9..1602e4c98 100644 --- a/app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt +++ b/app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt @@ -17,28 +17,22 @@ package org.akanework.gramophone.logic -import android.Manifest import android.annotation.SuppressLint -import android.app.NotificationChannel -import android.app.NotificationManager import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences -import android.content.pm.PackageManager import android.graphics.Bitmap import android.media.AudioManager import android.media.audiofx.AudioEffect import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import androidx.concurrent.futures.CallbackToFutureAdapter -import androidx.core.app.ActivityCompat import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -84,7 +78,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import org.akanework.gramophone.BuildConfig import org.akanework.gramophone.R -import org.akanework.gramophone.logic.ui.MyRecyclerView import org.akanework.gramophone.logic.utils.CircularShuffleOrder import org.akanework.gramophone.logic.utils.LastPlayedManager import org.akanework.gramophone.logic.utils.LrcUtils.LrcParserOptions @@ -98,8 +91,6 @@ import org.akanework.gramophone.logic.utils.exoplayer.EndedWorkaroundPlayer import org.akanework.gramophone.logic.utils.exoplayer.GramophoneMediaSourceFactory import org.akanework.gramophone.logic.utils.exoplayer.GramophoneRenderFactory import org.akanework.gramophone.ui.MainActivity -import org.akanework.gramophone.ui.components.LegacyLyricsAdapter -import org.akanework.gramophone.ui.components.NewLyricsView import kotlin.random.Random @@ -128,13 +119,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis const val SERVICE_GET_LYRICS_LEGACY = "get_lyrics_legacy" const val SERVICE_GET_SESSION = "get_session" const val SERVICE_TIMER_CHANGED = "changed_timer" - private const val FLAG_ALWAYS_SHOW_TICKER = 0x01000000 - private const val FLAG_ONLY_UPDATE_TICKER = 0x02000000 } - private var newView: NewLyricsView? = null - private var recyclerView: MyRecyclerView? = null - private val adapter - get() = recyclerView?.adapter as LegacyLyricsAdapter? + private var lastSessionId = 0 private var mediaSession: MediaLibrarySession? = null private var controller: MediaController? = null @@ -148,7 +134,6 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis private lateinit var lastPlayedManager: LastPlayedManager private val lyricsLock = Semaphore(1) private lateinit var prefs: SharedPreferences - private var highLyric : String = "" private fun getRepeatCommand() = when (controller!!.repeatMode) { @@ -388,7 +373,6 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis headSetReceiver, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) ) - startTimer() } // When destroying, we should release server side player @@ -673,139 +657,6 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis } } - fun startTimer() { - val handler = Handler(Looper.getMainLooper()) - val runnable = object : Runnable { - override fun run() { - lyric() - handler.postDelayed(this, 100) - } - } - handler.post(runnable) // 启动定时器 - } - - fun lyric() { - val getLyricsLegacy = controller?.getLyricsLegacy() - val getLyrics = controller?.getLyrics() - updateLyrics(getLyricsLegacy, getLyrics) - val highlightedLyric = getCurrentHighlightedLyric() - if (highlightedLyric != null) { - sendlyric(highlightedLyric) - } else { - null - } - - } - - private fun sendlyric(Lyrics: String) { - if (highLyric != Lyrics ) { - highLyric = Lyrics - val isLyricUIEnabled = prefs.getBoolean("lyric_statusbarLyric", true) - if (isLyricUIEnabled == true ){ - sendMeiZuStatusBarLyric(Lyrics,isLyricUIEnabled) - } else { - sendMeiZuStatusBarLyric("",isLyricUIEnabled) - } - //Log.d(TAG,"当前高亮歌词: $Lyrics") - - } - - } - private fun sendMeiZuStatusBarLyric(Lyrics: String, open: Boolean) { - val channelId = "StatusBarLyric" - val channelName = getString(R.string.settings_lyrics_StatusBarLyric) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val importance = NotificationManager.IMPORTANCE_MIN // 设为低优先级,避免打扰用户 - val channel = NotificationChannel(channelId, channelName, importance) - channel.description = getString(R.string.settings_lyrics_StatusBarLyric) - - val notificationManager = applicationContext.getSystemService(NotificationManager::class.java) - notificationManager.createNotificationChannel(channel) - } - val notification = NotificationCompat.Builder(applicationContext, channelId) - notification.setSmallIcon(R.drawable.ic_gramophone_monochrome) // 确保图标存在且有效 - .setContentTitle(getString(R.string.settings_lyrics_StatusBarLyric)) // 通知标题,例如歌曲名称 - .setContentText(Lyrics) // 通知正文,例如歌手和专辑信息 - .setTicker(Lyrics) // 设置状态栏歌词滚动内容 - .setPriority(NotificationCompat.PRIORITY_MIN) // 低优先级,避免打扰 - .setOngoing(true) // 设置为持续通知,用户无法轻易滑掉通知 - val StatusBar = notification.build() - //发送图标 - StatusBar.extras.putInt("ticker_icon", R.drawable.ic_gramophone_monochrome) - StatusBar.extras.putBoolean("ticker_icon_switch", false) - // 设置自定义 FLAG,确保只更新状态栏歌词,不更新其他内容 - // 保持状态栏歌词滚动显示 - StatusBar.flags = StatusBar.flags.or(FLAG_ALWAYS_SHOW_TICKER) - // 只更新 Ticker(歌词),不会更新其他属性 - StatusBar.flags = StatusBar.flags.or(FLAG_ONLY_UPDATE_TICKER) - // 检查是否需要请求通知权限(针对 Android 13 及以上) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ActivityCompat.checkSelfPermission( - this, - Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - // 如果在 Service 中没有权限,我们不能直接请求权限,因此需要在启动 Service 前确保权限已被授予 - // 这里可以记录日志或通知调用方权限没有授予 - Log.e("GramophoneService", "Notification permission not granted.") - return - } else { - // 已经有权限,直接发送通知,要是开关关闭则关闭通知 - if (open == true ) { - NotificationManagerCompat.from(applicationContext).notify(2, StatusBar) - } else { - NotificationManagerCompat.from(applicationContext).cancel(2) - } - } - } else { - // 低于 Android 13,不需要请求通知权限,直接发送通知 - if (open == true ) { - NotificationManagerCompat.from(applicationContext).notify(2, StatusBar) - } else { - NotificationManagerCompat.from(applicationContext).cancel(2) - } - } - } - - - private fun updateNewIndex(): Int { - val filteredList = lyricsLegacy?.filterIndexed { _, lyric -> - (lyric.timeStamp ?: 0) <= (controller?.currentPosition ?: 0) - } - - return if (filteredList?.isNotEmpty() == true) { - filteredList.indices.maxByOrNull { - filteredList[it].timeStamp ?: 0 - } ?: -1 - } else { - -1 - } - } - - - fun getCurrentHighlightedLyric(): String? { - val position = updateNewIndex() - return if (position != -1) { - lyricsLegacy?.get(position)?.content // 返回高亮的歌词内容 - } else { - null // 没有高亮的歌词 - } - } - - - fun updateLyrics(parsedLyrics: MutableList?, parsedLyricsa: SemanticLyrics?) { - if ( parsedLyrics.toString() == "null" ){ - lyricsLegacy = SemanticLyrics.convertForLegacy(parsedLyricsa) - adapter?.updateLyrics(lyricsLegacy) - newView?.updateLyrics(null) - } else { - lyricsLegacy = parsedLyrics - adapter?.updateLyrics(lyricsLegacy) - newView?.updateLyrics(null) - - } - } - @SuppressLint("MissingPermission", "NotificationPermission") // only used on S/S_V2 override fun onForegroundServiceStartNotAllowedException() { Log.w(TAG, "Failed to resume playback :/") diff --git a/app/src/main/kotlin/org/akanework/gramophone/ui/fragments/settings/ExperimentalSettingsTopFragment.kt b/app/src/main/kotlin/org/akanework/gramophone/ui/fragments/settings/ExperimentalSettingsTopFragment.kt index 574983807..47f47360a 100644 --- a/app/src/main/kotlin/org/akanework/gramophone/ui/fragments/settings/ExperimentalSettingsTopFragment.kt +++ b/app/src/main/kotlin/org/akanework/gramophone/ui/fragments/settings/ExperimentalSettingsTopFragment.kt @@ -17,22 +17,8 @@ package org.akanework.gramophone.ui.fragments.settings -import android.Manifest -import android.content.Intent -import android.content.SharedPreferences -import android.content.pm.PackageManager -import android.net.Uri -import android.os.Build import android.os.Bundle -import android.provider.Settings -import android.util.Log -import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog -import androidx.core.app.ActivityCompat import androidx.preference.Preference -import androidx.preference.PreferenceManager -import androidx.preference.SwitchPreferenceCompat import org.akanework.gramophone.BuildConfig import org.akanework.gramophone.R import org.akanework.gramophone.ui.fragments.BasePreferenceFragment @@ -44,28 +30,12 @@ class ExperimentalSettingsFragment : BaseSettingFragment(R.string.settings_exper class ExperimentalSettingsTopFragment : BasePreferenceFragment() { private lateinit var e: Exception - private val prefs by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()) } - - // Activity Result API 注册权限请求 - private val requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted: Boolean -> - if (isGranted) { - // 用户授予权限,启用状态栏歌词功能 - enableStatusBarLyric() - } else { - // 用户拒绝权限,显示提示 - Toast.makeText(requireContext(), "未授予通知权限,无法启用状态栏歌词", Toast.LENGTH_LONG).show() - disableLyricUISetting() - } - } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings_experimental, rootKey) findPreference("crash")!!.isVisible = BuildConfig.DEBUG if (BuildConfig.DEBUG) e = RuntimeException("skill issue") - prefs.registerOnSharedPreferenceChangeListener(preferenceChangeListener) } override fun onPreferenceTreeClick(preference: Preference): Boolean { @@ -73,73 +43,5 @@ class ExperimentalSettingsTopFragment : BasePreferenceFragment() { throw IllegalArgumentException("I crashed your app >:)", e) } return super.onPreferenceTreeClick(preference) - if (preference.key == "enable_status_bar_lyric") { - // 检查权限状态并请求权限 - checkAndRequestNotificationPermission() - return true - } - } - - private fun checkAndRequestNotificationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - when { - ActivityCompat.checkSelfPermission( - requireContext(), - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED -> { - enableStatusBarLyric() - } - shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) -> { - // 如果用户之前拒绝了权限请求但没有选择“永不提醒” - requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - } - else -> { - // 用户选择“永不提醒”,显示提示并关闭开关 - prefs.edit().putBoolean("lyric_statusbarLyric", false).apply() - showPermissionDeniedMessageWithSettings() - } - } - } else { - enableStatusBarLyric() - } - } - - private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> - if (key == "lyric_statusbarLyric") { - val isLyricUIEnabled = prefs.getBoolean("lyric_statusbarLyric", true) - if (isLyricUIEnabled) { - checkAndRequestNotificationPermission() - } else { - disableLyricUISetting() - } - } - } - - private fun disableLyricUISetting() { - // 更新开关状态并通知UI - prefs.edit().putBoolean("lyric_statusbarLyric", false).apply() - findPreference("lyric_statusbarLyric")?.isChecked = false - Toast.makeText(requireContext(),getString(R.string.settings_lyrics_StatusBarLyric_disable), Toast.LENGTH_SHORT).show() - } - - private fun enableStatusBarLyric() { - // 启用状态栏歌词的提示 - // 发送歌词的地方有单独判断 - Toast.makeText(requireContext(),getString(R.string.settings_lyrics_StatusBarLyric_enabled), Toast.LENGTH_SHORT).show() - } - - private fun showPermissionDeniedMessageWithSettings() { - AlertDialog.Builder(requireContext()) - .setTitle(getString(R.string.settings_lyrics_StatusBarLyric_permission_denied)) - .setMessage(getString(R.string.settings_lyrics_StatusBarLyric_open_permissions)) - .setPositiveButton(getString(R.string.settings_go_to_the_settings)) { _, _ -> - // 跳转到应用的权限设置页面 - val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", requireContext().packageName, null) - } - startActivity(intent) - } - .setNegativeButton(getString(R.string.settings_cancel), null) - .show() } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ade0413fe..87a12ded5 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -168,12 +168,4 @@ 主屏幕 配置标签顺序 应用将在启动时显示第一个标签。分隔符后的标签将被隐藏。 - 状态栏歌词 - 打开 状态栏歌词 (某些系统可用,需打开通知) - 没有通知权限 - 启用状态栏歌词功能需要通知权限。请在系统设置中手动启用该权限。 - 状态栏歌词已打开 - 状态栏歌词已经关闭 - 去设置 - 取消 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8704cbb4f..63a6894b8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -198,12 +198,4 @@ Enable new lyric parser that supports word-by-word synchronisation, speaker indicator, offset and more Enable new lyric UI Experimental UI to display word-by-word lyric with advanced features - Status Bar Lyric - Enable StatusBarLyric (Available on some systems, requires notifications to be turned on) - Notification permission denied - Notification permission is required to enable the status bar lyrics feature. Please enable the permission manually in the system settings. - Status bar lyrics enabled - Status bar lyrics disable - Go to the Settings - Cancel diff --git a/app/src/main/res/xml/settings_experimental.xml b/app/src/main/res/xml/settings_experimental.xml index 3796f1706..981bef19e 100644 --- a/app/src/main/res/xml/settings_experimental.xml +++ b/app/src/main/res/xml/settings_experimental.xml @@ -58,17 +58,6 @@ android:widgetLayout="@layout/preference_switch_widget" app:iconSpaceReserved="false" /> - - - - \ No newline at end of file