mirror of
https://github.com/GrakovNe/lissen-android.git
synced 2026-07-31 10:28:12 -04:00
player screen
This commit is contained in:
@@ -80,6 +80,7 @@ dependencies {
|
||||
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
implementation(libs.hilt.android)
|
||||
implementation(libs.androidx.media3.session)
|
||||
kapt(libs.hilt.android.compiler)
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
@@ -91,6 +92,11 @@ dependencies {
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.runtime.livedata)
|
||||
|
||||
implementation("androidx.media3:media3-exoplayer:1.4.1")
|
||||
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
|
||||
implementation("androidx.media3:media3-ui:1.4.1")
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
|
||||
<application
|
||||
android:name=".LissenApplication"
|
||||
android:allowBackup="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
@@ -16,6 +17,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Lissen"
|
||||
tools:targetApi="31">
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.AppActivity"
|
||||
android:exported="true"
|
||||
@@ -25,6 +27,16 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".player.service.AudioPlayerService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
43
app/src/main/java/org/grakovne/lissen/player/MediaModule.kt
Normal file
43
app/src/main/java/org/grakovne/lissen/player/MediaModule.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package org.grakovne.lissen.player
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.session.MediaSession
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ServiceComponent
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.scopes.ServiceScoped
|
||||
import org.grakovne.lissen.ui.activity.AppActivity
|
||||
|
||||
@Module
|
||||
@InstallIn(ServiceComponent::class)
|
||||
object MediaModule {
|
||||
|
||||
@Provides
|
||||
@ServiceScoped
|
||||
fun provideExoPlayer(@ApplicationContext context: Context): ExoPlayer {
|
||||
return ExoPlayer.Builder(context).build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ServiceScoped
|
||||
fun provideMediaSession(
|
||||
@ApplicationContext context: Context,
|
||||
exoPlayer: ExoPlayer
|
||||
): MediaSession {
|
||||
val sessionActivityPendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
Intent(context, AppActivity::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
return MediaSession.Builder(context, exoPlayer)
|
||||
.setSessionActivity(sessionActivityPendingIntent)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.grakovne.lissen.player.service
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.MediaSessionService
|
||||
import androidx.media3.ui.PlayerNotificationManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.grakovne.lissen.ui.activity.AppActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
@UnstableApi
|
||||
@AndroidEntryPoint
|
||||
class AudioPlayerService : MediaSessionService() {
|
||||
|
||||
@Inject
|
||||
lateinit var mediaSession: MediaSession
|
||||
|
||||
private lateinit var playerNotificationManager: PlayerNotificationManager
|
||||
|
||||
companion object {
|
||||
private const val CHANNEL_ID = "audio_player_channel"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
|
||||
fun createNotificationChannel(context: Context) {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Audio Player",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
description = "Channel for audio player notifications"
|
||||
}
|
||||
val notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
createNotificationChannel(this)
|
||||
|
||||
playerNotificationManager = PlayerNotificationManager.Builder(
|
||||
this,
|
||||
NOTIFICATION_ID,
|
||||
CHANNEL_ID
|
||||
)
|
||||
.setMediaDescriptionAdapter(MediaDescriptionAdapterImpl())
|
||||
.setNotificationListener(NotificationListenerImpl())
|
||||
.build()
|
||||
|
||||
playerNotificationManager.setPlayer(mediaSession.player)
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession {
|
||||
return mediaSession
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
private inner class MediaDescriptionAdapterImpl :
|
||||
PlayerNotificationManager.MediaDescriptionAdapter {
|
||||
override fun getCurrentContentTitle(player: Player): String {
|
||||
return player.mediaMetadata.title.toString()
|
||||
}
|
||||
|
||||
override fun getCurrentContentText(player: Player): String {
|
||||
return player.mediaMetadata.artist.toString()
|
||||
}
|
||||
|
||||
override fun getCurrentLargeIcon(
|
||||
player: Player,
|
||||
callback: PlayerNotificationManager.BitmapCallback
|
||||
): Bitmap? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun createCurrentContentIntent(player: Player): PendingIntent? {
|
||||
val intent = Intent(this@AudioPlayerService, AppActivity::class.java)
|
||||
return PendingIntent.getActivity(
|
||||
this@AudioPlayerService,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Внутренний класс для прослушивания событий уведомления.
|
||||
*/
|
||||
private inner class NotificationListenerImpl : PlayerNotificationManager.NotificationListener {
|
||||
override fun onNotificationPosted(
|
||||
notificationId: Int,
|
||||
notification: android.app.Notification,
|
||||
ongoing: Boolean
|
||||
) {
|
||||
if (ongoing) {
|
||||
startForeground(notificationId, notification)
|
||||
} else {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ navigationCompose = "2.8.2"
|
||||
paletteKtx = "1.0.0"
|
||||
retrofit = "2.9.0"
|
||||
runtimeLivedata = "1.7.3"
|
||||
media3Session = "1.4.1"
|
||||
|
||||
[libraries]
|
||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
||||
@@ -56,6 +57,7 @@ androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtim
|
||||
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" }
|
||||
material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
androidx-media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3Session" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user