Abstracted the MediaPlayer from the MainActivity and turned it into a service that has the ability to run regardless if an acitivity is present #28. Resolved issue where track display details was blank after switching from the settings view #26
This commit is contained in:
committed by
amazing-username
parent
971da4b9d2
commit
040544e630
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -15,15 +15,12 @@
|
|||||||
android:name=".activities.SettingsActivity"
|
android:name=".activities.SettingsActivity"
|
||||||
android:label="@string/title_activity_settings"
|
android:label="@string/title_activity_settings"
|
||||||
android:parentActivityName=".activities.MainActivity"
|
android:parentActivityName=".activities.MainActivity"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="com.example.mear.activities.MainActivity" />
|
android:value="com.example.mear.activities.MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".activities.fdh"
|
|
||||||
android:label="@string/title_activity_settings"
|
|
||||||
android:screenOrientation="portrait"></activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
@@ -35,6 +32,8 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service android:name=".playback.service.MusicService" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,50 +1,91 @@
|
|||||||
package com.example.mear.activities
|
package com.example.mear.activities
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.app.ActivityManager
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.media.MediaPlayer
|
import android.graphics.Color
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.os.IBinder
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.lang.Runnable
|
import java.lang.Runnable
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.io.*
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.fragment_play_controls.*
|
import kotlinx.android.synthetic.main.fragment_play_controls.*
|
||||||
import kotlinx.android.synthetic.main.fragment_track_cover.*
|
import kotlinx.android.synthetic.main.fragment_track_cover.*
|
||||||
import kotlinx.android.synthetic.main.fragment_track_details.*
|
import kotlinx.android.synthetic.main.fragment_track_details.*
|
||||||
import kotlinx.android.synthetic.main.fragment_track_elapsing.*
|
import kotlinx.android.synthetic.main.fragment_track_elapsing.*
|
||||||
import kotlinx.android.synthetic.main.fragment_track_flow.*
|
import kotlinx.android.synthetic.main.fragment_track_flow.*
|
||||||
import kotlin.io.*
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
|
import org.jetbrains.anko.image
|
||||||
|
import org.jetbrains.anko.imageBitmap
|
||||||
|
|
||||||
|
import com.example.mear.playback.service.MusicService
|
||||||
import com.example.mear.R
|
import com.example.mear.R
|
||||||
import com.example.mear.repositories.TrackRepository
|
import com.example.mear.models.PlayControls
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private var musicService: MusicService? = null
|
||||||
|
private var serviceBinded: Boolean? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
try {
|
||||||
|
if (serviceBinded == null || serviceBinded!! == false) {
|
||||||
|
unbindService(mConnection)
|
||||||
|
serviceBinded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
try {
|
||||||
|
if (serviceBinded!!.equals(null) || serviceBinded!! == false ) {
|
||||||
|
unbindService(mConnection)
|
||||||
|
serviceBinded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
songCount = TrackRepository(this).getSongCount()
|
|
||||||
currentSong = fetchSongIndex(PlayTypes.PlaySong)
|
|
||||||
TrackElapsing.progress = 0
|
TrackElapsing.progress = 0
|
||||||
TrackElapsing.max = 100
|
TrackElapsing.max = 100
|
||||||
initializeMediaPlayer()
|
|
||||||
|
|
||||||
initializeClickListeners()
|
try {
|
||||||
initializeCompletionListener()
|
initializeServices()
|
||||||
|
initializeClickListeners()
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private fun initializeClickListeners() {
|
private fun initializeClickListeners() {
|
||||||
PlayTrack.setOnClickListener {
|
PlayTrack.setOnClickListener {
|
||||||
@@ -62,8 +103,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
SettingsLink.setOnClickListener {
|
SettingsLink.setOnClickListener {
|
||||||
val intent = Intent(this, SettingsActivity::class.java)
|
val intent = Intent(this, SettingsActivity::class.java)
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
@@ -72,19 +111,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun initializeCompletionListener() {
|
private fun initializeServices() {
|
||||||
trackPlayer!!.setOnCompletionListener {
|
doBindService()
|
||||||
playNextSongTrack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun initializeMediaPlayer() {
|
|
||||||
if (trackPlayer == null) {
|
|
||||||
trackPlayer = MediaPlayer()
|
|
||||||
playerInitialized = true
|
|
||||||
val tr = TrackRepository(this).getTrack(currentSong!!)
|
|
||||||
trackPlayer!!.setDataSource(tr.songPath)
|
|
||||||
trackPlayer!!.prepare()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleShuffle() {
|
private fun toggleShuffle() {
|
||||||
@@ -108,12 +136,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun playSongTrack() {
|
private fun playSongTrack() {
|
||||||
PlayTrack.isEnabled = true
|
PlayTrack.isEnabled = false
|
||||||
try {
|
try {
|
||||||
if (!trackPlayer!!.isPlaying) {
|
if (!musicService!!.isPlaying()) {
|
||||||
trackPlayer!!.start()
|
musicService!!.playSongTrack()
|
||||||
} else {
|
} else {
|
||||||
trackPlayer!!.pause()
|
musicService!!.pauseSongTrack()
|
||||||
}
|
}
|
||||||
configureTrackDisplay()
|
configureTrackDisplay()
|
||||||
}
|
}
|
||||||
@@ -125,16 +153,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private fun playNextSongTrack() {
|
private fun playNextSongTrack() {
|
||||||
NextTrack.isEnabled = false
|
NextTrack.isEnabled = false
|
||||||
try {
|
try {
|
||||||
currentSong = fetchSongIndex(PlayTypes.PlayNextSong)
|
val controls = PlayControls(shuffleOn!!)
|
||||||
|
musicService!!.playNextTrack(controls)
|
||||||
|
|
||||||
if (trackPlayer!!.isPlaying) {
|
|
||||||
trackPlayer!!.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
trackPlayer!!.reset()
|
|
||||||
trackPlayer!!.setDataSource(TrackRepository(this).getTrack(currentSong!!).songPath)
|
|
||||||
trackPlayer!!.prepare()
|
|
||||||
trackPlayer!!.start()
|
|
||||||
configureTrackDisplay()
|
configureTrackDisplay()
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
@@ -146,16 +167,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private fun playPreviousSongTrack() {
|
private fun playPreviousSongTrack() {
|
||||||
PreviousTrack.isEnabled = false
|
PreviousTrack.isEnabled = false
|
||||||
try {
|
try {
|
||||||
currentSong = fetchSongIndex(PlayTypes.PlayPreviousSong)
|
val controls = PlayControls(shuffleOn!!)
|
||||||
|
musicService!!.playPreviousTrack(controls)
|
||||||
|
|
||||||
if (trackPlayer!!.isPlaying) {
|
|
||||||
trackPlayer!!.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
trackPlayer!!.reset()
|
|
||||||
trackPlayer!!.setDataSource(TrackRepository(this).getTrack(currentSong!!).songPath)
|
|
||||||
trackPlayer!!.prepare()
|
|
||||||
trackPlayer!!.start()
|
|
||||||
configureTrackDisplay()
|
configureTrackDisplay()
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
@@ -166,54 +180,60 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
private fun configureTrackDisplay() {
|
private fun configureTrackDisplay() {
|
||||||
try {
|
try {
|
||||||
configurePlayControlsDisplay()
|
runOnUiThread {
|
||||||
val currTrack = TrackRepository(this).getTrack(currentSong!!)
|
configurePlayControlsDisplay()
|
||||||
val trackTitle = currTrack.title
|
val currTrack = musicService!!.getCurrentTrack()
|
||||||
val artistTitle = currTrack.artist
|
val trackTitle = currTrack.title
|
||||||
val albumTitle = currTrack.album
|
val artistTitle = currTrack.artist
|
||||||
val trackDuration = currTrack.length
|
val albumTitle = currTrack.album
|
||||||
var trackCover: ByteArray? = null
|
val trackDuration = currTrack.length
|
||||||
val dur = String.format(
|
var trackCover: ByteArray? = null
|
||||||
"%02d:%02d", TimeUnit.SECONDS.toMinutes(trackDuration.toLong()),
|
val dur = String.format(
|
||||||
(trackDuration % 60)
|
"%02d:%02d", TimeUnit.SECONDS.toMinutes(trackDuration.toLong()),
|
||||||
)
|
(trackDuration % 60)
|
||||||
|
)
|
||||||
|
|
||||||
val mmr = MediaMetadataRetriever()
|
val mmr = MediaMetadataRetriever()
|
||||||
mmr.setDataSource(currTrack.songPath)
|
mmr.setDataSource(currTrack.songPath)
|
||||||
|
|
||||||
if (mmr.embeddedPicture != null) {
|
if (mmr.embeddedPicture != null) {
|
||||||
trackCover = mmr.embeddedPicture
|
trackCover = mmr.embeddedPicture
|
||||||
}
|
}
|
||||||
updateTrackProgress()
|
updateTrackProgress()
|
||||||
|
|
||||||
TrackTitle.text = null
|
resetControls()
|
||||||
ArtistTitle.text = null
|
|
||||||
AlbumTitle.text = null
|
|
||||||
CurrentPosition.text = null
|
|
||||||
TrackCover.setImageBitmap(null)
|
|
||||||
|
|
||||||
TrackTitle.text = trackTitle
|
|
||||||
ArtistTitle.text = artistTitle
|
TrackTitle.text = trackTitle
|
||||||
AlbumTitle.text = albumTitle
|
ArtistTitle.text = artistTitle
|
||||||
TrackDuration.text = dur
|
AlbumTitle.text = albumTitle
|
||||||
if (trackCover != null) {
|
TrackDuration.text = dur
|
||||||
val songImage = BitmapFactory
|
if (trackCover != null) {
|
||||||
.decodeByteArray(trackCover, 0, trackCover.size)
|
val songImage = BitmapFactory
|
||||||
TrackCover.setImageBitmap(songImage)
|
.decodeByteArray(trackCover, 0, trackCover.size)
|
||||||
|
TrackCover.imageBitmap = songImage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
val msg = ex.message
|
val msg = ex.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private fun resetControls() {
|
||||||
|
TrackTitle!!.text = null
|
||||||
|
ArtistTitle!!.text = null
|
||||||
|
AlbumTitle!!.text = null
|
||||||
|
CurrentPosition!!.text = null
|
||||||
|
TrackCover!!.imageBitmap = null
|
||||||
|
}
|
||||||
private fun updateTrackProgress() {
|
private fun updateTrackProgress() {
|
||||||
musicHandler!!.postDelayed(musicTrackTimeUpdateTask, 100)
|
musicHandler!!.postDelayed(musicTrackTimeUpdateTask, 250)
|
||||||
}
|
}
|
||||||
private fun configurePlayControlsDisplay() {
|
private fun configurePlayControlsDisplay() {
|
||||||
PlayTrack.background = null
|
PlayTrack.background = null
|
||||||
PlayTrack.colorFilter = null
|
PlayTrack.colorFilter = null
|
||||||
|
|
||||||
if (!trackPlayer!!.isPlaying) {
|
if (!musicService!!.isPlaying()) {
|
||||||
PlayTrack.setImageResource(android.R.drawable.ic_media_pause)
|
PlayTrack.setImageResource(android.R.drawable.ic_media_pause)
|
||||||
PlayTrack.setColorFilter(Color.RED)
|
PlayTrack.setColorFilter(Color.RED)
|
||||||
}
|
}
|
||||||
@@ -223,51 +243,25 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchSongIndex(playType: PlayTypes): Int {
|
|
||||||
var songIndex: Int? = null
|
|
||||||
|
|
||||||
try {
|
private fun isServiceRunning(serviceClass: Class<*>): Boolean {
|
||||||
when (playType) {
|
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
PlayTypes.PlayPreviousSong -> {
|
|
||||||
songIndex = songCount
|
for (service in activityManager.getRunningServices(Integer.MAX_VALUE)) {
|
||||||
if (currentSong!! != 0) {
|
if (serviceClass.name == service.service.className) {
|
||||||
if (!shuffleOn!!) {
|
return true
|
||||||
songIndex = currentSong!!.dec()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
songIndex = Random.nextInt(0, songCount!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PlayTypes.PlaySong -> {
|
|
||||||
songIndex = Random.nextInt(0, songCount!!)
|
|
||||||
}
|
|
||||||
PlayTypes.PlayNextSong -> {
|
|
||||||
songIndex = 0
|
|
||||||
if (currentSong!! != songCount!!) {
|
|
||||||
if (!shuffleOn!!) {
|
|
||||||
songIndex = currentSong!!.inc()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
songIndex = Random.nextInt(0, songCount!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
return false
|
||||||
val exMsg = ex.message
|
|
||||||
println(exMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return songIndex!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var musicTrackTimeUpdateTask = object: Runnable {
|
private var musicTrackTimeUpdateTask = object: Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
|
|
||||||
|
try {
|
||||||
var newPosition = 0
|
var newPosition = 0
|
||||||
val currentPosition = trackPlayer!!.currentPosition / 1000
|
val currentPosition = musicService!!.currentPositionOfTrack() / 1000
|
||||||
val totalDuration = trackPlayer!!.duration / 1000
|
val totalDuration = musicService!!.durationOfTrack() / 1000
|
||||||
newPosition = (((currentPosition).toDouble() / totalDuration) * 100).toInt()
|
newPosition = (((currentPosition).toDouble() / totalDuration) * 100).toInt()
|
||||||
val dur = String.format(
|
val dur = String.format(
|
||||||
"%02d:%02d", TimeUnit.SECONDS.toMinutes(currentPosition.toLong()),
|
"%02d:%02d", TimeUnit.SECONDS.toMinutes(currentPosition.toLong()),
|
||||||
@@ -277,18 +271,69 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
TrackElapsing.progress = newPosition
|
TrackElapsing.progress = newPosition
|
||||||
|
|
||||||
musicHandler!!.postDelayed(this, 100)
|
if (TrackCover.image == null && musicService!!.isPlaying()) {
|
||||||
|
configureTrackDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
musicHandler!!.postDelayed(this, 250)
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var musicHandler: Handler? = Handler()
|
private val mConnection = object : ServiceConnection {
|
||||||
private var trackPlayer: MediaPlayer? = null
|
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||||
private var currentSong: Int? = null
|
resetControls()
|
||||||
private var playerInitialized: Boolean? = false
|
|
||||||
private var shuffleOn: Boolean? = false
|
|
||||||
private var songCount: Int? = null
|
|
||||||
|
|
||||||
private enum class PlayTypes {
|
runBlocking {
|
||||||
PlayNextSong, PlaySong, PlayPreviousSong
|
val demo = launch {
|
||||||
|
musicService = (service as MusicService.LocalBinder).service
|
||||||
|
}
|
||||||
|
demo.start()
|
||||||
|
}
|
||||||
|
if (musicService != null) {
|
||||||
|
if (musicService!!.isPlaying()) {
|
||||||
|
configureTrackDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
this@MainActivity, "Music Service Started",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(className: ComponentName) {
|
||||||
|
resetControls()
|
||||||
|
Toast.makeText(
|
||||||
|
this@MainActivity, "Music Service Stopped",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun doBindService() {
|
||||||
|
val intent = Intent(this, MusicService::class.java)
|
||||||
|
if (isServiceRunning(MusicService::class.java)) {
|
||||||
|
val suc = "Service is already running"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startService(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
|
||||||
|
if (result) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private var musicHandler: Handler? = Handler()
|
||||||
|
private var shuffleOn: Boolean? = false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
TrackRepository(this).delete()
|
TrackRepository(this).delete()
|
||||||
val trackMgr = TrackManager(allSongs)
|
val trackMgr = TrackManager(allSongs)
|
||||||
|
trackMgr.configureTracks(this)
|
||||||
}
|
}
|
||||||
catch (ex: Exception) {
|
catch (ex: Exception) {
|
||||||
val exMsg = ex.message
|
val exMsg = ex.message
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.example.mear.constants
|
||||||
|
|
||||||
|
object DirectoryIgnore {
|
||||||
|
const val Android_Root = "Android/"
|
||||||
|
const val Notifications = "Notifications/"
|
||||||
|
const val Ringtones = "Ringtones/"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.example.mear.constants
|
||||||
|
|
||||||
|
object FileTypes {
|
||||||
|
const val Mp3 = "mp3"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.example.mear.models
|
||||||
|
|
||||||
|
data class PlayControls(val shuffleOn: Boolean) {
|
||||||
|
}
|
||||||
@@ -1,45 +1,274 @@
|
|||||||
package com.example.mear.playback.service
|
package com.example.mear.playback.service
|
||||||
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.R
|
import android.os.Looper
|
||||||
import android.R.string.cancel
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
|
||||||
|
import java.lang.Exception
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
import com.example.mear.models.Track
|
import com.example.mear.management.DatabaseManager
|
||||||
import com.example.mear.management.MusicFiles
|
import com.example.mear.management.MusicFiles
|
||||||
import com.example.mear.management.TrackManager
|
import com.example.mear.management.TrackManager
|
||||||
|
import com.example.mear.models.PlayControls
|
||||||
|
import com.example.mear.models.Track
|
||||||
import com.example.mear.repositories.TrackRepository
|
import com.example.mear.repositories.TrackRepository
|
||||||
|
|
||||||
|
|
||||||
class MusicService: Service() {
|
class MusicService: Service() {
|
||||||
|
|
||||||
private var trackPlayer: MediaPlayer? = null
|
private var trackPlayer: MediaPlayer? = null
|
||||||
|
private var currentSongIndex: Int? = null
|
||||||
|
private var shuffleOn: Boolean? = false
|
||||||
private val mBinder = LocalBinder()
|
private val mBinder = LocalBinder()
|
||||||
|
private val seconds = 4
|
||||||
|
|
||||||
|
|
||||||
inner class LocalBinder : Binder() {
|
inner class LocalBinder : Binder() {
|
||||||
internal val service: MusicService
|
internal val service: MusicService
|
||||||
get() = this@MusicService
|
get() = this@MusicService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private enum class PlayTypes {
|
||||||
|
PlayNextSong, PlaySong, PlayPreviousSong
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
initializeMediaPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
return Service.START_NOT_STICKY
|
return Service.START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
|
||||||
|
val intent = Intent(this, mBinder.javaClass)
|
||||||
|
onUnbind(intent)
|
||||||
|
|
||||||
Toast.makeText(this, "Music Service Stopped", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Music Service Stopped", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
return mBinder
|
return mBinder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
|
return super.onUnbind(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun playSongTrack() {
|
||||||
|
try {
|
||||||
|
trackPlayer!!.start()
|
||||||
|
}
|
||||||
|
catch (ex: Exception ) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun pauseSongTrack() {
|
||||||
|
try {
|
||||||
|
trackPlayer!!.pause()
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun playPreviousTrack() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun playNextTrack() {
|
||||||
|
try {
|
||||||
|
trackPlayer!!.reset()
|
||||||
|
var nextTrack = fetchSongIndex(PlayTypes.PlayNextSong)
|
||||||
|
val track = TrackRepository(this).getTrack(nextTrack)
|
||||||
|
trackPlayer!!.setDataSource(track.songPath)
|
||||||
|
trackPlayer!!.prepare()
|
||||||
|
trackPlayer!!.start()
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun playPreviousTrack(controls: PlayControls) {
|
||||||
|
try {
|
||||||
|
configureControl(controls)
|
||||||
|
val duration = trackPlayer!!.currentPosition / 1000
|
||||||
|
if (duration > seconds) {
|
||||||
|
|
||||||
|
|
||||||
|
trackPlayer!!.reset()
|
||||||
|
trackPlayer!!.setDataSource(TrackRepository(this).getTrack(currentSongIndex!!).songPath)
|
||||||
|
trackPlayer!!.prepare()
|
||||||
|
trackPlayer!!.start()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
|
||||||
|
var previousTrack = fetchSongIndex(PlayTypes.PlayPreviousSong)
|
||||||
|
val track = TrackRepository(this).getTrack(previousTrack)
|
||||||
|
trackPlayer!!.reset()
|
||||||
|
trackPlayer!!.setDataSource(track.songPath)
|
||||||
|
trackPlayer!!.prepare()
|
||||||
|
trackPlayer!!.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun configureControl(controls: PlayControls) {
|
||||||
|
shuffleOn = controls.shuffleOn
|
||||||
|
}
|
||||||
|
fun playNextTrack(controls: PlayControls) {
|
||||||
|
try {
|
||||||
|
configureControl(controls)
|
||||||
|
val nextTrack = fetchSongIndex(PlayTypes.PlayNextSong)
|
||||||
|
|
||||||
|
val track = TrackRepository(this).getTrack(nextTrack)
|
||||||
|
trackPlayer!!.reset()
|
||||||
|
trackPlayer!!.setDataSource(track.songPath)
|
||||||
|
trackPlayer!!.prepare()
|
||||||
|
trackPlayer!!.start()
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrentTrack(): Track {
|
||||||
|
var track: Track? = null
|
||||||
|
|
||||||
|
track = TrackRepository(this).getTrack(currentSongIndex!!)
|
||||||
|
|
||||||
|
return track!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun fetchSongIndex(playType: PlayTypes): Int {
|
||||||
|
var songIndex: Int? = currentSongIndex
|
||||||
|
val songCount = retrieveSongCount()
|
||||||
|
|
||||||
|
try {
|
||||||
|
when (playType) {
|
||||||
|
PlayTypes.PlayPreviousSong -> {
|
||||||
|
if (currentSongIndex!! != 0) {
|
||||||
|
if (!shuffleOn!!) {
|
||||||
|
currentSongIndex = currentSongIndex!!.dec()
|
||||||
|
songIndex = currentSongIndex!!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentSongIndex = Random.nextInt(0, songCount!!)
|
||||||
|
songIndex = currentSongIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlayTypes.PlaySong -> {
|
||||||
|
if (shuffleOn!!) {
|
||||||
|
songIndex = Random.nextInt(0, songCount!!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
songIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlayTypes.PlayNextSong -> {
|
||||||
|
if (currentSongIndex!! != songCount!!) {
|
||||||
|
if (!shuffleOn!!) {
|
||||||
|
currentSongIndex = currentSongIndex!!.inc()
|
||||||
|
songIndex = currentSongIndex!!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentSongIndex = Random.nextInt(0, songCount!!)
|
||||||
|
songIndex = currentSongIndex!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
println(exMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return songIndex!!
|
||||||
|
}
|
||||||
|
fun currentPositionOfTrack(): Int {
|
||||||
|
return trackPlayer!!.currentPosition
|
||||||
|
}
|
||||||
|
fun durationOfTrack(): Int {
|
||||||
|
return trackPlayer!!.duration
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPlaying(): Boolean {
|
||||||
|
return trackPlayer!!.isPlaying
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun initializeMediaPlayer() {
|
||||||
|
if (trackPlayer == null) {
|
||||||
|
trackPlayer = MediaPlayer()
|
||||||
|
}
|
||||||
|
if (trackRepositoryEmpty()!!) {
|
||||||
|
populateTrackRepository()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSongIndex = Random.nextInt(0, TrackRepository(this).getSongCount())
|
||||||
|
val trackToPlay = TrackRepository(this).getTrack(currentSongIndex!!)
|
||||||
|
trackPlayer!!.setDataSource(trackToPlay.songPath)
|
||||||
|
trackPlayer!!.prepareAsync()
|
||||||
|
trackPlayer!!.setOnCompletionListener {
|
||||||
|
playNextTrack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun populateTrackRepository() {
|
||||||
|
var mp3Paths = MusicFiles(android.os.Environment.getExternalStorageDirectory())
|
||||||
|
mp3Paths.searchForMp3Songs()
|
||||||
|
val paths = mp3Paths.allSongs
|
||||||
|
val trackMgr = TrackManager(paths!!)
|
||||||
|
trackMgr.configureTracks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun trackRepositoryEmpty(): Boolean? {
|
||||||
|
val trackCount = retrieveSongCount()
|
||||||
|
|
||||||
|
when (trackCount) {
|
||||||
|
null -> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
0 -> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun retrieveSongCount(): Int? {
|
||||||
|
|
||||||
|
try {
|
||||||
|
var db = DatabaseManager(this)
|
||||||
|
var trackDb = TrackRepository(this)
|
||||||
|
val count = trackDb.getSongCount()
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
catch (ex: Exception) {
|
||||||
|
val exMsg = ex.message
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,15 @@ import org.jetbrains.anko.db.*
|
|||||||
import com.example.mear.database
|
import com.example.mear.database
|
||||||
import com.example.mear.models.Track
|
import com.example.mear.models.Track
|
||||||
|
|
||||||
class TrackRepository(val context: Context) {
|
class TrackRepository {
|
||||||
|
private var context: Context? = null
|
||||||
|
|
||||||
fun getAll(): List<Track> = context.database.use {
|
constructor(context: Context)
|
||||||
|
{
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAll(): List<Track> = context!!.database.use {
|
||||||
val tracks = mutableListOf<Track>()
|
val tracks = mutableListOf<Track>()
|
||||||
|
|
||||||
select("Track" )
|
select("Track" )
|
||||||
@@ -31,7 +37,7 @@ class TrackRepository(val context: Context) {
|
|||||||
})
|
})
|
||||||
tracks
|
tracks
|
||||||
}
|
}
|
||||||
fun getTrack(id: Int): Track = context.database.use {
|
fun getTrack(id: Int): Track = context!!.database.use {
|
||||||
select("Track").where("Id = $id")
|
select("Track").where("Id = $id")
|
||||||
.parseSingle(object: MapRowParser<Track>{
|
.parseSingle(object: MapRowParser<Track>{
|
||||||
override fun parseRow(columns: Map<String, Any?>): Track {
|
override fun parseRow(columns: Map<String, Any?>): Track {
|
||||||
@@ -49,7 +55,7 @@ class TrackRepository(val context: Context) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fun getSongCount(): Int = context.database.use {
|
fun getSongCount(): Int = context!!.database.use {
|
||||||
select("TrackCount").limit(1)
|
select("TrackCount").limit(1)
|
||||||
.parseSingle(object : MapRowParser<Int>{
|
.parseSingle(object : MapRowParser<Int>{
|
||||||
override fun parseRow(columns: Map<String, Any?>): Int {
|
override fun parseRow(columns: Map<String, Any?>): Int {
|
||||||
@@ -59,7 +65,7 @@ class TrackRepository(val context: Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun insertTrack(track: Track) = context.database.use {
|
fun insertTrack(track: Track) = context!!.database.use {
|
||||||
insert("Track",
|
insert("Track",
|
||||||
"Id" to track.id,
|
"Id" to track.id,
|
||||||
"Title" to track.title,
|
"Title" to track.title,
|
||||||
@@ -68,16 +74,25 @@ class TrackRepository(val context: Context) {
|
|||||||
"Duration" to track.length,
|
"Duration" to track.length,
|
||||||
"FilePath" to track.songPath)
|
"FilePath" to track.songPath)
|
||||||
}
|
}
|
||||||
fun createSongCount(songCount: Int) = context.database.use {
|
fun createSongCount(songCount: Int) = context!!.database.use {
|
||||||
|
delete("TrackCount")
|
||||||
insert("TrackCount",
|
insert("TrackCount",
|
||||||
"Id" to 0,
|
"Id" to 0,
|
||||||
"TotalSongs" to songCount)
|
"TotalSongs" to songCount)
|
||||||
}
|
}
|
||||||
|
fun delete() = context!!.database.use {
|
||||||
fun delete(table: Any?) = context.database.use {
|
|
||||||
delete("Track", whereClause = "id = {$table.id}")
|
|
||||||
}
|
|
||||||
fun delete() = context.database.use {
|
|
||||||
delete("Track")
|
delete("Track")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLibraryCount(): Int? {
|
||||||
|
context!!.database.use {
|
||||||
|
select("TrackCount").limit(1)
|
||||||
|
.parseList (object : MapRowParser<Int> {
|
||||||
|
override fun parseRow(columns: Map<String, Any?>): Int {
|
||||||
|
return columns.getValue("TotalSongs").toString().toInt()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,4 +5,4 @@ import android.content.Context
|
|||||||
import com.example.mear.management.DatabaseManager
|
import com.example.mear.management.DatabaseManager
|
||||||
|
|
||||||
val Context.database: DatabaseManager
|
val Context.database: DatabaseManager
|
||||||
get() = DatabaseManager.getInstance(applicationContext)
|
get() = DatabaseManager.getInstance(applicationContext)
|
||||||
Reference in New Issue
Block a user