Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #21 from you-apps/local-music
Browse files Browse the repository at this point in the history
Local music
  • Loading branch information
SuhasDissa authored Oct 5, 2023
2 parents 5197fc5 + 67fc913 commit 8db9ff1
Show file tree
Hide file tree
Showing 64 changed files with 1,900 additions and 790 deletions.
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- The local music player requires these permissions to access audio files -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<application
android:name=".MellowMusicApplication"
android:allowBackup="true"
Expand Down
64 changes: 18 additions & 46 deletions app/src/main/java/app/suhasdissa/mellowmusic/AppContainer.kt
Original file line number Diff line number Diff line change
@@ -1,69 +1,41 @@
package app.suhasdissa.mellowmusic

import android.content.ContentResolver
import androidx.media3.session.MediaController
import app.suhasdissa.mellowmusic.backend.api.PipedApi
import app.suhasdissa.mellowmusic.backend.database.SongDatabase
import app.suhasdissa.mellowmusic.backend.repository.AuthRepository
import app.suhasdissa.mellowmusic.backend.repository.AuthRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.ChannelRepository
import app.suhasdissa.mellowmusic.backend.repository.ChannelRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.PlaylistRepository
import app.suhasdissa.mellowmusic.backend.repository.PlaylistRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.RadioRepository
import app.suhasdissa.mellowmusic.backend.repository.RadioRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.SearchRepository
import app.suhasdissa.mellowmusic.backend.repository.SearchRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.SongRepository
import app.suhasdissa.mellowmusic.backend.repository.SongRepositoryImpl
import app.suhasdissa.mellowmusic.backend.repository.LocalMusicRepository
import app.suhasdissa.mellowmusic.backend.repository.PipedMusicRepository
import app.suhasdissa.mellowmusic.backend.repository.SongDatabaseRepository
import app.suhasdissa.mellowmusic.backend.repository.SongDatabaseRepositoryImpl
import com.google.common.util.concurrent.ListenableFuture
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit

interface AppContainer {
val database: SongDatabase
val searchRepository: SearchRepository
val songRepository: SongRepository
val radioRepository: RadioRepository
val playlistRepository: PlaylistRepository
val channelRepository: ChannelRepository
val songDatabaseRepository: SongDatabaseRepository
val pipedMusicRepository: PipedMusicRepository
val localMusicRepository: LocalMusicRepository
val authRepository: AuthRepository
val controllerFuture: ListenableFuture<MediaController>
val pipedApi: PipedApi
val contentResolver: ContentResolver
}

