Skip to content

Commit

Permalink
Added Google Assistant support, shifted player stuff to player
Browse files Browse the repository at this point in the history
  • Loading branch information
brahmkshatriya committed Feb 17, 2024
1 parent 64b5a41 commit 44f60d4
Show file tree
Hide file tree
Showing 22 changed files with 520 additions and 202 deletions.
24 changes: 18 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />

<application
Expand All @@ -24,23 +26,33 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MUSIC_PLAYER" />

<category android:name="android.intent.category.APP_MUSIC" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<service
android:name=".PlaybackService"
android:name=".player.PlaybackService"
android:exported="true"
android:foregroundServiceType="mediaPlayback"
android:permission="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="androidx.media3.session.MediaLibraryService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>

Expand Down
11 changes: 6 additions & 5 deletions app/src/main/java/dev/brahmkshatriya/echo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.media3.session.MediaController
import androidx.media3.session.MediaBrowser
import androidx.media3.session.SessionToken
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationBarView
import com.google.common.util.concurrent.MoreExecutors
import dagger.hilt.android.AndroidEntryPoint
import dev.brahmkshatriya.echo.databinding.ActivityMainBinding
import dev.brahmkshatriya.echo.ui.player.initPlayer
import dev.brahmkshatriya.echo.player.PlaybackService
import dev.brahmkshatriya.echo.player.initPlayer
import dev.brahmkshatriya.echo.ui.utils.checkPermissions
import dev.brahmkshatriya.echo.ui.utils.emit
import dev.brahmkshatriya.echo.ui.utils.updateBottomMarginWithSystemInsets
Expand Down Expand Up @@ -46,9 +47,9 @@ class MainActivity : AppCompatActivity() {
updateBottomMarginWithSystemInsets(binding.navHostFragment)

val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
val controllerFuture = MediaBrowser.Builder(this, sessionToken).buildAsync()
val listener = Runnable { initPlayer(this, controllerFuture.get()) }
controllerFuture.addListener(listener, MoreExecutors.directExecutor())
controllerFuture.addListener(listener, ContextCompat.getMainExecutor(this))
}

