Код: Выделить всё
## В чем хак?
Хак заключается в ручном объявлении и обработке экземпляра PhoneStateReceiver,
Код: Выделить всё
ScreenStateReceiverКод: Выделить всё
@AndroidEntryPoint
class SpeakifyNotificationListener: NotificationListenerService() {
// ...all other declarations/business logic
private val phoneStateReceiver = PhoneStateReceiver()
private val screenStateReceiver = ScreenStateReceiver()
override fun onCreate() {
super.onCreate()
// Start the collector when the service is created.
// This ensures it's always listening as long as the service process is alive.
startListeningForNotifications()
Log.d("SpeakifyNLS", "Service created. Registering PhoneStateReceiver.")
// Define the intent filter for the broadcast we want to receive.
val intentFilter = IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
// Register the receiver dynamically.
// For Android O (API 26) and above, you must specify if the receiver is exported.
registerReceiver(phoneStateReceiver, intentFilter, RECEIVER_EXPORTED)
val screenStateFilter = IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_USER_PRESENT)
}
registerReceiver(screenStateReceiver, screenStateFilter)
}
//...other business logic
override fun onDestroy() {
super.onDestroy()
ttsManager.shutdown()
Log.d("SpeakifyNLS", "Service destroyed. Unregistering PhoneStateReceiver.")
// IMPORTANT: Always unregister the receiver to avoid memory leaks.
try {
unregisterReceiver(phoneStateReceiver)
} catch (e: Exception) {
// Can throw an exception if the receiver was never registered, so catch it.
Log.e("SpeakifyNLS", "Error unregistering PhoneStateReceiver", e)
}
try {
unregisterReceiver(screenStateReceiver)
} catch (e: Exception) {
Log.e("SpeakifyNLS", "Error unregistering ScreenStateReceiver", e)
}
}
}
Код: Выделить всё
SpeakifyNotificationListenerКод: Выделить всё
Конечно...
Код: Выделить всё
class PhoneStateReceiver: BroadcastReceiver()Но проблема также возникает с ScreenStateReceiver, код которого выглядит следующим образом:
Код: Выделить всё
class ScreenStateReceiver: BroadcastReceiver() {
private var originalNotificationVolume: Int = -1
override fun onReceive(context: Context, intent: Intent) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
if (intent.action == Intent.ACTION_SCREEN_OFF) {
Log.d("ScreenStateReceiver", "Screen OFF. Maximizing notification volume.")
// Save the current volume if it hasn't been saved yet
if (originalNotificationVolume == -1) {
originalNotificationVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
}
// Get max volume and set it
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume, 0)
}
if (intent.action == Intent.ACTION_USER_PRESENT) {
Log.d("ScreenStateReceiver", "User PRESENT. Restoring original notification volume.")
// Restore the original volume
if (originalNotificationVolume != -1) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, originalNotificationVolume, 0)
originalNotificationVolume = -1
}
}
}
}
Когда я вручную обрабатываю их жизненные циклы в SpeakifyNotificationListener и, например, выключаю экран...
Мы видим журналы этого в LogCat
Код: Выделить всё
2025-11-09 01:07:40.319 29964-30015 BufferQueueConsumer com.mikewarren.speakify D [com.mikewarren.speakify/com.mikewarren.speakify.activities.MainActivity#14605(BLAST Consumer)pid:29964](id:750c00000002,api:0,p:-1,c:29964) disconnect
2025-11-09 01:07:40.742 29964-29964 ScreenStateReceiver com.mikewarren.speakify D Screen OFF. Maximizing notification volume.
2025-11-09 01:07:42.895 29964-29999 ClerkLog com.mikewarren.speakify D Fetching token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA with options: GetTokenOptions(template=null, skipCache=false, expirationBuffer=10) and cache key: sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:42.897 29964-30028 ClerkLog com.mikewarren.speakify D Found cached token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:42.904 29964-30028 ClerkLog com.mikewarren.speakify D Cached token is still valid for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.668 29964-30028 ClerkLog com.mikewarren.speakify D startTokenRefresh() called - debugMode: false, hasConfigured: true
2025-11-09 01:07:45.674 29964-30000 ClerkLog com.mikewarren.speakify D Fetching token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA with options: GetTokenOptions(template=null, skipCache=false, expirationBuffer=10) and cache key: sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.675 29964-30031 ClerkLog com.mikewarren.speakify D Found cached token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.677 29964-29964 BufferQueueConsumer com.mikewarren.speakify D [](id:750c00000003,api:0,p:-1,c:29964) connect: controlledByApp=false
2025-11-09 01:07:45.678 29964-29964 BLASTBufferQueue com.mikewarren.speakify D [VRI[MainActivity]#3](f:0,a:0) constructor()
2025-11-09 01:07:45.678 29964-29964 BLASTBufferQueue com.mikewarren.speakify D [VRI[MainActivity]#3](f:0,a:0) update width=1080 height=2640 format=-1 mTransformHint=0
2025-11-09 01:07:45.679 29964-30031 ClerkLog com.mikewarren.speakify D Cached token is still valid for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.715 29964-30015 BLASTBufferQueue com.mikewarren.speakify D [VRI[MainActivity]#3](f:0,a:1) acquireNextBufferLocked size=1080x2640 mFrameNumber=1 applyTransaction=true mTimestamp=65901552365081(auto) mPendingTransactions.size=0 graphicBufferId=128694400057371 transform=0
2025-11-09 01:07:45.820 29964-30031 ClerkLog com.mikewarren.speakify D startTokenRefresh() called - debugMode: false, hasConfigured: true
2025-11-09 01:07:45.821 29964-30028 ClerkLog com.mikewarren.speakify D Starting background device attestation
2025-11-09 01:07:45.826 29964-30030 ClerkLog com.mikewarren.speakify D Background device attestation completed successfully
2025-11-09 01:07:45.827 29964-30002 ClerkLog com.mikewarren.speakify D Fetching token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA with options: GetTokenOptions(template=null, skipCache=false, expirationBuffer=10) and cache key: sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.827 29964-30030 ClerkLog com.mikewarren.speakify D Found cached token for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.831 29964-30030 ClerkLog com.mikewarren.speakify D Cached token is still valid for session sess_35AmZy9IRUTeryYmrWLakU2LtOA
2025-11-09 01:07:45.981 29964-29964 ScreenStateReceiver com.mikewarren.speakify D User PRESENT. Restoring original notification volume.
Обрабатывать жизненный цикл некоторых несвязанных получателей в SpeakifyNotificationListener, как это, определенно запах кода... и я не уверен, что мне нужно сделать, чтобы это исправить.>
Подробнее здесь: https://stackoverflow.com/questions/798 ... me-weird-h
Мобильная версия