class DefaultAppContainer(
override val database: SongDatabase,
override val controllerFuture: ListenableFuture<MediaController>
override val controllerFuture: ListenableFuture<MediaController>,
override val contentResolver: ContentResolver
) : AppContainer {

private val json = Json { ignoreUnknownKeys = true }

private val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://pipedapi.kavin.rocks/")
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()

override val pipedApi: PipedApi = retrofit
.create(PipedApi::class.java)

override val searchRepository: SearchRepository by lazy {
SearchRepositoryImpl(database.searchDao(), database.songsDao(), pipedApi)
}
override val songRepository: SongRepository by lazy {
SongRepositoryImpl(database.songsDao(), pipedApi)
}
override val radioRepository: RadioRepository by lazy {
RadioRepositoryImpl(database.songsDao(), pipedApi)
override val songDatabaseRepository: SongDatabaseRepository by lazy {
SongDatabaseRepositoryImpl(database.songsDao())
}
override val playlistRepository: PlaylistRepository by lazy {
PlaylistRepositoryImpl(pipedApi)
override val pipedMusicRepository: PipedMusicRepository by lazy {
PipedMusicRepository(database.songsDao(), database.searchDao())
}
override val channelRepository: ChannelRepository by lazy {
ChannelRepositoryImpl(pipedApi)
override val localMusicRepository: LocalMusicRepository by lazy {
LocalMusicRepository(contentResolver, database.searchDao())
}
override val authRepository: AuthRepository by lazy {
AuthRepositoryImpl(pipedApi)
AuthRepositoryImpl()
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/app/suhasdissa/mellowmusic/Destination.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package app.suhasdissa.mellowmusic

sealed class Destination(val route: String) {
object PipedMusic : Destination("piped_music")
object LocalMusic : Destination("local_music")
object YoutubeMusic : Destination("yt_music")
object OnlineSearch : Destination("online_search")
object LocalSearch : Destination("local_search")
object Settings : Destination("settings")
object About : Destination("about")
object NetworkSettings : Destination("net_settings")
object DatabaseSettings : Destination("database_settings")
object Playlists : Destination("playlist_screen")
object LocalPlaylists : Destination("local_playlist_screen")
object Artist : Destination("artist")
}
37 changes: 0 additions & 37 deletions app/src/main/java/app/suhasdissa/mellowmusic/Destinations.kt

This file was deleted.

34 changes: 33 additions & 1 deletion app/src/main/java/app/suhasdissa/mellowmusic/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Surface
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.core.net.toUri
import androidx.navigation.compose.rememberNavController
import app.suhasdissa.mellowmusic.backend.viewmodel.PlayerViewModel
import app.suhasdissa.mellowmusic.ui.components.NavDrawerContent
import app.suhasdissa.mellowmusic.ui.theme.LibreMusicTheme
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

Expand All @@ -29,7 +39,29 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.surface
) {
AppNavHost(navHostController = navHostController)
val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
var currentDestination by remember {
mutableStateOf<Destination>(Destination.PipedMusic)
}
ModalNavigationDrawer(
drawerState = drawerState,
gesturesEnabled = drawerState.isOpen,
drawerContent = {
NavDrawerContent(
currentDestination = currentDestination,
onDestinationSelected = {
scope.launch {
drawerState.close()
}
navHostController.navigateTo(it.route)
currentDestination = it
}
)
}
) {
AppNavHost(navHostController = navHostController)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MellowMusicApplication : Application(), ImageLoaderFactory {
ComponentName(this, PlayerService::class.java)
)
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
container = DefaultAppContainer(database, controllerFuture)
container = DefaultAppContainer(database, controllerFuture, contentResolver)
preferences.registerOnSharedPreferenceChangeListener(listener)
UpdateUtil.getCurrentVersion(this.applicationContext)
}
Expand Down
48 changes: 37 additions & 11 deletions app/src/main/java/app/suhasdissa/mellowmusic/NavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package app.suhasdissa.mellowmusic
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import app.suhasdissa.mellowmusic.backend.viewmodel.LocalSearchViewModel
import app.suhasdissa.mellowmusic.backend.viewmodel.LocalSongViewModel
import app.suhasdissa.mellowmusic.backend.viewmodel.PipedSearchViewModel
import app.suhasdissa.mellowmusic.ui.screens.home.HomeScreen
import app.suhasdissa.mellowmusic.ui.screens.search.AlbumScreen
import app.suhasdissa.mellowmusic.ui.screens.search.ArtistScreen
import app.suhasdissa.mellowmusic.ui.screens.search.PlaylistScreen
import app.suhasdissa.mellowmusic.ui.screens.search.LocalSearchScreen
import app.suhasdissa.mellowmusic.ui.screens.search.SearchScreen
import app.suhasdissa.mellowmusic.ui.screens.settings.AboutScreen
import app.suhasdissa.mellowmusic.ui.screens.settings.DatabaseSettingsScreen
Expand All @@ -20,49 +25,70 @@ fun AppNavHost(navHostController: NavHostController) {
val viewModelStoreOwner = LocalViewModelStoreOwner.current!!
NavHost(
navController = navHostController,
startDestination = Home.route
startDestination = Destination.PipedMusic.route
) {
composable(route = Home.route) {
composable(route = Destination.PipedMusic.route) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
HomeScreen(onNavigate = { destination ->
navHostController.navigateTo(destination.route)
})
}
}

composable(route = Search.route) {
composable(
route = Destination.OnlineSearch.route
) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
SearchScreen(onNavigate = {
navHostController.navigateTo(it.route)
})
}
}
composable(
route = Destination.LocalSearch.route
) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
LocalSearchScreen(onNavigate = {
navHostController.navigateTo(it.route)
})
}
}

composable(route = Settings.route) {
composable(route = Destination.Settings.route) {
SettingsScreen(onNavigate = { route ->
navHostController.navigateTo(route)
})
}

composable(route = About.route) {
composable(route = Destination.About.route) {
AboutScreen()
}

composable(route = NetworkSettings.route) {
composable(route = Destination.NetworkSettings.route) {
NetworkSettingsScreen()
}

composable(route = DatabaseSettings.route) {
composable(route = Destination.DatabaseSettings.route) {
DatabaseSettingsScreen()
}

composable(Playlists.route) {
composable(Destination.Playlists.route) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: PipedSearchViewModel =
viewModel(factory = PipedSearchViewModel.Factory)
AlbumScreen(searchViewModel.albumInfoState)
}
}

composable(Destination.LocalPlaylists.route) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
PlaylistScreen()
val searchViewModel: LocalSearchViewModel =
viewModel(factory = LocalSongViewModel.Factory)
AlbumScreen(searchViewModel.albumInfoState)
}
}

composable(route = Artist.route) {
composable(route = Destination.Artist.route) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
ArtistScreen(onNavigate = {
navHostController.navigateTo(it.route)
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/app/suhasdissa/mellowmusic/backend/data/Album.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.suhasdissa.mellowmusic.backend.data

import android.net.Uri

data class Album(
val id: String,
val title: String,
val thumbnailUri: Uri? = null,
val artistsText: String,
val numberOfSongs: Int? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package app.suhasdissa.mellowmusic.backend.data

import android.net.Uri

data class Artist(
val id: String,
val artistsText: String,
val thumbnailUri: Uri? = null,
val description: String? = null
)
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package app.suhasdissa.mellowmusic.backend.database.entities
package app.suhasdissa.mellowmusic.backend.data

import androidx.room.Entity
import androidx.room.PrimaryKey
import android.net.Uri

@Entity
data class Song(
@PrimaryKey val id: String,
val id: String,
val title: String,
val artistsText: String? = null,
val durationText: String?,
val thumbnailUrl: String?,
val likedAt: Long? = null,
val totalPlayTimeMs: Long = 0
val thumbnailUri: Uri? = null,
val albumId: Long? = null,
val artistId: Long? = null
) {
fun toggleLike(): Song {
return copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import app.suhasdissa.mellowmusic.backend.database.dao.RawDao
import app.suhasdissa.mellowmusic.backend.database.dao.SearchDao
import app.suhasdissa.mellowmusic.backend.database.dao.SongsDao
import app.suhasdissa.mellowmusic.backend.database.entities.SearchQuery
import app.suhasdissa.mellowmusic.backend.database.entities.Song
import app.suhasdissa.mellowmusic.backend.database.entities.SongEntity

@Database(
entities = [
Song::class,
SongEntity::class,
SearchQuery::class
],
version = 1,
Expand Down
Loading

0 comments on commit 8db9ff1

Please sign in to comment.