override fun onNewIntent(intent: Intent?) {
Expand Down
140 changes: 0 additions & 140 deletions app/src/main/java/dev/brahmkshatriya/echo/PlaybackService.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package dev.brahmkshatriya.echo.data.clients

import android.net.Uri
import dev.brahmkshatriya.echo.data.models.StreamableAudio
import dev.brahmkshatriya.echo.data.models.Track

interface TrackClient {
suspend fun getTrack(uri: String): Track?
suspend fun getTrack(uri: Uri): Track?
suspend fun getStreamable(track: Track): StreamableAudio

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.brahmkshatriya.echo.data.extensions

import android.content.Context
import android.net.Uri
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
Expand All @@ -9,8 +10,8 @@ import androidx.paging.PagingState
import dev.brahmkshatriya.echo.data.clients.HomeFeedClient
import dev.brahmkshatriya.echo.data.clients.SearchClient
import dev.brahmkshatriya.echo.data.clients.TrackClient
import dev.brahmkshatriya.echo.data.models.MediaItem.Companion.toMediaItem
import dev.brahmkshatriya.echo.data.models.MediaItem.Companion.toMediaItemsContainer
import dev.brahmkshatriya.echo.data.models.EchoMediaItem.Companion.toMediaItem
import dev.brahmkshatriya.echo.data.models.EchoMediaItem.Companion.toMediaItemsContainer
import dev.brahmkshatriya.echo.data.models.MediaItemsContainer
import dev.brahmkshatriya.echo.data.models.QuickSearchItem
import dev.brahmkshatriya.echo.data.models.StreamableAudio
Expand All @@ -28,11 +29,12 @@ class OfflineExtension(val context: Context) : SearchClient, TrackClient, HomeFe
override suspend fun quickSearch(query: String): List<QuickSearchItem> = listOf()

override suspend fun search(query: String): Flow<PagingData<MediaItemsContainer>> = flow {
val albums = LocalAlbum.search(context, query, 1, 50)
val trimmed = query.trim()
val albums = LocalAlbum.search(context, trimmed, 1, 50)
.map { it.toMediaItem() }.ifEmpty { null }
val tracks = LocalTrack.search(context, query, 1, 50)
val tracks = LocalTrack.search(context, trimmed, 1, 50)
.map { it.toMediaItem() }.ifEmpty { null }
val artists = LocalArtist.search(context, query, 1, 50)
val artists = LocalArtist.search(context, trimmed, 1, 50)
.map { it.toMediaItem() }.ifEmpty { null }

val result = listOfNotNull(
Expand Down Expand Up @@ -88,8 +90,8 @@ class OfflineExtension(val context: Context) : SearchClient, TrackClient, HomeFe
}
}

override suspend fun getTrack(uri: String): Track {
return LocalTrack.get(context, uri) ?: throw IOException("Track not found")
override suspend fun getTrack(uri: Uri): Track? {
return LocalTrack.get(context, uri)
}

override suspend fun getStreamable(track: Track): StreamableAudio {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package dev.brahmkshatriya.echo.data.models

sealed class MediaItem {
data class TrackItem(val track: Track) : MediaItem()
data class AlbumItem(val album: Album.WithCover) : MediaItem()
data class ArtistItem(val artist: Artist.WithCover) : MediaItem()
data class PlaylistItem(val playlist: Playlist.WithCover) : MediaItem()
sealed class EchoMediaItem {
data class TrackItem(val track: Track) : EchoMediaItem()
data class AlbumItem(val album: Album.WithCover) : EchoMediaItem()
data class ArtistItem(val artist: Artist.WithCover) : EchoMediaItem()
data class PlaylistItem(val playlist: Playlist.WithCover) : EchoMediaItem()

companion object {
fun Track.toMediaItem() = TrackItem(this)
fun Album.WithCover.toMediaItem() = AlbumItem(this)
fun Artist.WithCover.toMediaItem() = ArtistItem(this)
fun Playlist.WithCover.toMediaItem() = PlaylistItem(this)

fun List<MediaItem>.toMediaItemsContainer(title: String, subtitle: String? = null)
fun List<EchoMediaItem>.toMediaItemsContainer(title: String, subtitle: String? = null)
= MediaItemsContainer.Category(title, this, subtitle)
}

override fun equals(other: Any?): Boolean {
if(other is MediaItem) {
if(other is EchoMediaItem) {
return when(this) {
is TrackItem -> this.track.uri == (other as? TrackItem)?.track?.uri
is AlbumItem -> this.album.uri == (other as? AlbumItem)?.album?.uri
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dev.brahmkshatriya.echo.data.models
sealed class MediaItemsContainer {
data class Category(
val title: String,
val list: List<MediaItem>,
val list: List<EchoMediaItem>,
val subtitle: String? = null
) : MediaItemsContainer()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package dev.brahmkshatriya.echo.data.models

sealed class QuickSearchItem {
data class SearchQueryItem(val query: String) : QuickSearchItem()
data class SearchMediaItem(val mediaItem: MediaItem) : QuickSearchItem()
data class SearchMediaItem(val mediaItem: EchoMediaItem) : QuickSearchItem()
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ interface LocalTrack {
return tracks
}

fun get(context: Context, uri: String): Track? {
val id = uri.substringAfterLast('/')
fun get(context: Context, uri: Uri): Track? {
val id = uri.lastPathSegment ?: return null
val whereCondition = "${MediaStore.Audio.Media._ID} = ?"
val selectionArgs = arrayOf(id)
return context.queryTracks(whereCondition, selectionArgs, 0, 1).firstOrNull()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.brahmkshatriya.echo.ui.player
package dev.brahmkshatriya.echo.player

import android.animation.ObjectAnimator
import android.content.res.Resources
Expand All @@ -12,7 +12,7 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.media3.common.Player.REPEAT_MODE_ALL
import androidx.media3.common.Player.REPEAT_MODE_OFF
import androidx.media3.common.Player.REPEAT_MODE_ONE
import androidx.media3.session.MediaController
import androidx.media3.session.MediaBrowser
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
Expand All @@ -23,8 +23,8 @@ import com.google.android.material.checkbox.MaterialCheckBox.OnCheckedStateChang
import com.google.android.material.checkbox.MaterialCheckBox.STATE_CHECKED
import dev.brahmkshatriya.echo.MainActivity
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.ui.player.PlayerHelper.Companion.mediaItemBuilder
import dev.brahmkshatriya.echo.ui.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.player.PlayerHelper.Companion.mediaItemBuilder
import dev.brahmkshatriya.echo.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.ui.utils.dpToPx
import dev.brahmkshatriya.echo.ui.utils.emit
import dev.brahmkshatriya.echo.ui.utils.loadInto
Expand All @@ -35,7 +35,7 @@ import kotlin.math.max

fun initPlayer(
activity: MainActivity,
player: MediaController
player: MediaBrowser
) {
val playerBinding = activity.binding.bottomPlayer
val container = activity.binding.bottomPlayerContainer as View
Expand Down Expand Up @@ -68,8 +68,10 @@ fun initPlayer(
bottomBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
PlayerBackButtonHelper.playerCollapsed.value = newState
if (newState == STATE_HIDDEN)
playerViewModel.clearQueue()
when (newState) {
STATE_HIDDEN -> playerViewModel.clearQueue()
else -> bottomBehavior.isHideable = newState != STATE_EXPANDED
}
}

override fun onSlide(bottomSheet: View, slideOffset: Float) {
Expand Down
Loading

0 comments on commit 44f60d4

Please sign in to comment.