├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── ids.xml │ │ │ │ ├── themes_light.xml │ │ │ │ ├── themes_inverted.xml │ │ │ │ ├── preloaded_fonts.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── styles.xml │ │ │ │ └── font_certs.xml │ │ │ ├── drawable │ │ │ │ ├── saxo.jpg │ │ │ │ ├── cover1.jpg │ │ │ │ ├── cover2.png │ │ │ │ ├── next.xml │ │ │ │ ├── previous.xml │ │ │ │ ├── volume_available.xml │ │ │ │ ├── ic_invert_theme.xml │ │ │ │ ├── favorite_border.xml │ │ │ │ ├── repeat.xml │ │ │ │ ├── pause.xml │ │ │ │ ├── play_icon.xml │ │ │ │ ├── playlist.xml │ │ │ │ ├── ic_shuffle_bordered.xml │ │ │ │ ├── ic_overflow_menu.xml │ │ │ │ ├── shuffle.xml │ │ │ │ ├── ic_sound_bars.xml │ │ │ │ ├── ic_if_speaker.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── font │ │ │ │ ├── roboto_light.ttf │ │ │ │ └── roboto.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── fragment_playlist.xml │ │ │ │ ├── fragment_favorites.xml │ │ │ │ ├── fragment_artist_info.xml │ │ │ │ ├── tabs_main.xml │ │ │ │ ├── fragment_artists.xml │ │ │ │ ├── fragment_fragment_album.xml │ │ │ │ ├── current_songs_item.xml │ │ │ │ ├── artist_track_item.xml │ │ │ │ ├── artist_item.xml │ │ │ │ ├── album_item.xml │ │ │ │ ├── activity_player.xml │ │ │ │ ├── fragment_tracks.xml │ │ │ │ ├── track.xml │ │ │ │ ├── controls.xml │ │ │ │ ├── fragment_album_details.xml │ │ │ │ └── fragment_info.xml │ │ │ ├── menu │ │ │ │ ├── artist_sorting.xml │ │ │ │ ├── track_sorting.xml │ │ │ │ ├── menu_player.xml │ │ │ │ └── album_sorting.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── ic_launcher-web.png │ │ ├── java │ │ │ └── com │ │ │ │ └── revosleap │ │ │ │ └── proxima │ │ │ │ ├── callbacks │ │ │ │ ├── BXColor.kt │ │ │ │ └── PlayerAdapter.kt │ │ │ │ ├── models │ │ │ │ ├── PlayList.kt │ │ │ │ ├── Song.kt │ │ │ │ ├── Album.kt │ │ │ │ └── Artist.kt │ │ │ │ ├── utils │ │ │ │ ├── utils │ │ │ │ │ ├── Universal.kt │ │ │ │ │ ├── UniversalUtils.kt │ │ │ │ │ ├── AlbumProvider.kt │ │ │ │ │ ├── AudioUtils.kt │ │ │ │ │ ├── WrappedAsyncTaskLoader.kt │ │ │ │ │ ├── PlayListProvider.kt │ │ │ │ │ ├── PreferenceHelper.kt │ │ │ │ │ ├── GetAudio.kt │ │ │ │ │ ├── EqualizerUtils.kt │ │ │ │ │ ├── GlideLoader.kt │ │ │ │ │ ├── ArtistProvider.kt │ │ │ │ │ └── SongProvider.kt │ │ │ │ ├── playback │ │ │ │ │ ├── PlaybackInfoListener.kt │ │ │ │ │ └── BXNotificationManager.kt │ │ │ │ └── adapters │ │ │ │ │ ├── ArtistTabAdapter.kt │ │ │ │ │ └── MainTabsAdapter.kt │ │ │ │ ├── ui │ │ │ │ ├── viewholders │ │ │ │ │ └── TrackViewHolder.kt │ │ │ │ └── fragments │ │ │ │ │ ├── FragmentPlaylist.kt │ │ │ │ │ ├── FragmentFavorites.kt │ │ │ │ │ ├── FragmentArtistInfo.kt │ │ │ │ │ ├── FragmentArtistAlbum.kt │ │ │ │ │ ├── FragmentArtistTrack.kt │ │ │ │ │ ├── FragmentArtists.kt │ │ │ │ │ ├── FragmentAlbum.kt │ │ │ │ │ ├── FragmentAlbumInfo.kt │ │ │ │ │ ├── FragmentTracks.kt │ │ │ │ │ └── FragmentInfo.kt │ │ │ │ └── services │ │ │ │ └── MusicPlayerService.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── revosleap │ │ │ └── proxima │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── revosleap │ │ └── proxima │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── alpha_02.png ├── alpha_03.png ├── alpha_04.png ├── alpha_05.png ├── home_alpha_01.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── caches │ └── build_file_checksums.ser ├── vcs.xml ├── misc.xml ├── runConfigurations.xml ├── gradle.xml └── codeStyles │ └── Project.xml ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /alpha_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/alpha_02.png -------------------------------------------------------------------------------- /alpha_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/alpha_03.png -------------------------------------------------------------------------------- /alpha_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/alpha_04.png -------------------------------------------------------------------------------- /alpha_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/alpha_05.png -------------------------------------------------------------------------------- /home_alpha_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/home_alpha_01.png -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/saxo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/drawable/saxo.jpg -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /app/src/main/res/drawable/cover1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/drawable/cover1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/cover2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/drawable/cover2.png -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/font/roboto_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/themes_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carloscj6/BXPlayer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/themes_inverted.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/callbacks/BXColor.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.callbacks 2 | 3 | interface BXColor { 4 | fun songColor(color:Int) 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/models/PlayList.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.models 2 | 3 | class PlayList { 4 | var title:String="" 5 | var path:String="" 6 | var dateAdded:Long=0L 7 | } -------------------------------------------------------------------------------- /app/src/main/res/values/preloaded_fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @font/roboto 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 256dp 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 01 17:16:18 EAT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_playlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_favorites.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/next.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/previous.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/font/roboto.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/artist_sorting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/models/Song.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.models 2 | 3 | import java.io.Serializable 4 | 5 | class Song : Serializable { 6 | var path: String? = "" 7 | var title: String? = "" 8 | var artist: String? = "" 9 | var albumName: String? = "" 10 | var artistId: Int = 0 11 | var songYear: Int = 0 12 | var duration: Long = 0L 13 | var trackNumber:Int=0 14 | } 15 | -------------------------------------------------------------------------------- /app/src/test/java/com/revosleap/proxima/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * @see [Testing documentation](http://d.android.com/tools/testing) 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, (2 + 2).toLong()) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/track_sorting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_player.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/album_sorting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/volume_available.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/Universal.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | object Universal { 4 | const val infoAction = "com.revosleap.startInfo" 5 | const val SORT_BY_YEAR = "year" 6 | const val SORT_BY_ARTIST = "artist" 7 | const val SORT_BY_TITLE = "title" 8 | const val SORT_BY_NAME = "name" 9 | const val SORT_BY_SONGS = "songs" 10 | const val ALBUM_BUNDLE= "album.bundle" 11 | const val SONGS_BUNDLE="songs.bundle" 12 | const val ALBUMS_BUNDLE="albums.bundle" 13 | const val ALBUM_INFO_TAG ="album.fragment_info" 14 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BX Player 3 | 4 | Settings 5 | Play a song first! 6 | Equalizer 7 | Artist 8 | Date 9 | Title 10 | Release 11 | Name 12 | Songs 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/models/Album.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.models 2 | 3 | import java.io.Serializable 4 | 5 | class Album:Serializable { 6 | val songs: MutableList = mutableListOf() 7 | 8 | val title: String 9 | get() = firstSong.albumName!! 10 | 11 | val artistId: Int 12 | get() = firstSong.artistId 13 | 14 | val artistName: String? 15 | get() = firstSong.artist 16 | 17 | val year: Int 18 | get() = firstSong.songYear 19 | 20 | val songCount: Int 21 | get() = songs.size 22 | 23 | private val firstSong: Song 24 | get() = songs[0] 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_invert_theme.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/viewholders/TrackViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.viewholders 2 | 3 | import android.support.v7.widget.RecyclerView 4 | import android.view.View 5 | import android.widget.ImageView 6 | import android.widget.TextView 7 | 8 | import com.revosleap.proxima.R 9 | 10 | class TrackViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 11 | private var trackImage: ImageView = itemView.findViewById(R.id.imageView2) 12 | var title: TextView 13 | var artist: TextView 14 | 15 | init { 16 | title = itemView.findViewById(R.id.textViewTitleTrack) 17 | artist = itemView.findViewById(R.id.textViewArtistTrack) 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/favorite_border.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/models/Artist.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.models 2 | 3 | import java.io.Serializable 4 | 5 | class Artist:Serializable { 6 | val albums: MutableList = mutableListOf() 7 | 8 | val id: Int 9 | get() = firstAlbum.artistId 10 | 11 | val name: String? 12 | get() = firstAlbum.artistName 13 | 14 | private val firstAlbum: Album 15 | get() = if (albums.isEmpty()) Album() else albums[0] 16 | 17 | val songCount: Int 18 | get() { 19 | var songCount = 0 20 | for (album in albums) { 21 | songCount += album.songCount 22 | } 23 | return songCount 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/repeat.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentPlaylist.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | 10 | import com.revosleap.proxima.R 11 | 12 | 13 | /** 14 | * A simple [Fragment] subclass. 15 | */ 16 | class FragmentPlaylist : Fragment() { 17 | 18 | 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle?): View? { 21 | // Inflate the constControls for this fragment 22 | return inflater.inflate(R.layout.fragment_playlist, container, false) 23 | } 24 | 25 | }// Required empty public constructor 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/revosleap/proxima/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * @see [Testing documentation](http://d.android.com/tools/testing) 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getTargetContext() 20 | 21 | assertEquals("com.revosleap.proxima", appContext.packageName) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentFavorites.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | 10 | import com.revosleap.proxima.R 11 | 12 | 13 | /** 14 | * A simple [Fragment] subclass. 15 | */ 16 | class FragmentFavorites : Fragment() { 17 | 18 | 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle?): View? { 21 | // Inflate the constControls for this fragment 22 | return inflater.inflate(R.layout.fragment_favorites, container, false) 23 | } 24 | 25 | }// Required empty public constructor 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/playback/PlaybackInfoListener.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.playback 2 | 3 | import android.support.annotation.IntDef 4 | 5 | 6 | abstract class PlaybackInfoListener { 7 | 8 | open fun onPositionChanged(position: Int) {} 9 | 10 | open fun onStateChanged(@State state: Int) {} 11 | 12 | open fun onPlaybackCompleted() {} 13 | 14 | @IntDef(State.INVALID, State.PLAYING, State.PAUSED, State.COMPLETED, State.RESUMED) 15 | @Retention(AnnotationRetention.SOURCE) 16 | annotation class State { 17 | companion object { 18 | 19 | const val INVALID = -1 20 | const val PLAYING = 0 21 | const val PAUSED = 1 22 | const val COMPLETED = 2 23 | const val RESUMED = 3 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | android.debug.obsoleteApi=true 11 | android.enableR8 = true 12 | # When configured, Gradle will run in incubating parallel mode. 13 | # This option should only be used with decoupled projects. More details, visit 14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 15 | # org.gradle.parallel=true 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/pause.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | #EC407A 7 | 8 | #000000 9 | #FFFFFF 10 | 11 | #FAFAFA 12 | #060606 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/play_icon.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep public class * implements com.bumptech.glide.module.GlideModule 2 | 3 | 4 | # Add project specific ProGuard rules here. 5 | # You can control the set of applied configuration files using the 6 | # proguardFiles setting in build.gradle. 7 | # 8 | # For more details, see 9 | # http://developer.android.com/guide/developing/tools/proguard.html 10 | 11 | # If your project uses WebView with JS, uncomment the following 12 | # and specify the fully qualified class name to the JavaScript interface 13 | # class: 14 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 15 | # public *; 16 | #} 17 | 18 | # Uncomment this to preserve the line number information for 19 | # debugging stack traces. 20 | #-keepattributes SourceFile,LineNumberTable 21 | 22 | # If you keep the line number information, uncomment this to 23 | # hide the original source file name. 24 | #-renamesourcefileattribute SourceFile 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BXPlayer 2 | Music player Slowly coming to life. 3 | ## Screenshots 4 | 5 | 6 | 7 | License 8 | ------- 9 | Copyright (C) 2019 Carlos Anyona 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Affero General Public License as published 13 | by the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Affero General Public License for more details. 20 | 21 | You should have received a copy of the GNU Affero General Public License 22 | along with this program. If not, see . 23 | 24 | Check full license [here](/LICENSE) 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/UniversalUtils.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | object UniversalUtils { 4 | fun formatTime(ms: Long): String { 5 | val totalSecs = ms / 1000 6 | val hours = totalSecs / 3600 7 | val minutes = totalSecs / 60 % 60 8 | val secs = totalSecs % 60 9 | val minutesString = when { 10 | minutes == 0L -> "00" 11 | minutes < 10 -> "0$minutes" 12 | else -> "" + minutes 13 | } 14 | val secsString = when { 15 | secs == 0L -> "00" 16 | secs < 10 -> "0$secs" 17 | else -> "" + secs 18 | } 19 | return when { 20 | hours > 0 -> "$hours:$minutesString:$secsString" 21 | minutes > 0 -> "$minutes:$secsString" 22 | else -> "0:$secsString" 23 | } 24 | } 25 | 26 | fun formatTrack(trackNumber: Int): Int { 27 | var formatted = trackNumber 28 | if (trackNumber >= 1000) { 29 | formatted = trackNumber % 1000 30 | } 31 | return formatted 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_artist_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tabs_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/AlbumProvider.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import com.revosleap.proxima.models.Album 4 | import com.revosleap.proxima.models.Song 5 | import java.util.* 6 | 7 | object AlbumProvider { 8 | fun retrieveAlbums(songs: MutableList?): MutableList { 9 | val albums = mutableListOf() 10 | if (songs != null) { 11 | for (song in songs) { 12 | getAlbum(albums, song.albumName!!).songs.add(song) 13 | } 14 | } 15 | if (albums.size > 1) { 16 | sortAlbums(albums) 17 | } 18 | return albums 19 | } 20 | 21 | private fun sortAlbums(albums: MutableList) { 22 | albums.sortWith(Comparator { obj1, obj2 -> Integer.compare(obj1.year, obj2.year) }) 23 | } 24 | 25 | private fun getAlbum(albums: MutableList, albumName: String): Album { 26 | for (album in albums) { 27 | if (!album.songs.isEmpty() && album.songs[0].albumName == albumName) { 28 | return album 29 | } 30 | } 31 | val album = Album() 32 | albums.add(album) 33 | return album 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/playlist.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_artists.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 21 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/adapters/ArtistTabAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.adapters 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.support.v4.app.FragmentManager 6 | import android.support.v4.app.FragmentStatePagerAdapter 7 | import com.revosleap.proxima.ui.fragments.FragmentArtistAlbum 8 | import com.revosleap.proxima.ui.fragments.FragmentArtistTrack 9 | import com.revosleap.proxima.utils.utils.Universal 10 | 11 | class ArtistTabAdapter(fm: FragmentManager,songString: String?,albumString: String?) 12 | : FragmentStatePagerAdapter(fm) { 13 | val album= albumString 14 | val song= songString 15 | val bundle = Bundle() 16 | private val fragmentArtistAlbum = FragmentArtistAlbum() 17 | private val fragmentArtistTrack = FragmentArtistTrack() 18 | 19 | override fun getItem(position: Int): Fragment? { 20 | bundle.putString(Universal.SONGS_BUNDLE,song) 21 | bundle.putString(Universal.ALBUMS_BUNDLE,album) 22 | return when (position) { 23 | 0 -> { 24 | fragmentArtistTrack.arguments = bundle 25 | fragmentArtistTrack 26 | } 27 | 1 -> { 28 | fragmentArtistAlbum.arguments= bundle 29 | fragmentArtistAlbum 30 | } 31 | else -> null 32 | } 33 | 34 | } 35 | 36 | override fun getCount(): Int { 37 | return 2 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/adapters/MainTabsAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.adapters 2 | 3 | import android.support.v4.app.Fragment 4 | import android.support.v4.app.FragmentManager 5 | import android.support.v4.app.FragmentStatePagerAdapter 6 | import com.revosleap.proxima.callbacks.BXColor 7 | import com.revosleap.proxima.ui.activities.PlayerActivity 8 | import com.revosleap.proxima.ui.fragments.* 9 | 10 | class MainTabsAdapter(fm: FragmentManager, size: Int, playerActivity: PlayerActivity) : FragmentStatePagerAdapter(fm), BXColor { 11 | private val faveFrag = FragmentFavorites() 12 | private val playlist = FragmentPlaylist() 13 | private val tracks = FragmentTracks() 14 | private val albums = FragmentAlbum() 15 | private val artists = FragmentArtists() 16 | private val player=playerActivity 17 | override fun songColor(color: Int) { 18 | tracks.songColor(color) 19 | albums.songColor(color) 20 | artists.songColor(color) 21 | } 22 | 23 | private val fragmentCount = size 24 | override fun getItem(position: Int): Fragment? { 25 | player.setColorCallback(this) 26 | return when (position) { 27 | 0 -> faveFrag 28 | 1 -> playlist 29 | 2 -> tracks 30 | 3 -> albums 31 | 4 -> artists 32 | else -> null 33 | } 34 | } 35 | 36 | override fun getCount(): Int { 37 | return fragmentCount 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/callbacks/PlayerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.callbacks 2 | 3 | 4 | import android.app.Activity 5 | import android.media.MediaPlayer 6 | import com.revosleap.proxima.models.Album 7 | import com.revosleap.proxima.models.Song 8 | import com.revosleap.proxima.utils.playback.PlaybackInfoListener 9 | 10 | 11 | interface PlayerAdapter { 12 | 13 | fun isMediaPlayer(): Boolean 14 | 15 | fun isPlaying(): Boolean 16 | 17 | fun isReset(): Boolean 18 | 19 | fun getCurrentSong(): Song? 20 | 21 | fun getCurrentSongs():MutableList? 22 | 23 | fun getPlayerPosition(): Int 24 | 25 | fun getSelectedAlbum(): Album? 26 | 27 | fun initMediaPlayer() 28 | 29 | fun shufflePlayList() 30 | 31 | fun release() 32 | 33 | fun resumeOrPause() 34 | 35 | fun reset() 36 | 37 | fun instantReset() 38 | 39 | fun skip(isNext: Boolean) 40 | 41 | fun openEqualizer(activity: Activity) 42 | 43 | fun seekTo(position: Int) 44 | 45 | fun setPlaybackInfoListener(playbackInfoListener: PlaybackInfoListener) 46 | 47 | fun registerNotificationActionsReceiver(isRegister: Boolean) 48 | 49 | fun setCurrentSong(song: Song, songs: MutableList) 50 | 51 | fun onPauseActivity() 52 | 53 | fun onResumeActivity() 54 | 55 | fun setSelectedAlbum(album: Album) 56 | 57 | fun getMediaPlayer(): MediaPlayer? 58 | 59 | @PlaybackInfoListener.State 60 | fun getState(): Int 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/AudioUtils.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.BitmapFactory 6 | import android.media.MediaMetadataRetriever 7 | import com.revosleap.proxima.R 8 | import java.io.ByteArrayInputStream 9 | import java.io.InputStream 10 | import java.util.* 11 | import java.util.concurrent.TimeUnit 12 | 13 | object AudioUtils { 14 | @Throws(IllegalStateException::class) 15 | fun cover(path: String, context: Context): Bitmap { 16 | val retriever = MediaMetadataRetriever() 17 | val inputStream: InputStream? 18 | retriever.setDataSource(path) 19 | return if (retriever.embeddedPicture != null) { 20 | inputStream = ByteArrayInputStream(retriever.embeddedPicture) 21 | val image = BitmapFactory.decodeStream(inputStream) 22 | retriever.release() 23 | image 24 | } else { 25 | BitmapFactory.decodeResource(context.resources, 26 | R.drawable.cover2) 27 | } 28 | } 29 | 30 | fun formatDuration(duration: Int): String { 31 | return String.format(Locale.getDefault(), "%02d:%02d", 32 | TimeUnit.MILLISECONDS.toMinutes(duration.toLong()), 33 | TimeUnit.MILLISECONDS.toSeconds(duration.toLong()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration.toLong()))) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/WrappedAsyncTaskLoader.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.support.v4.content.AsyncTaskLoader 5 | 6 | internal abstract class WrappedAsyncTaskLoader 7 | /** 8 | * Constructor of `WrappedAsyncTaskLoader` 9 | * 10 | * @param context The [Context] to use. 11 | */ 12 | (context: Context) : AsyncTaskLoader(context) { 13 | 14 | private var mData: D? = null 15 | 16 | /** 17 | * {@inheritDoc} 18 | */ 19 | override fun deliverResult(data: D?) { 20 | if (!isReset) { 21 | this.mData = data 22 | super.deliverResult(data) 23 | } 24 | } 25 | 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | override fun onStartLoading() { 30 | super.onStartLoading() 31 | 32 | if (this.mData != null) { 33 | deliverResult(this.mData) 34 | } else if (takeContentChanged() || this.mData == null) { 35 | forceLoad() 36 | } 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | override fun onStopLoading() { 43 | super.onStopLoading() 44 | // Attempt to cancel the current load task if possible 45 | cancelLoad() 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | override fun onReset() { 52 | super.onReset() 53 | // Ensure the loader is stopped 54 | onStopLoading() 55 | this.mData = null 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/PlayListProvider.kt: -------------------------------------------------------------------------------- 1 | //package com.revosleap.proxima.utils.utils 2 | // 3 | //import android.content.Context 4 | //import android.database.Cursor 5 | //import android.provider.MediaStore 6 | //import com.revosleap.proxima.models.Album 7 | //import com.revosleap.proxima.models.PlayList 8 | // 9 | // 10 | //object PlayListProvider { 11 | // private const val AUDIO_ID= 0 12 | // private const val CONTENT_DIRECTORY= 1 13 | // private const val PLAYLIST_ID= 2 14 | // private const val _ID = 3 15 | // 16 | // 17 | // private val BASE_PROJECTION = arrayOf( 18 | // MediaStore.Audio.PlaylistsColumns.NAME, 19 | // MediaStore.Audio.PlaylistsColumns.DATA, 20 | // MediaStore.Audio.PlaylistsColumns.DATE_ADDED 21 | // ) 22 | // fun getAllPlayLists(cursor: Cursor?):MutableList{ 23 | // val playLists= mutableListOf() 24 | // if (cursor!=null && cursor.moveToFirst()){ 25 | // 26 | // } 27 | // } 28 | // private fun getPlayListFromCursor(cursor: Cursor):Album{ 29 | // val album= Album() 30 | // album.title = 31 | // } 32 | // private fun makePlaylistCursor(context: Context, sortOrder: String?): Cursor? { 33 | // try { 34 | // return context.contentResolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, 35 | // BASE_PROJECTION, null, null, sortOrder) 36 | // } catch (e: SecurityException) { 37 | // return null 38 | // } 39 | // 40 | // } 41 | //} -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_fragment_album.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 24 | 25 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/current_songs_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 21 | 22 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_shuffle_bordered.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/services/MusicPlayerService.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.services 2 | 3 | import android.app.Service 4 | import android.content.Intent 5 | import android.os.Binder 6 | import android.os.IBinder 7 | import com.revosleap.proxima.utils.playback.BXNotificationManager 8 | import com.revosleap.proxima.utils.playback.MediaPlayerHolder 9 | 10 | class MusicPlayerService : Service() { 11 | private val binder= MusicBinder() 12 | var mediaPlayerHolder: MediaPlayerHolder? = null 13 | private set 14 | var musicNotificationManager: BXNotificationManager? = null 15 | private set 16 | var isRestoredFromPause = false 17 | 18 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 19 | mediaPlayerHolder?.registerNoisyReceiver() 20 | return START_NOT_STICKY 21 | } 22 | 23 | override fun onBind(intent: Intent): IBinder { 24 | if (mediaPlayerHolder == null) { 25 | mediaPlayerHolder = MediaPlayerHolder(this) 26 | musicNotificationManager = BXNotificationManager(this) 27 | mediaPlayerHolder!!.registerNotificationActionsReceiver(true) 28 | } 29 | return binder 30 | } 31 | 32 | override fun onDestroy() { 33 | mediaPlayerHolder!!.registerNotificationActionsReceiver(false) 34 | musicNotificationManager = null 35 | mediaPlayerHolder!!.release() 36 | super.onDestroy() 37 | } 38 | inner class MusicBinder:Binder(){ 39 | val serviceInstance: MusicPlayerService 40 | get() = this@MusicPlayerService 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/PreferenceHelper.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.preference.PreferenceManager 5 | 6 | class PreferenceHelper(context: Context) { 7 | companion object { 8 | private const val SORTING_TOKEN = "player.trackSorting" 9 | private const val ALBUM_SORTING = "player.albumSorting" 10 | private const val ARTIST_SORTING = "player.artistSorting" 11 | private const val CURRENT_PLAYING = "player.currentPlaying" 12 | private const val CURRENT_PLAYING_INDEX = "player.currentPlayingIndex" 13 | private const val CURRENT_PLAYING_POSITION="current.song.position" 14 | } 15 | 16 | private val preferences = PreferenceManager.getDefaultSharedPreferences(context) 17 | 18 | var sortingStyle = preferences.getString(SORTING_TOKEN, "")!! 19 | set(value) = preferences.edit().putString(SORTING_TOKEN, value).apply() 20 | 21 | var albumSorting = preferences.getString(ALBUM_SORTING, "")!! 22 | set(value) = preferences.edit().putString(ALBUM_SORTING, value).apply() 23 | 24 | var artistSorting = preferences.getString(ARTIST_SORTING, "")!! 25 | set(value) = preferences.edit().putString(ARTIST_SORTING, value).apply() 26 | 27 | var playingList = preferences.getString(CURRENT_PLAYING, "")!! 28 | set(value) = preferences.edit().putString(CURRENT_PLAYING, value).apply() 29 | 30 | var currentIndex = preferences.getInt(CURRENT_PLAYING_INDEX, 0) 31 | set(value) = preferences.edit().putInt(CURRENT_PLAYING_INDEX, value).apply() 32 | 33 | var currentPosition = preferences.getInt(CURRENT_PLAYING_INDEX, 0) 34 | set(value) = preferences.edit().putInt(CURRENT_PLAYING_POSITION, value).apply() 35 | 36 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "com.revosleap.proxima" 9 | minSdkVersion 21 10 | targetSdkVersion 28 11 | versionCode 2 12 | versionName "0.0.2-alpha" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'com.android.support:appcompat-v7:28.0.0' 26 | implementation 'com.android.support:support-v4:28.0.0' 27 | testImplementation 'junit:junit:4.12' 28 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | implementation 'com.android.support:design:28.0.0' 32 | implementation 'com.android.support:palette-v7:28.0.0' 33 | implementation 'com.revosleap.layout:blurrylayout:1.0.2' 34 | implementation "org.jetbrains.anko:anko-commons:$anko_version" 35 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 36 | //noinspection GradleDependency 37 | implementation 'com.github.bumptech.glide:glide:3.8.0' 38 | implementation "com.revosleap.adapter:SimpleAdapter:$simple_adapter_version" 39 | implementation 'com.google.code.gson:gson:2.8.5' 40 | 41 | } 42 | repositories { 43 | mavenCentral() 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_overflow_menu.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/artist_track_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 32 | 33 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/GetAudio.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.provider.MediaStore 5 | import com.revosleap.proxima.models.Song 6 | import org.jetbrains.anko.toast 7 | import java.util.* 8 | 9 | class GetAudio { 10 | fun geAllAudio(context: Context): MutableList { 11 | 12 | val temAudioList = ArrayList() 13 | val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 14 | val order = MediaStore.Audio.Media.TITLE 15 | 16 | val projection = arrayOf(MediaStore.Audio.AudioColumns.DATA, MediaStore.Audio.AudioColumns.ALBUM, MediaStore.Audio.ArtistColumns.ARTIST, MediaStore.Audio.AudioColumns.TITLE, MediaStore.Audio.AudioColumns.DURATION) 17 | val cursor = context.contentResolver.query(uri, projection, null, null, order) 18 | if (cursor != null) { 19 | while (cursor.moveToNext()) { 20 | 21 | val model = Song() 22 | val path = cursor.getString(0) 23 | val album = cursor.getString(1) 24 | val artist = cursor.getString(2) 25 | val name = cursor.getString(3) 26 | val duration = cursor.getString(4) 27 | 28 | try { 29 | model.title = name 30 | model.albumName = album 31 | model.artist = artist 32 | model.path = path 33 | model.duration = duration.toLong() 34 | } catch (e: Exception) { 35 | e.printStackTrace() 36 | } 37 | 38 | if(duration.toLong() >= 30000){ 39 | temAudioList.add(model) 40 | } 41 | 42 | } 43 | cursor.close() 44 | } else 45 | context.toast("No Audio Files Found") 46 | return temAudioList 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shuffle.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 23 | 24 | 35 | 39 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sound_bars.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/artist_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 23 | 24 | 25 | 41 | 42 | 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/EqualizerUtils.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.app.Activity 4 | import android.content.ActivityNotFoundException 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.media.MediaPlayer 8 | import android.media.audiofx.AudioEffect 9 | import android.widget.Toast 10 | import com.revosleap.proxima.R 11 | 12 | object EqualizerUtils { 13 | fun hasEqualizer(context: Context): Boolean { 14 | val effects = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL) 15 | val pm = context.packageManager 16 | val ri = pm.resolveActivity(effects, 0) 17 | return ri != null 18 | } 19 | 20 | internal fun openAudioEffectSession(context: Context, sessionId: Int) { 21 | val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) 22 | intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId) 23 | intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName) 24 | intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC) 25 | context.sendBroadcast(intent) 26 | } 27 | 28 | internal fun closeAudioEffectSession(context: Context, sessionId: Int) { 29 | val audioEffectsIntent = Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) 30 | audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId) 31 | audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName) 32 | context.sendBroadcast(audioEffectsIntent) 33 | } 34 | 35 | internal fun openEqualizer(activity: Activity, mediaPlayer: MediaPlayer) { 36 | val sessionId = mediaPlayer.audioSessionId 37 | 38 | if (sessionId == AudioEffect.ERROR_BAD_VALUE) { 39 | notifyNoSessionId(activity) 40 | } else { 41 | try { 42 | val effects = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL) 43 | effects.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId) 44 | effects.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC) 45 | activity.startActivityForResult(effects, 0) 46 | } catch (notFound: ActivityNotFoundException) { 47 | notFound.printStackTrace() 48 | } 49 | 50 | } 51 | } 52 | 53 | fun notifyNoSessionId(context: Context) { 54 | Toast.makeText(context, context.getString(R.string.bad_id), Toast.LENGTH_SHORT).show() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/album_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 26 | 27 | 28 | 43 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | 53 | 54 | 57 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/GlideLoader.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | 4 | import android.content.Context 5 | import android.util.Log 6 | 7 | import com.bumptech.glide.Glide 8 | import com.bumptech.glide.GlideBuilder 9 | import com.bumptech.glide.Priority 10 | import com.bumptech.glide.load.data.DataFetcher 11 | import com.bumptech.glide.load.model.GenericLoaderFactory 12 | import com.bumptech.glide.load.model.ModelLoader 13 | import com.bumptech.glide.load.model.ModelLoaderFactory 14 | import com.bumptech.glide.load.model.stream.StreamModelLoader 15 | import com.bumptech.glide.module.GlideModule 16 | 17 | import java.io.IOException 18 | import java.io.InputStream 19 | 20 | class GlideLoader : GlideModule { 21 | 22 | 23 | override fun applyOptions(context: Context, builder: GlideBuilder) { 24 | 25 | } 26 | 27 | override fun registerComponents(context: Context, glide: Glide) { 28 | glide.register(InputStream::class.java, InputStream::class.java, PassthroughStreamLoader.Factory()) 29 | } 30 | 31 | class PassthroughStreamLoader : StreamModelLoader { 32 | override fun getResourceFetcher(model: InputStream, width: Int, height: Int): DataFetcher { 33 | return object : DataFetcher { 34 | @Throws(Exception::class) 35 | override fun loadData(priority: Priority): InputStream { 36 | return model 37 | } 38 | 39 | override fun cleanup() { 40 | try { 41 | model.close() 42 | } catch (e: IOException) { 43 | Log.w("PassthroughDataFetcher", "Cannot clean up after stream", e) 44 | } 45 | 46 | } 47 | 48 | override fun getId(): String { 49 | return System.currentTimeMillis().toString() // There's no way to have a meaningful value here, 50 | // which means that caching of straight-loaded InputStreams is not possible. 51 | } 52 | 53 | override fun cancel() { 54 | // do nothing 55 | } 56 | } 57 | } 58 | 59 | class Factory : ModelLoaderFactory { 60 | override fun build(context: Context, factories: GenericLoaderFactory): ModelLoader { 61 | return PassthroughStreamLoader() 62 | } 63 | 64 | override fun teardown() { 65 | // nothing to do 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/ArtistProvider.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.provider.MediaStore 5 | import com.revosleap.proxima.models.Album 6 | import com.revosleap.proxima.models.Artist 7 | import java.util.* 8 | 9 | object ArtistProvider { 10 | val ARTISTS_LOADER = 0 11 | 12 | private val songLoaderSortOrder: String 13 | get() = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER + ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER + ", " + MediaStore.Audio.Media.DEFAULT_SORT_ORDER 14 | 15 | @Throws(Exception::class) 16 | private fun sortArtists(artists: MutableList) { 17 | artists.sortWith(Comparator { obj1, obj2 -> obj1.name?.compareTo(obj2.name!!, ignoreCase = true)!! }) 18 | } 19 | 20 | @Throws(Exception::class) 21 | fun getAllArtists(context: Context): MutableList { 22 | val songs = SongProvider.getAllDeviceSongs(context) 23 | // sortArtists(artists); 24 | return retrieveArtists(AlbumProvider.retrieveAlbums(songs)) 25 | } 26 | 27 | fun getArtist(artists: MutableList, selectedArtist: String): Artist? { 28 | var returnerArtist: Artist? = null 29 | for (artist in artists) { 30 | if (artist.name == selectedArtist) { 31 | returnerArtist = artist 32 | } 33 | } 34 | return returnerArtist 35 | } 36 | 37 | private fun retrieveArtists(albums: MutableList?): MutableList { 38 | val artists = mutableListOf() 39 | if (albums != null) { 40 | for (album in albums) { 41 | getOrCreateArtist(artists, album.artistId).albums.add(album) 42 | } 43 | } 44 | return artists 45 | } 46 | 47 | private fun getOrCreateArtist(artists: MutableList, artistId: Int): Artist { 48 | for (artist in artists) { 49 | if (!artist.albums.isEmpty() && !artist.albums[0].songs.isEmpty() 50 | && artist.albums[0].songs[0].artistId == artistId) { 51 | return artist 52 | } 53 | } 54 | val artist = Artist() 55 | artists.add(artist) 56 | return artist 57 | } 58 | 59 | internal class AsyncArtistLoader(context: Context) : WrappedAsyncTaskLoader>(context) { 60 | 61 | override fun loadInBackground(): MutableList? { 62 | try { 63 | return getAllArtists(context) 64 | } catch (e: Exception) { 65 | e.printStackTrace() 66 | } 67 | 68 | return null 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 20 | 21 | 22 | 33 | 34 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_tracks.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 26 | 27 | 39 | 40 | 53 | 54 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/utils/SongProvider.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.utils 2 | 3 | import android.content.Context 4 | import android.database.Cursor 5 | import android.provider.MediaStore 6 | import com.revosleap.proxima.models.Album 7 | import com.revosleap.proxima.models.Song 8 | 9 | object SongProvider { 10 | private val TITLE = 0 11 | private val TRACK = 1 12 | private val YEAR = 2 13 | private val DURATION = 3 14 | private val PATH = 4 15 | private val ALBUM = 5 16 | private val ARTIST_ID = 6 17 | private val ARTIST = 7 18 | 19 | private val BASE_PROJECTION = arrayOf(MediaStore.Audio.AudioColumns.TITLE, // 0 20 | MediaStore.Audio.AudioColumns.TRACK, // 1 21 | MediaStore.Audio.AudioColumns.YEAR, // 2 22 | MediaStore.Audio.AudioColumns.DURATION, // 3 23 | MediaStore.Audio.AudioColumns.DATA, // 4 24 | MediaStore.Audio.AudioColumns.ALBUM, // 5 25 | MediaStore.Audio.AudioColumns.ARTIST_ID, // 6 26 | MediaStore.Audio.AudioColumns.ARTIST)// 7 27 | 28 | private val mAllDeviceSongs = mutableListOf() 29 | 30 | fun getAllDeviceSongs(context: Context): MutableList { 31 | return getSongs(makeSongCursor(context, null)) 32 | } 33 | 34 | fun getAllArtistSongs(albums: MutableList): MutableList { 35 | val songsList = mutableListOf() 36 | for (album in albums) { 37 | songsList.addAll(album.songs) 38 | } 39 | return songsList 40 | } 41 | 42 | private fun getSongs(cursor: Cursor?): MutableList { 43 | val songs = mutableListOf() 44 | if (cursor != null && cursor.moveToFirst()) { 45 | do { 46 | val song = getSongFromCursorImpl(cursor) 47 | if (song.duration >= 30000) { 48 | songs.add(song) 49 | mAllDeviceSongs.add(song) 50 | } 51 | } while (cursor.moveToNext()) 52 | } 53 | 54 | cursor?.close() 55 | if (songs.size > 1) { 56 | songs.sortWith(compareBy { it.title }) 57 | } 58 | return songs 59 | } 60 | 61 | 62 | private fun getSongFromCursorImpl(cursor: Cursor): Song { 63 | val song = Song() 64 | song.title = cursor.getString(TITLE) 65 | song.trackNumber = UniversalUtils.formatTrack(cursor.getInt(TRACK)) 66 | song.songYear = cursor.getInt(YEAR) 67 | song.duration = cursor.getLong(DURATION) 68 | song.path = cursor.getString(PATH) 69 | song.albumName = cursor.getString(ALBUM) 70 | song.artistId = cursor.getInt(ARTIST_ID) 71 | song.artist = cursor.getString(ARTIST) 72 | 73 | return song 74 | } 75 | 76 | internal fun makeSongCursor(context: Context, sortOrder: String?): Cursor? { 77 | try { 78 | return context.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 79 | BASE_PROJECTION, null, null, sortOrder) 80 | } catch (e: SecurityException) { 81 | return null 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentArtistInfo.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.support.design.widget.TabLayout 6 | import android.support.v4.app.Fragment 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import com.google.gson.Gson 11 | import com.google.gson.reflect.TypeToken 12 | import com.revosleap.proxima.R 13 | import com.revosleap.proxima.models.Album 14 | import com.revosleap.proxima.models.Song 15 | import com.revosleap.proxima.ui.activities.PlayerActivity 16 | import com.revosleap.proxima.utils.adapters.ArtistTabAdapter 17 | import com.revosleap.proxima.utils.utils.Universal 18 | import kotlinx.android.synthetic.main.fragment_artist_info.* 19 | import java.lang.reflect.Type 20 | 21 | class FragmentArtistInfo : Fragment() { 22 | private var playerActivity: PlayerActivity? = null 23 | private var artistTabAdapter: ArtistTabAdapter? = null 24 | private var songs = mutableListOf() 25 | private var albums = mutableListOf() 26 | private var songString: String? = null 27 | private var albumString: String? = null 28 | 29 | override fun onAttach(context: Context?) { 30 | super.onAttach(context) 31 | playerActivity = activity as PlayerActivity 32 | } 33 | 34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) 35 | : View? { 36 | setHasOptionsMenu(true) 37 | getItems() 38 | return inflater.inflate(R.layout.fragment_artist_info, container, false) 39 | } 40 | 41 | 42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 43 | super.onViewCreated(view, savedInstanceState) 44 | setTabs() 45 | val params = linearLayout.layoutParams as ViewGroup.MarginLayoutParams 46 | params.bottomMargin = playerActivity?.controls()!!.height 47 | linearLayout.layoutParams = params 48 | } 49 | 50 | 51 | private fun setTabs() { 52 | artistTabAdapter = ArtistTabAdapter(playerActivity?.supportFragmentManager!!, songString, albumString) 53 | tabLayoutArtist.apply { 54 | addTab(newTab().setText("Tracks (${songs.size})")) 55 | addTab(newTab().setText("Albums (${albums.size})")) 56 | tabGravity = TabLayout.GRAVITY_FILL 57 | } 58 | viewPagerArtist.apply { 59 | adapter = artistTabAdapter 60 | addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayoutArtist)) 61 | } 62 | tabLayoutArtist.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(viewPagerArtist)) 63 | } 64 | 65 | private fun getItems() { 66 | songString = arguments?.getString(Universal.SONGS_BUNDLE) 67 | albumString = arguments?.getString(Universal.ALBUMS_BUNDLE) 68 | val gson = Gson() 69 | val type: Type = object : TypeToken>() {}.type 70 | val albumType: Type = object : TypeToken>() {}.type 71 | val songs = gson.fromJson>(songString, type) 72 | val artAlbums = gson.fromJson>(albumString, albumType) 73 | if (songs != null && songs.size > 0 && artAlbums != null && artAlbums.size > 0) { 74 | this.songs = songs 75 | albums = artAlbums 76 | } 77 | 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/res/values/font_certs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @array/com_google_android_gms_fonts_certs_dev 5 | @array/com_google_android_gms_fonts_certs_prod 6 | 7 | 8 | 9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= 10 | 11 | 12 | 13 | 14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/track.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 27 | 28 | 29 | 30 | 51 | 52 | 71 | 72 | 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentArtistAlbum.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.support.v7.widget.LinearLayoutManager 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import com.google.gson.Gson 11 | import com.google.gson.reflect.TypeToken 12 | import com.revosleap.proxima.R 13 | import com.revosleap.proxima.models.Album 14 | import com.revosleap.proxima.ui.activities.PlayerActivity 15 | import com.revosleap.proxima.utils.utils.Universal 16 | import com.revosleap.simpleadapter.SimpleAdapter 17 | import com.revosleap.simpleadapter.SimpleCallbacks 18 | import kotlinx.android.synthetic.main.fragment_fragment_album.* 19 | import kotlinx.android.synthetic.main.track.view.* 20 | import java.lang.reflect.Type 21 | 22 | class FragmentArtistAlbum : Fragment(), SimpleCallbacks { 23 | private var simpleAdapter: SimpleAdapter? = null 24 | private var albums = mutableListOf() 25 | private var playerActivity: PlayerActivity? = null 26 | 27 | override fun onAttach(context: Context?) { 28 | super.onAttach(context) 29 | playerActivity = activity as PlayerActivity 30 | 31 | } 32 | 33 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) 34 | : View? { 35 | getAlbums() 36 | simpleAdapter = SimpleAdapter(R.layout.track, this) 37 | return inflater.inflate(R.layout.fragment_fragment_album, container, false) 38 | } 39 | 40 | override fun bindView(view: View, item: Any, position: Int) { 41 | item as Album 42 | val title = view.textViewArtistTrack 43 | val subTitle = view.textViewTitleTrack 44 | title.text = item.title 45 | subTitle.text = item.year.toString() 46 | 47 | } 48 | 49 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 50 | super.onViewCreated(view, savedInstanceState) 51 | recyclerViewAlbum.apply { 52 | adapter = simpleAdapter 53 | layoutManager = LinearLayoutManager(playerActivity) 54 | hasFixedSize() 55 | } 56 | simpleAdapter?.addManyItems(albums.toMutableList()) 57 | buttonListSortAlbums.visibility= View.GONE 58 | } 59 | 60 | private fun getAlbums() { 61 | val albumString = arguments?.getString(Universal.ALBUMS_BUNDLE) 62 | val gson = Gson() 63 | val albumType: Type = object : TypeToken>() {}.type 64 | val artAlbums = gson.fromJson>(albumString, albumType) 65 | if (artAlbums != null && artAlbums.size > 0) { 66 | albums = artAlbums 67 | } 68 | } 69 | 70 | private fun goToInfo(position: Int) { 71 | val gson = Gson() 72 | val gsonString = gson.toJson(albums[position].songs) 73 | val albumInfo = FragmentAlbumInfo() 74 | val bundle = Bundle() 75 | bundle.putString(Universal.ALBUM_BUNDLE, gsonString) 76 | albumInfo.arguments = bundle 77 | playerActivity?.supportFragmentManager!! 78 | .beginTransaction() 79 | .replace(R.id.frame_current, albumInfo, Universal.ALBUM_INFO_TAG) 80 | .addToBackStack(null) 81 | .commit() 82 | playerActivity?.replaceFragment() 83 | } 84 | 85 | override fun onViewClicked(view: View, item: Any, position: Int) { 86 | goToInfo(position) 87 | } 88 | 89 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 90 | 91 | } 92 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/controls.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | 24 | 25 | 26 | 44 | 45 | 61 | 62 | 79 | 80 | 98 | 99 | 115 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_album_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | 13 | 16 | 17 | 25 | 34 | 39 | 48 | 56 | 57 | 62 | 63 | 64 | 67 | 79 | 92 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_if_speaker.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentArtistTrack.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.support.v7.widget.LinearLayoutManager 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import com.google.gson.Gson 11 | import com.google.gson.reflect.TypeToken 12 | import com.revosleap.proxima.R 13 | import com.revosleap.proxima.callbacks.PlayerAdapter 14 | import com.revosleap.proxima.models.Song 15 | import com.revosleap.proxima.services.MusicPlayerService 16 | import com.revosleap.proxima.ui.activities.PlayerActivity 17 | import com.revosleap.proxima.utils.playback.BXNotificationManager 18 | import com.revosleap.proxima.utils.playback.PlaybackInfoListener 19 | import com.revosleap.proxima.utils.utils.Universal 20 | import com.revosleap.proxima.utils.utils.UniversalUtils 21 | import com.revosleap.simpleadapter.SimpleAdapter 22 | import com.revosleap.simpleadapter.SimpleCallbacks 23 | import kotlinx.android.synthetic.main.artist_track_item.view.* 24 | import kotlinx.android.synthetic.main.fragment_tracks.* 25 | import java.lang.reflect.Type 26 | 27 | class FragmentArtistTrack : Fragment(), SimpleCallbacks { 28 | private var playerActivity: PlayerActivity? = null 29 | private var simpleAdapter: SimpleAdapter? = null 30 | private var songs = mutableListOf() 31 | private var mPlayerAdapter: PlayerAdapter? = null 32 | private var mMusicService: MusicPlayerService? = null 33 | private var mMusicNotificationManager: BXNotificationManager? = null 34 | private var mPlaybackListener: PlaybackListener? = null 35 | 36 | override fun onAttach(context: Context?) { 37 | super.onAttach(context) 38 | playerActivity = activity as PlayerActivity 39 | getService() 40 | } 41 | 42 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) 43 | : View? { 44 | simpleAdapter = SimpleAdapter(R.layout.artist_track_item, this) 45 | getMusicList() 46 | return inflater.inflate(R.layout.fragment_tracks, container, false) 47 | } 48 | 49 | override fun bindView(view: View, item: Any, position: Int) { 50 | item as Song 51 | val trackNo = view.textViewTrackNumber 52 | val trackTitle = view.textViewSongTitle 53 | val trackDuration = view.textViewSongDuration 54 | trackNo.text = item.trackNumber.toString() 55 | trackTitle.text = item.title 56 | trackDuration.text = UniversalUtils.formatTime(item.duration) 57 | 58 | } 59 | 60 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 61 | super.onViewCreated(view, savedInstanceState) 62 | trackRecycler.apply { 63 | adapter = simpleAdapter 64 | layoutManager = LinearLayoutManager(playerActivity) 65 | hasFixedSize() 66 | } 67 | simpleAdapter?.addManyItems(songs.toMutableList()) 68 | buttonListShuffle.visibility= View.GONE 69 | buttonListSort.visibility= View.GONE 70 | buttonListPlayAll.visibility= View.GONE 71 | } 72 | 73 | 74 | override fun onResume() { 75 | super.onResume() 76 | getService() 77 | } 78 | 79 | override fun onViewClicked(view: View, item: Any, position: Int) { 80 | onSongSelected(songs[position], songs) 81 | } 82 | 83 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 84 | 85 | } 86 | 87 | private fun onSongSelected(song: Song, songs: MutableList) { 88 | mPlayerAdapter!!.setCurrentSong(song, songs) 89 | mPlayerAdapter!!.initMediaPlayer() 90 | mPlayerAdapter!!.getMediaPlayer()?.start() 91 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 92 | mMusicNotificationManager!!.createNotification()) 93 | 94 | 95 | } 96 | 97 | 98 | fun getMusicList() { 99 | val songString = arguments?.getString(Universal.SONGS_BUNDLE) 100 | val gson = Gson() 101 | val type: Type = object : TypeToken>() {}.type 102 | val songs = gson.fromJson>(songString, type) 103 | if (songs != null && songs.size > 0) { 104 | this.songs = songs 105 | } 106 | } 107 | 108 | private fun getService() { 109 | mMusicService = playerActivity?.getPlayerService() 110 | if (mMusicService != null) { 111 | mPlayerAdapter = mMusicService!!.mediaPlayerHolder 112 | mMusicNotificationManager = mMusicService!!.musicNotificationManager 113 | } 114 | if (mPlaybackListener == null) { 115 | mPlaybackListener = PlaybackListener() 116 | mPlayerAdapter?.setPlaybackInfoListener(mPlaybackListener!!) 117 | } 118 | } 119 | 120 | private fun updatePlayingInfo(restore: Boolean, startPlay: Boolean) { 121 | if (startPlay) { 122 | mPlayerAdapter!!.getMediaPlayer()?.start() 123 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 124 | mMusicNotificationManager!!.createNotification()) 125 | 126 | } 127 | val currentSong = mPlayerAdapter?.getCurrentSong() 128 | playerActivity?.updatePlaying(currentSong!!) 129 | 130 | if (restore) { 131 | 132 | if (mMusicService!!.isRestoredFromPause) { 133 | mMusicService!!.stopForeground(false) 134 | mMusicService!!.musicNotificationManager?.notificationManager!! 135 | .notify(BXNotificationManager.NOTIFICATION_ID, 136 | mMusicService!!.musicNotificationManager?.notificationBuilder?.build()) 137 | mMusicService!!.isRestoredFromPause = false 138 | } 139 | 140 | } 141 | } 142 | 143 | internal inner class PlaybackListener : PlaybackInfoListener() { 144 | override fun onPositionChanged(position: Int) { 145 | 146 | } 147 | 148 | override fun onStateChanged(@State state: Int) { 149 | if (mPlayerAdapter?.getState() != State.RESUMED && mPlayerAdapter?.getState() != State.PAUSED) { 150 | updatePlayingInfo(false, startPlay = true) 151 | } 152 | } 153 | 154 | override fun onPlaybackCompleted() { 155 | 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentArtists.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.support.v4.app.Fragment 7 | import android.support.v7.widget.LinearLayoutManager 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.PopupMenu 12 | import com.google.gson.Gson 13 | import com.revosleap.proxima.R 14 | import com.revosleap.proxima.callbacks.BXColor 15 | import com.revosleap.proxima.models.Artist 16 | import com.revosleap.proxima.models.Song 17 | import com.revosleap.proxima.ui.activities.PlayerActivity 18 | import com.revosleap.proxima.utils.utils.ArtistProvider 19 | import com.revosleap.proxima.utils.utils.PreferenceHelper 20 | import com.revosleap.proxima.utils.utils.Universal 21 | import com.revosleap.simpleadapter.SimpleAdapter 22 | import com.revosleap.simpleadapter.SimpleCallbacks 23 | import kotlinx.android.synthetic.main.artist_item.view.* 24 | import kotlinx.android.synthetic.main.fragment_artists.* 25 | import org.jetbrains.anko.AnkoLogger 26 | 27 | 28 | class FragmentArtists : Fragment(), SimpleCallbacks, AnkoLogger, BXColor { 29 | private var preferenceHelper: PreferenceHelper? = null 30 | private var simpleAdapter: SimpleAdapter? = null 31 | private var artistList = mutableListOf() 32 | private var viewColors = 0 33 | private var playerActivity: PlayerActivity? = null 34 | 35 | override fun onAttach(context: Context?) { 36 | super.onAttach(context) 37 | playerActivity = activity as PlayerActivity 38 | } 39 | 40 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 41 | savedInstanceState: Bundle?): View? { 42 | simpleAdapter = SimpleAdapter(R.layout.artist_item, this) 43 | artistList = ArtistProvider.getAllArtists(activity!!) 44 | preferenceHelper = PreferenceHelper(activity!!) 45 | return inflater.inflate(R.layout.fragment_artists, container, false) 46 | } 47 | 48 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 49 | super.onViewCreated(view, savedInstanceState) 50 | getSongs() 51 | artistrecycler.apply { 52 | adapter = simpleAdapter 53 | layoutManager = LinearLayoutManager(activity) 54 | hasFixedSize() 55 | } 56 | buttonListSortArtists.setOnClickListener { 57 | sortArtists(it) 58 | } 59 | if (viewColors != 0) { 60 | buttonListSortArtists?.setColorFilter(viewColors) 61 | } 62 | } 63 | 64 | override fun bindView(view: View, item: Any, position: Int) { 65 | item as Artist 66 | val artistName = view.textViewArtistName 67 | val artistInfo = view.textViewArtistInfo 68 | artistName.text = item.name 69 | var album = "Album" 70 | var track = "Track" 71 | if (item.albums.size > 1) { 72 | album = "Albums" 73 | } 74 | if (item.songCount > 1) { 75 | track = "Tracks" 76 | } 77 | val info = item.songCount.toString() + " $track | " + item.albums.size + " $album" 78 | artistInfo.text = info 79 | } 80 | 81 | override fun onViewClicked(view: View, item: Any, position: Int) { 82 | viewDetails(position) 83 | } 84 | 85 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 86 | 87 | } 88 | 89 | override fun songColor(color: Int) { 90 | viewColors = color 91 | 92 | } 93 | 94 | private fun viewDetails(position: Int) { 95 | val fragmentArtistInfo = FragmentArtistInfo() 96 | val artist = artistList[position] 97 | val albums = artist.albums 98 | val songs = mutableListOf() 99 | albums.forEach { 100 | it.songs.forEach { song -> 101 | songs.add(song) 102 | } 103 | } 104 | val gson = Gson() 105 | val albumString = gson.toJson(albums) 106 | val songString = gson.toJson(songs) 107 | val bundle = Bundle() 108 | bundle.putString(Universal.ALBUMS_BUNDLE, albumString) 109 | bundle.putString(Universal.SONGS_BUNDLE, songString) 110 | fragmentArtistInfo.arguments = bundle 111 | playerActivity?.supportFragmentManager!! 112 | .beginTransaction() 113 | .replace(R.id.frame_current, fragmentArtistInfo, Universal.ALBUM_INFO_TAG) 114 | .addToBackStack(null) 115 | .commit() 116 | playerActivity?.replaceFragment() 117 | 118 | } 119 | 120 | private fun sortArtists(view: View) { 121 | val menu = PopupMenu(activity!!, view) 122 | menu.inflate(R.menu.artist_sorting) 123 | menu.show() 124 | menu.setOnMenuItemClickListener { item -> 125 | when (item?.itemId) { 126 | R.id.item_name -> { 127 | preferenceHelper?.artistSorting = Universal.SORT_BY_NAME 128 | val sorted = artistList.sortedWith(compareBy { it.name }) 129 | artistList = sorted.toMutableList() 130 | simpleAdapter?.changeItems(artistList.toMutableList()) 131 | } 132 | R.id.item_songs -> { 133 | preferenceHelper?.artistSorting = Universal.SORT_BY_SONGS 134 | val sorted = artistList.sortedWith(compareBy { it.songCount }) 135 | artistList = sorted.toMutableList() 136 | artistList.reverse() 137 | simpleAdapter?.changeItems(artistList.toMutableList()) 138 | } 139 | } 140 | true 141 | } 142 | } 143 | 144 | private fun getSongs() { 145 | val sorting = preferenceHelper?.artistSorting 146 | if (sorting?.isEmpty()!!) { 147 | simpleAdapter?.addManyItems(artistList.toMutableList()) 148 | } 149 | if (sorting == Universal.SORT_BY_SONGS) { 150 | val sorted = artistList.sortedWith(compareBy { it.songCount }) 151 | artistList = sorted.toMutableList() 152 | artistList.reverse() 153 | simpleAdapter?.changeItems(artistList.toMutableList()) 154 | } 155 | if (sorting == Universal.SORT_BY_NAME) { 156 | val sorted = artistList.sortedWith(compareBy { it.name }) 157 | artistList = sorted.toMutableList() 158 | simpleAdapter?.changeItems(artistList.toMutableList()) 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentAlbum.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.support.v4.app.Fragment 7 | import android.support.v7.widget.GridLayoutManager 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.PopupMenu 12 | import com.google.gson.Gson 13 | import com.revosleap.proxima.R 14 | import com.revosleap.proxima.callbacks.BXColor 15 | import com.revosleap.proxima.models.Album 16 | import com.revosleap.proxima.models.Song 17 | import com.revosleap.proxima.ui.activities.PlayerActivity 18 | import com.revosleap.proxima.utils.utils.AlbumProvider 19 | import com.revosleap.proxima.utils.utils.PreferenceHelper 20 | import com.revosleap.proxima.utils.utils.SongProvider 21 | import com.revosleap.proxima.utils.utils.Universal 22 | import com.revosleap.simpleadapter.SimpleAdapter 23 | import com.revosleap.simpleadapter.SimpleCallbacks 24 | import kotlinx.android.synthetic.main.album_item.view.* 25 | import kotlinx.android.synthetic.main.fragment_fragment_album.* 26 | import org.jetbrains.anko.AnkoLogger 27 | import org.jetbrains.anko.info 28 | 29 | 30 | class FragmentAlbum : Fragment(), SimpleCallbacks, AnkoLogger, BXColor { 31 | private var musicList = mutableListOf() 32 | private var albumList = mutableListOf() 33 | var simpleAdapter: SimpleAdapter? = null 34 | private var preferenceHelper: PreferenceHelper? = null 35 | private var playerActivity: PlayerActivity? = null 36 | private var viewColor = 0 37 | 38 | override fun onAttach(context: Context?) { 39 | super.onAttach(context) 40 | playerActivity = activity!! as PlayerActivity 41 | } 42 | 43 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 44 | savedInstanceState: Bundle?): View? { 45 | simpleAdapter = SimpleAdapter(R.layout.album_item, this) 46 | musicList = SongProvider.getAllDeviceSongs(activity!!) 47 | info(musicList.size) 48 | preferenceHelper = PreferenceHelper(activity!!) 49 | return inflater.inflate(R.layout.fragment_fragment_album, container, false) 50 | } 51 | 52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 53 | super.onViewCreated(view, savedInstanceState) 54 | getAlbumList() 55 | simpleAdapter?.setHasStableIds(true) 56 | recyclerViewAlbum.apply { 57 | adapter = simpleAdapter 58 | layoutManager = GridLayoutManager(activity!!, 2) 59 | hasFixedSize() 60 | } 61 | buttonListSortAlbums.setOnClickListener { 62 | sortAlbums(it) 63 | } 64 | if (viewColor != 0) { 65 | buttonListSortAlbums?.setColorFilter(viewColor) 66 | } 67 | } 68 | 69 | override fun bindView(view: View, item: Any, position: Int) { 70 | item as Album 71 | val albumTitle = view.textViewAlbumName 72 | val albumArtist = view.textViewAlbumArtist 73 | val artist = item.artistName + " (" + item.songs.size + ")" 74 | albumTitle.text = item.title 75 | albumArtist.text = artist 76 | } 77 | 78 | override fun onViewClicked(view: View, item: Any, position: Int) { 79 | goToInfo(position) 80 | } 81 | 82 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 83 | 84 | } 85 | 86 | override fun songColor(color: Int) { 87 | viewColor = color 88 | 89 | } 90 | 91 | private fun goToInfo(position: Int) { 92 | val gson = Gson() 93 | val gsonString = gson.toJson(albumList[position].songs) 94 | val albumInfo = FragmentAlbumInfo() 95 | val bundle = Bundle() 96 | bundle.putString(Universal.ALBUM_BUNDLE, gsonString) 97 | albumInfo.arguments = bundle 98 | playerActivity?.supportFragmentManager!! 99 | .beginTransaction() 100 | .replace(R.id.frame_current, albumInfo, Universal.ALBUM_INFO_TAG) 101 | .addToBackStack(null) 102 | .commit() 103 | playerActivity?.replaceFragment() 104 | } 105 | 106 | private fun sortAlbums(view: View) { 107 | val menu = PopupMenu(activity!!, view) 108 | menu.inflate(R.menu.album_sorting) 109 | menu.show() 110 | menu.setOnMenuItemClickListener { item -> 111 | when (item?.itemId) { 112 | R.id.item_artist -> { 113 | preferenceHelper?.sortingStyle = Universal.SORT_BY_ARTIST 114 | val sorted = albumList.sortedWith(compareBy { it.artistName }) 115 | simpleAdapter?.changeItems(sorted.toMutableList()) 116 | albumList = sorted.toMutableList() 117 | } 118 | R.id.item_release -> { 119 | preferenceHelper?.sortingStyle = Universal.SORT_BY_YEAR 120 | val sorted = albumList.sortedWith(compareBy { it.year }) 121 | simpleAdapter?.changeItems(sorted.toMutableList()) 122 | albumList = sorted.toMutableList() 123 | } 124 | R.id.item_title -> { 125 | preferenceHelper?.sortingStyle = Universal.SORT_BY_TITLE 126 | val sorted = albumList.sortedWith(compareBy { it.title }) 127 | simpleAdapter?.changeItems(sorted.toMutableList()) 128 | albumList = sorted.toMutableList() 129 | } 130 | 131 | } 132 | true 133 | } 134 | } 135 | 136 | private fun getAlbumList() { 137 | val sortOrder = preferenceHelper?.albumSorting 138 | albumList = AlbumProvider.retrieveAlbums(musicList) 139 | if (sortOrder!!.isEmpty()) { 140 | simpleAdapter?.addManyItems(albumList.toMutableList()) 141 | 142 | } 143 | if (sortOrder == Universal.SORT_BY_ARTIST) { 144 | val sorted = albumList.sortedWith(compareBy { it.artistName }) 145 | simpleAdapter?.changeItems(sorted.toMutableList()) 146 | albumList = sorted.toMutableList() 147 | } 148 | if (sortOrder == Universal.SORT_BY_TITLE) { 149 | val sorted = albumList.sortedWith(compareBy { it.title }) 150 | simpleAdapter?.changeItems(sorted.toMutableList()) 151 | albumList = sorted.toMutableList() 152 | } 153 | if (sortOrder == Universal.SORT_BY_YEAR) { 154 | val dateSorted = albumList.sortedWith(compareBy { it.year }) 155 | simpleAdapter?.changeItems(dateSorted.toMutableList()) 156 | albumList = dateSorted.toMutableList() 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentAlbumInfo.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.support.v7.widget.LinearLayoutManager 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import com.google.gson.Gson 11 | import com.google.gson.reflect.TypeToken 12 | import com.revosleap.proxima.R 13 | import com.revosleap.proxima.models.Song 14 | import com.revosleap.proxima.services.MusicPlayerService 15 | import com.revosleap.proxima.ui.activities.PlayerActivity 16 | import com.revosleap.proxima.utils.playback.BXNotificationManager 17 | import com.revosleap.proxima.utils.playback.PlaybackInfoListener 18 | import com.revosleap.proxima.callbacks.PlayerAdapter 19 | import com.revosleap.proxima.utils.utils.Universal 20 | import com.revosleap.simpleadapter.SimpleAdapter 21 | import com.revosleap.simpleadapter.SimpleCallbacks 22 | import kotlinx.android.synthetic.main.fragment_album_details.* 23 | import kotlinx.android.synthetic.main.track.view.* 24 | import java.lang.reflect.Type 25 | 26 | class FragmentAlbumInfo : Fragment(), SimpleCallbacks { 27 | private var albumString: String? = null 28 | private var playerActivity: PlayerActivity? = null 29 | private var songs = mutableListOf() 30 | private var simpleAdapter: SimpleAdapter? = null 31 | private var mPlayerAdapter: PlayerAdapter? = null 32 | private var mMusicService: MusicPlayerService? = null 33 | private var mMusicNotificationManager: BXNotificationManager? = null 34 | private var mPlaybackListener: PlaybackListener? = null 35 | override fun onAttach(context: Context?) { 36 | super.onAttach(context) 37 | playerActivity = activity!! as PlayerActivity 38 | getService() 39 | } 40 | 41 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 42 | savedInstanceState: Bundle?): View? { 43 | albumString = arguments?.getString(Universal.ALBUM_BUNDLE) 44 | simpleAdapter = SimpleAdapter(R.layout.track, this) 45 | getMusicList() 46 | return inflater.inflate(R.layout.fragment_album_details, container, false) 47 | } 48 | 49 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 50 | super.onViewCreated(view, savedInstanceState) 51 | recyclerViewAlbum.apply { 52 | adapter = simpleAdapter 53 | layoutManager = LinearLayoutManager(playerActivity) 54 | hasFixedSize() 55 | } 56 | simpleAdapter?.addManyItems(songs.toMutableList()) 57 | setButtons() 58 | val params = cordinator.layoutParams as ViewGroup.MarginLayoutParams 59 | params.bottomMargin = playerActivity?.controls()!!.height 60 | cordinator.layoutParams = params 61 | textViewAlbumArtist.text = songs[0].artist 62 | textViewAlbumName.text=songs[0].albumName 63 | } 64 | 65 | override fun onResume() { 66 | super.onResume() 67 | getService() 68 | } 69 | 70 | override fun bindView(view: View, item: Any, position: Int) { 71 | item as Song 72 | val titleText = view.textViewTitleTrack 73 | val artistText = view.textViewArtistTrack 74 | titleText.text = item.artist 75 | artistText.text = item.title 76 | } 77 | 78 | override fun onViewClicked(view: View, item: Any, position: Int) { 79 | onSongSelected(songs[position], songs) 80 | } 81 | 82 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 83 | 84 | } 85 | private fun setButtons(){ 86 | buttonListPlayAll.setOnClickListener { 87 | onSongSelected(songs[0],songs) 88 | } 89 | buttonListShuffle.setOnClickListener { 90 | songs.shuffle() 91 | onSongSelected(songs[0],songs) 92 | } 93 | } 94 | private fun onSongSelected(song: Song, songs: MutableList) { 95 | mPlayerAdapter!!.setCurrentSong(song, songs) 96 | mPlayerAdapter!!.initMediaPlayer() 97 | mPlayerAdapter!!.getMediaPlayer()?.start() 98 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 99 | mMusicNotificationManager!!.createNotification()) 100 | 101 | 102 | } 103 | 104 | private fun getMusicList() { 105 | if (albumString != null) { 106 | val gson = Gson() 107 | val type: Type = object : TypeToken>() {}.type 108 | val songs = gson.fromJson>(albumString, type) 109 | if (songs != null && songs.size > 0) { 110 | this.songs = songs 111 | } 112 | } 113 | } 114 | 115 | private fun getService() { 116 | mMusicService = playerActivity?.getPlayerService() 117 | if (mMusicService != null) { 118 | mPlayerAdapter = mMusicService!!.mediaPlayerHolder 119 | mMusicNotificationManager = mMusicService!!.musicNotificationManager 120 | } 121 | if (mPlaybackListener == null) { 122 | mPlaybackListener = PlaybackListener() 123 | mPlayerAdapter?.setPlaybackInfoListener(mPlaybackListener!!) 124 | } 125 | } 126 | 127 | private fun updatePlayingInfo(restore: Boolean, startPlay: Boolean) { 128 | if (startPlay) { 129 | mPlayerAdapter!!.getMediaPlayer()?.start() 130 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 131 | mMusicNotificationManager!!.createNotification()) 132 | 133 | } 134 | val currentSong = mPlayerAdapter?.getCurrentSong() 135 | playerActivity?.updatePlaying(currentSong!!) 136 | 137 | if (restore) { 138 | 139 | if (mMusicService!!.isRestoredFromPause) { 140 | mMusicService!!.stopForeground(false) 141 | mMusicService!!.musicNotificationManager?.notificationManager!! 142 | .notify(BXNotificationManager.NOTIFICATION_ID, 143 | mMusicService!!.musicNotificationManager?.notificationBuilder?.build()) 144 | mMusicService!!.isRestoredFromPause = false 145 | } 146 | 147 | } 148 | } 149 | 150 | internal inner class PlaybackListener : PlaybackInfoListener() { 151 | override fun onPositionChanged(position: Int) { 152 | 153 | } 154 | 155 | override fun onStateChanged(@State state: Int) { 156 | if (mPlayerAdapter?.getState() != State.RESUMED && mPlayerAdapter?.getState() != State.PAUSED) { 157 | updatePlayingInfo(false, startPlay = true) 158 | } 159 | } 160 | 161 | override fun onPlaybackCompleted() { 162 | 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/utils/playback/BXNotificationManager.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.utils.playback 2 | 3 | import android.app.Notification 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.app.PendingIntent 7 | import android.content.Context 8 | import android.content.Intent 9 | import android.graphics.Bitmap 10 | import android.graphics.Canvas 11 | import android.graphics.Color 12 | import android.media.session.MediaSessionManager 13 | import android.os.Build 14 | import android.os.RemoteException 15 | import android.support.annotation.RequiresApi 16 | import android.support.v4.app.NotificationCompat 17 | import android.support.v4.media.MediaMetadataCompat 18 | import android.support.v4.media.session.MediaControllerCompat 19 | import android.support.v4.media.session.MediaSessionCompat 20 | 21 | import com.revosleap.proxima.models.Song 22 | import com.revosleap.proxima.utils.utils.AudioUtils 23 | import com.revosleap.proxima.ui.activities.PlayerActivity 24 | import com.revosleap.proxima.R 25 | import com.revosleap.proxima.services.MusicPlayerService 26 | import com.revosleap.proxima.utils.utils.Universal 27 | 28 | class BXNotificationManager internal constructor(private val musicPlayerService: MusicPlayerService) { 29 | private val channelId = "com.revosleap.proxima.channelId" 30 | private val requestCode = 100 31 | val notificationManager: NotificationManager = musicPlayerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 32 | private var mediaSession: MediaSessionCompat? = null 33 | private var mediaSessionManager: MediaSessionManager? = null 34 | private var transportControls: MediaControllerCompat.TransportControls? = null 35 | var notificationBuilder: NotificationCompat.Builder? = null 36 | private set 37 | private val context: Context 38 | 39 | init { 40 | context = musicPlayerService.application 41 | } 42 | 43 | private fun playerAction(action: String): PendingIntent { 44 | 45 | val pauseIntent = Intent() 46 | pauseIntent.action = action 47 | 48 | return PendingIntent.getBroadcast(musicPlayerService, requestCode, pauseIntent, PendingIntent.FLAG_UPDATE_CURRENT) 49 | } 50 | 51 | private fun notificationAction(action: String): NotificationCompat.Action { 52 | 53 | val icon: Int 54 | 55 | icon = when (action) { 56 | PREV_ACTION -> R.drawable.previous 57 | PLAY_PAUSE_ACTION -> 58 | 59 | if (musicPlayerService.mediaPlayerHolder?.getState() != PlaybackInfoListener.State.PAUSED) 60 | R.drawable.pause 61 | else 62 | R.drawable.play_icon 63 | NEXT_ACTION -> R.drawable.next 64 | else -> R.drawable.previous 65 | } 66 | return NotificationCompat.Action.Builder(icon, action, playerAction(action)).build() 67 | } 68 | 69 | fun createNotification(): Notification { 70 | 71 | val song = musicPlayerService.mediaPlayerHolder?.getCurrentSong() 72 | 73 | notificationBuilder = NotificationCompat.Builder(musicPlayerService, channelId) 74 | 75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 76 | createNotificationChannel() 77 | } 78 | 79 | val openPlayerIntent = Intent(musicPlayerService, PlayerActivity::class.java) 80 | openPlayerIntent.action =Universal.infoAction 81 | openPlayerIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP 82 | val contentIntent = PendingIntent.getActivity(musicPlayerService, requestCode, 83 | openPlayerIntent, 0) 84 | updateMetaData(song!!) 85 | val artist = song.artist 86 | val songTitle = song.title 87 | notificationBuilder!! 88 | .setShowWhen(false) 89 | .setSmallIcon(R.drawable.ic_if_speaker) 90 | .setLargeIcon(AudioUtils.cover(song.path!!,context)) 91 | .setColor(context.resources.getColor(R.color.colorAccentLight)) 92 | .setContentTitle(songTitle) 93 | .setContentText(artist) 94 | .setContentIntent(contentIntent) 95 | .addAction(notificationAction(PREV_ACTION)) 96 | .addAction(notificationAction(PLAY_PAUSE_ACTION)) 97 | .addAction(notificationAction(NEXT_ACTION)) 98 | .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) 99 | 100 | notificationBuilder!!.setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle() 101 | .setShowActionsInCompactView(0, 1, 2) 102 | .setMediaSession(mediaSession!!.sessionToken)) 103 | return notificationBuilder!!.build() 104 | } 105 | 106 | @RequiresApi(26) 107 | private fun createNotificationChannel() { 108 | 109 | if (notificationManager.getNotificationChannel(channelId) == null) { 110 | val notificationChannel = NotificationChannel(channelId, 111 | musicPlayerService.getString(R.string.app_name), 112 | NotificationManager.IMPORTANCE_LOW) 113 | 114 | notificationChannel.description = musicPlayerService.getString(R.string.app_name) 115 | 116 | notificationChannel.enableLights(false) 117 | notificationChannel.enableVibration(false) 118 | notificationChannel.setShowBadge(false) 119 | 120 | notificationManager.createNotificationChannel(notificationChannel) 121 | } 122 | } 123 | 124 | private fun updateMetaData(mSelectedSong: Song) { 125 | mediaSession = MediaSessionCompat(context, "BXPlayer") 126 | mediaSession!!.setMetadata(MediaMetadataCompat.Builder() 127 | .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, AudioUtils.cover(mSelectedSong.path!!,context)) 128 | .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, mSelectedSong.artist) 129 | .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, mSelectedSong.albumName) 130 | .putString(MediaMetadataCompat.METADATA_KEY_TITLE, mSelectedSong.title) 131 | .build()) 132 | } 133 | 134 | @Throws(RemoteException::class) 135 | private fun initMediaSession(model: Song) { 136 | if (mediaSessionManager != null) return //mediaSessionManager exists 137 | 138 | mediaSessionManager = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager 139 | // Create a new MediaSession 140 | mediaSession = MediaSessionCompat(context.applicationContext, "AudioPlayer") 141 | //Get MediaSessions transport controls 142 | transportControls = mediaSession!!.controller.transportControls 143 | //set MediaSession -> ready to receive media commands 144 | mediaSession!!.isActive = true 145 | //indicate that the MediaSession handles transport control commands 146 | // through its MediaSessionCompat.Callback. 147 | mediaSession!!.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS) 148 | updateMetaData(model) 149 | //Set mediaSession's MetaData 150 | 151 | 152 | } 153 | 154 | private fun getLargeIcon(image: Bitmap): Bitmap { 155 | 156 | // final VectorDrawable vectorDrawable = (VectorDrawable) musicPlayerService.getDrawable(R.drawable.cover2); 157 | 158 | val largeIconSize = context.resources.getDimensionPixelSize(R.dimen.notification_large_dim) 159 | val map = Bitmap.createBitmap(largeIconSize, largeIconSize, Bitmap.Config.ARGB_8888) 160 | val canvas = Canvas(map) 161 | canvas.drawColor(Color.TRANSPARENT) 162 | val bitmap = image.copy(Bitmap.Config.ARGB_8888, true) 163 | val height = bitmap.height 164 | val width = bitmap.width 165 | bitmap.width = width * 5 / 10 166 | bitmap.height = height * 5 / 10 167 | canvas.drawBitmap(map, 2f, 2f, null) 168 | 169 | return Bitmap.createScaledBitmap(image, 50, 50, true) 170 | } 171 | 172 | interface BitmapColors { 173 | fun setColors(color: Int) 174 | } 175 | 176 | companion object { 177 | const val NOTIFICATION_ID = 101 178 | internal const val PLAY_PAUSE_ACTION = "com.revosleap.proxima.PLAYPAUSE" 179 | const internal val NEXT_ACTION = "com.revosleap.proxima.NEXT" 180 | const internal val PREV_ACTION = "com.revosleap.proxima.PREV" 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentTracks.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.ServiceConnection 7 | import android.os.Bundle 8 | import android.os.Handler 9 | import android.os.IBinder 10 | import android.support.v4.app.Fragment 11 | import android.support.v7.widget.LinearLayoutManager 12 | import android.view.LayoutInflater 13 | import android.view.MenuItem 14 | import android.view.View 15 | import android.view.ViewGroup 16 | import android.widget.PopupMenu 17 | import com.revosleap.proxima.R 18 | import com.revosleap.proxima.callbacks.BXColor 19 | import com.revosleap.proxima.models.Song 20 | import com.revosleap.proxima.services.MusicPlayerService 21 | import com.revosleap.proxima.ui.activities.PlayerActivity 22 | import com.revosleap.proxima.utils.playback.BXNotificationManager 23 | import com.revosleap.proxima.utils.playback.PlaybackInfoListener 24 | import com.revosleap.proxima.callbacks.PlayerAdapter 25 | import com.revosleap.proxima.utils.utils.GetAudio 26 | import com.revosleap.proxima.utils.utils.PreferenceHelper 27 | import com.revosleap.proxima.utils.utils.Universal 28 | import com.revosleap.simpleadapter.SimpleAdapter 29 | import com.revosleap.simpleadapter.SimpleCallbacks 30 | import kotlinx.android.synthetic.main.fragment_tracks.* 31 | import kotlinx.android.synthetic.main.track.view.* 32 | 33 | 34 | class FragmentTracks : Fragment(), SimpleCallbacks,BXColor { 35 | private var serviceBound = false 36 | private var list = mutableListOf() 37 | private var mMusicService: MusicPlayerService? = null 38 | private var mIsBound: Boolean = false 39 | private var simpleAdapter: SimpleAdapter? = null 40 | private var mPlayerAdapter: PlayerAdapter? = null 41 | private var mMusicNotificationManager: BXNotificationManager? = null 42 | private var mPlaybackListener: PlaybackListener? = null 43 | private var preferenceHelper: PreferenceHelper? = null 44 | private var playerActivity:PlayerActivity?=null 45 | private var viewColor =0 46 | private val mConnection = object : ServiceConnection { 47 | override fun onServiceConnected(name: ComponentName, service: IBinder) { 48 | mMusicService = (service as MusicPlayerService.MusicBinder).serviceInstance 49 | mPlayerAdapter = mMusicService!!.mediaPlayerHolder 50 | mMusicNotificationManager = mMusicService!!.musicNotificationManager 51 | if (mPlaybackListener == null) { 52 | mPlaybackListener = PlaybackListener() 53 | mPlayerAdapter!!.setPlaybackInfoListener(mPlaybackListener!!) 54 | } 55 | } 56 | 57 | override fun onServiceDisconnected(name: ComponentName) { 58 | mMusicService = null 59 | } 60 | } 61 | 62 | override fun onAttach(context: Context?) { 63 | super.onAttach(context) 64 | playerActivity= activity!! as PlayerActivity 65 | doBindService() 66 | } 67 | 68 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 69 | savedInstanceState: Bundle?): View? { 70 | val view = inflater.inflate(R.layout.fragment_tracks, container, false) 71 | list = GetAudio().geAllAudio(activity!!.baseContext) 72 | simpleAdapter = SimpleAdapter(R.layout.track, this) 73 | preferenceHelper = PreferenceHelper(activity!!) 74 | return view 75 | } 76 | 77 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 78 | super.onViewCreated(view, savedInstanceState) 79 | getSongList() 80 | trackRecycler.apply { 81 | adapter = simpleAdapter 82 | layoutManager = LinearLayoutManager(activity) 83 | hasFixedSize() 84 | } 85 | buttonListPlayAll.setOnClickListener { 86 | onSongSelected(list[0], list) 87 | } 88 | buttonListShuffle.setOnClickListener { 89 | list.shuffle() 90 | onSongSelected(list[0], list) 91 | } 92 | buttonListSort.setOnClickListener { 93 | getSorting(it) 94 | } 95 | if (viewColor!=0){ 96 | setColors(viewColor) 97 | } 98 | } 99 | 100 | override fun onResume() { 101 | super.onResume() 102 | if (viewColor!=0){ 103 | setColors(viewColor) 104 | } 105 | } 106 | 107 | override fun onSaveInstanceState(outState: Bundle) { 108 | super.onSaveInstanceState(outState) 109 | outState.putBoolean("serviceStatus", serviceBound) 110 | } 111 | 112 | override fun onDestroy() { 113 | super.onDestroy() 114 | super.onDestroy() 115 | mPlaybackListener = null 116 | doUnbindService() 117 | } 118 | override fun songColor(color: Int) { 119 | viewColor = color 120 | setColors(color) 121 | 122 | } 123 | private fun setColors(color: Int){ 124 | buttonListSort?.setColorFilter(color) 125 | buttonListShuffle?.setColorFilter(color) 126 | buttonListPlayAll?.setColorFilter(color) 127 | } 128 | private fun onSongSelected(song: Song, songs:MutableList) { 129 | mPlayerAdapter!!.setCurrentSong(song, songs) 130 | mPlayerAdapter!!.initMediaPlayer() 131 | mPlayerAdapter!!.getMediaPlayer()?.start() 132 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 133 | mMusicNotificationManager!!.createNotification()) 134 | 135 | 136 | } 137 | 138 | override fun bindView(view: View, item: Any, position: Int) { 139 | item as Song 140 | val title = view.textViewTitleTrack 141 | val artist = view.textViewArtistTrack 142 | val image = view.imageView2 143 | artist.text = item.title 144 | title.text = item.artist 145 | 146 | } 147 | 148 | override fun onViewClicked(view: View, item: Any, position: Int) { 149 | onSongSelected(list[position], list) 150 | } 151 | 152 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 153 | 154 | } 155 | 156 | private fun getSongList() { 157 | val sortOrder = preferenceHelper?.sortingStyle 158 | if (sortOrder!!.isEmpty()) { 159 | simpleAdapter?.addManyItems(list.toMutableList()) 160 | } 161 | if (sortOrder == Universal.SORT_BY_ARTIST) { 162 | val sorted = list.sortedWith(compareBy { it.artist }) 163 | simpleAdapter?.changeItems(sorted.toMutableList()) 164 | list = sorted.toMutableList() 165 | } 166 | if (sortOrder == Universal.SORT_BY_TITLE) { 167 | val sorted = list.sortedWith(compareBy { it.title }) 168 | simpleAdapter?.changeItems(sorted.toMutableList()) 169 | list = sorted.toMutableList() 170 | } 171 | if (sortOrder == Universal.SORT_BY_YEAR) { 172 | val dateSorted = list.sortedWith(compareBy { it.songYear }) 173 | simpleAdapter?.changeItems(dateSorted.toMutableList()) 174 | list = dateSorted.toMutableList() 175 | } 176 | } 177 | 178 | private fun getSorting(view: View) { 179 | val menu = PopupMenu(activity!!, view) 180 | menu.menuInflater.inflate(R.menu.track_sorting, menu.menu) 181 | menu.show() 182 | menu.setOnMenuItemClickListener(object : PopupMenu.OnMenuItemClickListener { 183 | override fun onMenuItemClick(p0: MenuItem?): Boolean { 184 | when (p0?.itemId) { 185 | R.id.item_artist -> { 186 | preferenceHelper?.sortingStyle = Universal.SORT_BY_ARTIST 187 | val sorted = list.sortedWith(compareBy { it.artist }) 188 | simpleAdapter?.changeItems(sorted.toMutableList()) 189 | list = sorted.toMutableList() 190 | } 191 | R.id.item_date -> { 192 | preferenceHelper?.sortingStyle = Universal.SORT_BY_YEAR 193 | val dateSorted = list.sortedWith(compareBy { it.songYear }) 194 | simpleAdapter?.changeItems(dateSorted.toMutableList()) 195 | list = dateSorted.toMutableList() 196 | } 197 | R.id.item_title -> { 198 | preferenceHelper?.sortingStyle = Universal.SORT_BY_TITLE 199 | val sorted = list.sortedWith(compareBy { it.title }) 200 | simpleAdapter?.changeItems(sorted.toMutableList()) 201 | list = sorted.toMutableList() 202 | } 203 | 204 | 205 | } 206 | return true 207 | } 208 | }) 209 | } 210 | 211 | private fun doBindService() { 212 | activity!!.bindService(Intent(activity, 213 | MusicPlayerService::class.java), mConnection, Context.BIND_AUTO_CREATE) 214 | mIsBound = true 215 | 216 | val startNotStickyIntent = Intent(activity, MusicPlayerService::class.java) 217 | activity!!.startService(startNotStickyIntent) 218 | } 219 | 220 | private fun doUnbindService() { 221 | if (mIsBound) { 222 | // Detach our existing connection. 223 | activity!!.unbindService(mConnection) 224 | mIsBound = false 225 | } 226 | } 227 | 228 | internal inner class PlaybackListener : PlaybackInfoListener() { 229 | 230 | override fun onPositionChanged(position: Int) { 231 | 232 | } 233 | 234 | override fun onStateChanged(@State state: Int) { 235 | if (mPlayerAdapter?.getState() != State.RESUMED && mPlayerAdapter?.getState() != State.PAUSED) { 236 | updatePlayingInfo(false, startPlay = true) 237 | } 238 | } 239 | 240 | override fun onPlaybackCompleted() { 241 | 242 | } 243 | } 244 | 245 | private fun updatePlayingInfo(restore: Boolean, startPlay: Boolean) { 246 | if (startPlay) { 247 | mPlayerAdapter!!.getMediaPlayer()?.start() 248 | Handler().postDelayed({ 249 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 250 | mMusicNotificationManager!!.createNotification()) 251 | }, 250) 252 | } 253 | val song =mPlayerAdapter?.getCurrentSong()!! 254 | playerActivity?.updatePlaying(song) 255 | if (restore) { 256 | Handler().postDelayed({ 257 | //stop foreground if coming from pause state 258 | if (mMusicService!!.isRestoredFromPause) { 259 | mMusicService!!.stopForeground(false) 260 | mMusicService!!.musicNotificationManager?.notificationManager!! 261 | .notify(BXNotificationManager.NOTIFICATION_ID, 262 | mMusicService!!.musicNotificationManager?.notificationBuilder?.build()) 263 | mMusicService!!.isRestoredFromPause = false 264 | } 265 | }, 250) 266 | } 267 | } 268 | // val retriever = MediaMetadataRetriever() 269 | // val inputStream: InputStream? 270 | // retriever.setDataSource(path) 271 | // if (retriever.embeddedPicture != null) { 272 | // inputStream = ByteArrayInputStream(retriever.embeddedPicture) 273 | // Glide.with(holder.itemView.context).load(inputStream) 274 | // .into(holder.trackImage) 275 | // } 276 | 277 | } 278 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 32 | 33 | 46 | 47 | 54 | 55 | 56 | 57 | 73 | 74 | 87 | 88 | 100 | 101 | 115 | 116 | 128 | 129 | 130 | 142 | 143 | 156 | 157 | 167 | 168 | 179 | 180 | 192 | 193 | 205 | 206 | 218 | 219 | 231 | 232 | 244 | 245 | -------------------------------------------------------------------------------- /app/src/main/java/com/revosleap/proxima/ui/fragments/FragmentInfo.kt: -------------------------------------------------------------------------------- 1 | package com.revosleap.proxima.ui.fragments 2 | 3 | 4 | import android.content.Context 5 | import android.content.res.ColorStateList 6 | import android.graphics.Bitmap 7 | import android.graphics.BitmapFactory 8 | import android.media.MediaMetadataRetriever 9 | import android.os.Build 10 | import android.os.Bundle 11 | import android.os.Handler 12 | import android.support.v4.app.Fragment 13 | import android.support.v7.graphics.Palette 14 | import android.support.v7.widget.LinearLayoutManager 15 | import android.view.LayoutInflater 16 | import android.view.View 17 | import android.view.ViewGroup 18 | import android.widget.SeekBar 19 | import android.widget.Toast 20 | import com.revosleap.proxima.R 21 | import com.revosleap.proxima.callbacks.BXColor 22 | import com.revosleap.proxima.callbacks.PlayerAdapter 23 | import com.revosleap.proxima.models.Song 24 | import com.revosleap.proxima.services.MusicPlayerService 25 | import com.revosleap.proxima.ui.activities.PlayerActivity 26 | import com.revosleap.proxima.utils.playback.BXNotificationManager 27 | import com.revosleap.proxima.utils.playback.PlaybackInfoListener 28 | import com.revosleap.proxima.utils.utils.EqualizerUtils 29 | import com.revosleap.proxima.utils.utils.UniversalUtils 30 | import com.revosleap.simpleadapter.SimpleAdapter 31 | import com.revosleap.simpleadapter.SimpleCallbacks 32 | import kotlinx.android.synthetic.main.current_songs_item.view.* 33 | import kotlinx.android.synthetic.main.fragment_info.* 34 | import org.jetbrains.anko.startService 35 | import java.io.ByteArrayInputStream 36 | import java.io.InputStream 37 | 38 | 39 | class FragmentInfo : Fragment(), View.OnClickListener, BXColor, SimpleCallbacks { 40 | private var mUserIsSeeking: Boolean = false 41 | private var mSelectedArtist: String? = null 42 | private var mPlayerAdapter: PlayerAdapter? = null 43 | private var mMusicService: MusicPlayerService? = null 44 | private var mMusicNotificationManager: BXNotificationManager? = null 45 | private var mPlaybackListener: PlaybackListener? = null 46 | private var playerActivity: PlayerActivity? = null 47 | private var simpleAdapter: SimpleAdapter? = null 48 | private var currentPlayList = mutableListOf() 49 | var isPlayListShown = false 50 | override fun onAttach(context: Context?) { 51 | super.onAttach(context) 52 | getService() 53 | playerActivity = activity!! as PlayerActivity 54 | playerActivity?.setColorCallback(this) 55 | } 56 | 57 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 58 | savedInstanceState: Bundle?): View? { 59 | simpleAdapter = SimpleAdapter(R.layout.current_songs_item, this) 60 | return inflater.inflate(R.layout.fragment_info, container, false) 61 | } 62 | 63 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 64 | super.onViewCreated(view, savedInstanceState) 65 | setClickListeners() 66 | initializeSeekBar() 67 | showPlaylist() 68 | mPlaybackListener?.onStateChanged(PlaybackInfoListener.State.PLAYING) 69 | } 70 | 71 | override fun onResume() { 72 | super.onResume() 73 | getService() 74 | activity?.startService() 75 | mPlayerAdapter!!.getMediaPlayer()?.start() 76 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 77 | mMusicNotificationManager!!.createNotification()) 78 | } 79 | 80 | override fun bindView(view: View, item: Any, position: Int) { 81 | item as Song 82 | val title = view.textViewTitleCurrent 83 | val artist = view.textViewCurrentArtist 84 | title.text = item.title 85 | artist.text = item.artist 86 | } 87 | 88 | override fun onViewClicked(view: View, item: Any, position: Int) { 89 | onSongSelected(currentPlayList[position], currentPlayList) 90 | } 91 | 92 | override fun onViewLongClicked(it: View?, item: Any, position: Int) { 93 | 94 | } 95 | 96 | private fun onSongSelected(song: Song, songs: MutableList) { 97 | mPlayerAdapter?.setCurrentSong(song, songs) 98 | mPlayerAdapter?.initMediaPlayer() 99 | mPlayerAdapter?.getMediaPlayer()?.start() 100 | mMusicService?.startForeground(BXNotificationManager.NOTIFICATION_ID, 101 | mMusicNotificationManager?.createNotification()) 102 | 103 | 104 | } 105 | 106 | override fun songColor(color: Int) { 107 | buttonInfoPlaylist?.setColorFilter(color) 108 | buttonInfoPlay?.setColorFilter(color) 109 | buttonInfoAll?.setColorFilter(color) 110 | buttonInfoFave?.setColorFilter(color) 111 | buttonInfoVol?.setColorFilter(color) 112 | seekBarInfo?.progressBackgroundTintList = ColorStateList.valueOf(color) 113 | buttonInfoShuffle?.setColorFilter(color) 114 | buttonInfoNext?.setColorFilter(color) 115 | buttonInfoPrev?.setColorFilter(color) 116 | } 117 | 118 | private fun getBxColor(bitmap: Bitmap) { 119 | var color: Int 120 | color = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 121 | activity?.resources!!.getColor(R.color.colorAccent, null) 122 | } else activity?.resources!!.getColor(R.color.colorAccent) 123 | Palette.from(bitmap).generate { palette -> 124 | val vibrant = palette?.lightVibrantSwatch 125 | try { 126 | color = vibrant?.rgb!! 127 | songColor(color) 128 | playerActivity?.setViewColors(color) 129 | } catch (e: Exception) { 130 | } 131 | } 132 | 133 | 134 | } 135 | 136 | private fun showPlaylist() { 137 | recyclerViewCurrent.apply { 138 | adapter = simpleAdapter 139 | layoutManager = LinearLayoutManager(playerActivity) 140 | hasFixedSize() 141 | } 142 | 143 | 144 | } 145 | 146 | private fun getService() { 147 | val playerActivity = activity as PlayerActivity 148 | mMusicService = playerActivity.getPlayerService() 149 | if (mMusicService != null) { 150 | mPlayerAdapter = mMusicService!!.mediaPlayerHolder 151 | mMusicNotificationManager = mMusicService!!.musicNotificationManager 152 | } 153 | if (mPlaybackListener == null) { 154 | mPlaybackListener = PlaybackListener() 155 | mPlayerAdapter?.setPlaybackInfoListener(mPlaybackListener!!) 156 | } 157 | } 158 | 159 | private fun initializeSeekBar() { 160 | seekBarInfo.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 161 | var userSelectedPosition = 0 162 | 163 | override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 164 | if (fromUser) { 165 | userSelectedPosition = progress 166 | 167 | } 168 | textViewProgress?.text = UniversalUtils.formatTime(progress.toLong()) 169 | } 170 | 171 | override fun onStartTrackingTouch(seekBar: SeekBar) { 172 | mUserIsSeeking = true 173 | } 174 | 175 | override fun onStopTrackingTouch(seekBar: SeekBar) { 176 | if (mUserIsSeeking) { 177 | // mSongPosition.setTextColor(currentPositionColor); 178 | } 179 | mUserIsSeeking = false 180 | mPlayerAdapter!!.seekTo(userSelectedPosition) 181 | } 182 | }) 183 | } 184 | 185 | private fun updatePlayingStatus() { 186 | val drawable = if (mPlayerAdapter?.getState() != PlaybackInfoListener.State.PAUSED) 187 | R.drawable.pause 188 | else 189 | R.drawable.play_icon 190 | try { 191 | buttonInfoPlay.setImageDrawable(activity?.getDrawable(drawable)) 192 | 193 | } catch (e: Exception) { 194 | e.printStackTrace() 195 | } 196 | 197 | } 198 | 199 | private fun updatePlayingInfo(restore: Boolean, startPlay: Boolean) { 200 | 201 | 202 | if (startPlay) { 203 | mPlayerAdapter?.getMediaPlayer()?.start() 204 | Handler().postDelayed({ 205 | mMusicService!!.startForeground(BXNotificationManager.NOTIFICATION_ID, 206 | mMusicNotificationManager!!.createNotification()) 207 | }, 250) 208 | } 209 | currentPlayList = mPlayerAdapter?.getCurrentSongs()!! 210 | simpleAdapter?.addManyItems(currentPlayList.toMutableList()) 211 | val selectedSong = mPlayerAdapter?.getCurrentSong() 212 | mSelectedArtist = selectedSong?.artist 213 | val duration = selectedSong?.duration 214 | seekBarInfo?.max = duration?.toInt()!! 215 | textViewDuration?.text = UniversalUtils.formatTime(duration) 216 | textViewInfoTitle?.text = selectedSong.title 217 | textViewInfoArtist?.text = selectedSong.artist 218 | val retriever = MediaMetadataRetriever() 219 | val inputStream: InputStream? 220 | retriever.setDataSource(selectedSong.path) 221 | 222 | if (retriever.embeddedPicture != null) { 223 | inputStream = ByteArrayInputStream(retriever.embeddedPicture) 224 | val image = BitmapFactory.decodeStream(inputStream) 225 | imageViewInfo?.setImageBitmap(image) 226 | try { 227 | playerActivity?.updatePlaying(selectedSong) 228 | getBxColor(image) 229 | } catch (e: Exception) { 230 | } 231 | 232 | } else { 233 | val image = BitmapFactory.decodeResource(playerActivity?.resources!!, R.drawable.cover2) 234 | imageViewInfo?.setImageBitmap(image) 235 | try { 236 | playerActivity?.updatePlaying(selectedSong) 237 | getBxColor(image) 238 | } catch (e: Exception) { 239 | } 240 | } 241 | 242 | 243 | if (restore) { 244 | seekBarInfo?.progress = mPlayerAdapter?.getPlayerPosition()!! 245 | updatePlayingStatus() 246 | 247 | } 248 | 249 | } 250 | 251 | override fun onClick(v: View) { 252 | when (v.id) { 253 | R.id.buttonInfoPlaylist -> { 254 | showThisPlayList() 255 | } 256 | R.id.buttonInfoFave -> { 257 | } 258 | R.id.buttonInfoVol -> { 259 | } 260 | R.id.buttonInfoShuffle -> { 261 | mPlayerAdapter?.shufflePlayList() 262 | } 263 | R.id.buttonInfoPrev -> skipPrev() 264 | R.id.buttonInfoPlay -> resumeOrPause() 265 | R.id.buttonInfoNext -> skipNext() 266 | R.id.buttonInfoAll -> { 267 | } 268 | } 269 | } 270 | 271 | private fun showThisPlayList() { 272 | val song = mPlayerAdapter?.getCurrentSong()!! 273 | val position = currentPlayList.indexOf(song) 274 | if (!isPlayListShown) { 275 | recyclerViewCurrent?.visibility = View.VISIBLE 276 | constraintLayout2?.visibility = View.GONE 277 | recyclerViewCurrent.scrollToPosition(position) 278 | isPlayListShown = true 279 | playerActivity?.togglePlaylist(true) 280 | } 281 | } 282 | 283 | fun togglePlayList() { 284 | if (isPlayListShown) { 285 | recyclerViewCurrent?.visibility = View.GONE 286 | constraintLayout2?.visibility = View.VISIBLE 287 | isPlayListShown = false 288 | playerActivity?.togglePlaylist(false) 289 | } 290 | } 291 | 292 | private fun setClickListeners() { 293 | buttonInfoAll.setOnClickListener(this) 294 | buttonInfoFave.setOnClickListener(this) 295 | buttonInfoNext.setOnClickListener(this) 296 | buttonInfoPlay.setOnClickListener(this) 297 | buttonInfoPlaylist.setOnClickListener(this) 298 | buttonInfoPrev.setOnClickListener(this) 299 | buttonInfoShuffle.setOnClickListener(this) 300 | buttonInfoVol.setOnClickListener(this) 301 | } 302 | 303 | internal inner class PlaybackListener : PlaybackInfoListener() { 304 | 305 | override fun onPositionChanged(position: Int) { 306 | if (!mUserIsSeeking) { 307 | seekBarInfo?.progress = position 308 | } 309 | } 310 | 311 | override fun onStateChanged(@State state: Int) { 312 | updatePlayingStatus() 313 | if (mPlayerAdapter?.getState() != PlaybackInfoListener.State.RESUMED && mPlayerAdapter?.getState() != PlaybackInfoListener.State.PAUSED) { 314 | updatePlayingInfo(restore = false, startPlay = true) 315 | } 316 | if (state == PlaybackInfoListener.State.PLAYING) { 317 | updatePlayingInfo(restore = true, startPlay = false) 318 | 319 | } 320 | } 321 | 322 | override fun onPlaybackCompleted() { 323 | //updateResetStatus(true); 324 | } 325 | } 326 | 327 | private fun checkIsPlayer(): Boolean { 328 | 329 | val isPlayer = mPlayerAdapter?.isMediaPlayer() 330 | if (!isPlayer!!) { 331 | EqualizerUtils.notifyNoSessionId(activity!!) 332 | } 333 | return isPlayer 334 | } 335 | 336 | private fun skipPrev() { 337 | if (checkIsPlayer()) { 338 | mPlayerAdapter?.instantReset() 339 | } 340 | } 341 | 342 | private fun resumeOrPause() { 343 | if (checkIsPlayer()) { 344 | mPlayerAdapter?.resumeOrPause() 345 | } 346 | } 347 | 348 | private fun skipNext() { 349 | if (checkIsPlayer()) { 350 | mPlayerAdapter!!.skip(true) 351 | } 352 | } 353 | 354 | fun openEqualizer() { 355 | if (EqualizerUtils.hasEqualizer(activity!!)) { 356 | if (checkIsPlayer()) { 357 | mPlayerAdapter?.openEqualizer(activity!!) 358 | } 359 | } else { 360 | Toast.makeText(activity, "No equalizer found", Toast.LENGTH_SHORT).show() 361 | } 362 | } 363 | 364 | 365 | } 366 | --------------------------------------------------------------------------------