Skip to content

Commit cccaebc

Browse files
committed
*
1 parent 2fac5a0 commit cccaebc

21 files changed

+344
-257
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ calculating foot frequency every 10 seconds or 30 steps.
2525
- ObjectBox
2626

2727
## Todo
28-
- [ ] fetching album arts from Last.fm
29-
- [ ] recording playing behaivours to recommend music
28+
-[x] fetch album arts from Last.fm
29+
-[ ] record playing behaviors to recommend music
30+
-[ ] display player state and message
31+

app/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ apply plugin: 'kotlin-kapt'
66

77
android {
88
compileSdkVersion 26
9-
buildToolsVersion "26.0.0"
9+
buildToolsVersion '26.0.2'
1010
defaultConfig {
1111
applicationId "com.junnanhao.next"
1212
minSdkVersion 19

app/objectbox-models/default.json

+40-2
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,48 @@
9898
}
9999
],
100100
"relations": []
101+
},
102+
{
103+
"id": "3:8836922074187047255",
104+
"lastPropertyId": "1:3039684814473408330",
105+
"name": "Cluster",
106+
"properties": [
107+
{
108+
"id": "1:3039684814473408330",
109+
"name": "id"
110+
}
111+
],
112+
"relations": []
113+
},
114+
{
115+
"id": "4:6956555321611202334",
116+
"lastPropertyId": "4:686932639959323499",
117+
"name": "ClusterMembership",
118+
"properties": [
119+
{
120+
"id": "1:8135685907885604481",
121+
"name": "id"
122+
},
123+
{
124+
"id": "2:1236474682744980074",
125+
"name": "membership"
126+
},
127+
{
128+
"id": "3:4475694740025433391",
129+
"indexId": "1:6836789378517783015",
130+
"name": "clusterId"
131+
},
132+
{
133+
"id": "4:686932639959323499",
134+
"indexId": "2:2052746911721191889",
135+
"name": "songId"
136+
}
137+
],
138+
"relations": []
101139
}
102140
],
103-
"lastEntityId": "2:5293221884736564273",
104-
"lastIndexId": "0:0",
141+
"lastEntityId": "4:6956555321611202334",
142+
"lastIndexId": "2:2052746911721191889",
105143
"lastRelationId": "0:0",
106144
"lastSequenceId": "0:0",
107145
"modelVersion": 3,

app/proguard-rules.pro

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@
3131
-keepattributes Exceptions
3232

3333
# Retrofit use okio
34-
-dontwarn okio.**
34+
-dontwarn okio.**

app/release/output.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.junnanhao.next","split":"","minSdkVersion":"19"}}]

app/src/main/java/com/junnanhao/next/MusicService.kt

+25-47
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import android.os.Message
88
import android.os.RemoteException
99
import android.support.v4.media.MediaBrowserCompat.MediaItem
1010
import android.support.v4.media.MediaBrowserServiceCompat
11-
import android.support.v4.media.MediaMetadataCompat
1211
import android.support.v4.media.session.MediaButtonReceiver
1312
import android.support.v4.media.session.MediaSessionCompat
1413
import android.support.v4.media.session.PlaybackStateCompat
@@ -31,41 +30,27 @@ class MusicService : MediaBrowserServiceCompat(),
3130
private var mMediaSession: MediaSessionCompat? = null
3231
private var mStateBuilder: PlaybackStateCompat.Builder? = null
3332
private lateinit var musicProvider: MusicProvider
34-
private lateinit var queueManager: QueueManager
3533
private lateinit var playbackManager: PlaybackManager
3634
private lateinit var playback: Playback
3735
private var mMediaNotificationManager: MediaNotificationManager? = null
3836
private val mDelayedStopHandler = DelayedStopHandler(this)
37+
private lateinit var mChainManager: ChainManager
3938

4039
override fun onCreate() {
4140
super.onCreate()
41+
try {
42+
mMediaNotificationManager = MediaNotificationManager(this)
43+
} catch (e: RemoteException) {
44+
throw IllegalStateException("Could not create a MediaNotificationManager", e)
45+
}
4246
musicProvider = MusicProvider(ObjectBoxMusicSource(application))
43-
musicProvider.retrieveMusic()
44-
45-
queueManager = QueueManager(musicProvider, object : QueueManager.MetadataUpdateListener {
46-
override fun onMetadataChanged(metadata: MediaMetadataCompat) {
47-
mMediaSession?.setMetadata(metadata)
48-
}
49-
50-
override fun onMetadataRetrieveError() {
51-
// playbackManager.updatePlaybackState(
52-
// getString(R.string.error_no_metadata))
53-
}
54-
55-
override fun onCurrentQueueIndexUpdated(queueIndex: Int) {
56-
playbackManager.handlePlayRequest()
57-
}
58-
59-
override fun onQueueUpdated(title: String,
60-
newQueue: List<MediaSessionCompat.QueueItem>) {
61-
mMediaSession?.setQueue(newQueue)
62-
}
63-
})
64-
47+
mChainManager = ChainManagerImpl(musicProvider, application as App)
6548
playback = LocalPlayback(applicationContext, musicProvider)
66-
playbackManager = PlaybackManager(LogManager(application), this, resources,
67-
musicProvider, queueManager, playback)
49+
playbackManager = PlaybackManager(playback, mChainManager, this)
50+
initMediaSession()
51+
}
6852

53+
private fun initMediaSession() {
6954
// Create a MediaSessionCompat
7055
mMediaSession = MediaSessionCompat(applicationContext, LOG_TAG)
7156

@@ -85,15 +70,9 @@ class MusicService : MediaBrowserServiceCompat(),
8570

8671
// MySessionCallback() has methods that handle callbacks from a media controller
8772
mMediaSession!!.setCallback(playbackManager.mediaSessionCallback)
88-
89-
try {
90-
mMediaNotificationManager = MediaNotificationManager(this)
91-
} catch (e: RemoteException) {
92-
throw IllegalStateException("Could not create a MediaNotificationManager", e)
93-
}
94-
9573
}
9674

75+
9776
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?)
9877
: MediaBrowserServiceCompat.BrowserRoot? {
9978
// (Optional) Control the level of access for the specified package name.
@@ -110,6 +89,7 @@ class MusicService : MediaBrowserServiceCompat(),
11089
}
11190

11291
private fun allowBrowsing(clientPackageName: String, clientUid: Int): Boolean {
92+
wtf { "client package name: $clientPackageName, client Uid: $clientUid" }
11393
return true
11494
}
11595

@@ -143,17 +123,6 @@ class MusicService : MediaBrowserServiceCompat(),
143123
}
144124

145125

146-
override fun onPlaybackStart() {
147-
148-
mMediaSession?.isActive = true
149-
mDelayedStopHandler.removeCallbacksAndMessages(null)
150-
151-
// The service needs to continue running even after the bound client (usually a
152-
// MediaController) disconnects, otherwise the music playback will stop.
153-
// Calling startService(Intent) will keep the service running until it is explicitly killed.
154-
startService(Intent(applicationContext, MusicService::class.java))
155-
}
156-
157126
override fun onDestroy() {
158127
super.onDestroy()
159128
wtf { "onDestroy" }
@@ -174,6 +143,16 @@ class MusicService : MediaBrowserServiceCompat(),
174143
.subscribe()
175144
}
176145

146+
override fun onPlaybackStart() {
147+
148+
mMediaSession?.isActive = true
149+
mDelayedStopHandler.removeCallbacksAndMessages(null)
150+
151+
// The service needs to continue running even after the bound client (usually a
152+
// MediaController) disconnects, otherwise the music playback will stop.
153+
// Calling startService(Intent) will keep the service running until it is explicitly killed.
154+
startService(Intent(applicationContext, MusicService::class.java))
155+
}
177156

178157
override fun onPlaybackStop() {
179158
mMediaSession?.isActive = false
@@ -216,7 +195,7 @@ class MusicService : MediaBrowserServiceCompat(),
216195
private val LOG_TAG = "NextMusicService"
217196
// The action of the incoming Intent indicating that it contains a command
218197
// to be executed (see {@link #onStartCommand})
219-
val ACTION_CMD = "com.example.android.uamp.ACTION_CMD"
198+
val ACTION_CMD = "com.junnanhao.next.ACTION_CMD"
220199
// The key in the extras of the incoming Intent indicating the command that
221200
// should be executed (see {@link #onStartCommand})
222201
val CMD_NAME = "CMD_NAME"
@@ -227,9 +206,8 @@ class MusicService : MediaBrowserServiceCompat(),
227206
// to local playback from cast playback.
228207
val CMD_STOP_CASTING = "CMD_STOP_CASTING"
229208

230-
231209
// Extra on MediaSession that contains the Cast device name currently connected to
232-
val EXTRA_CONNECTED_CAST = "com.example.android.uamp.CAST_NAME"
210+
val EXTRA_CONNECTED_CAST = "com.junnanhao.next.CAST_NAME"
233211

234212
// Delay stopSelf by using a handler.
235213
private val STOP_DELAY = 30000

app/src/main/java/com/junnanhao/next/data/MusicProvider.kt

+14-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.junnanhao.next.data
22

33
import android.graphics.Bitmap
44
import android.support.v4.media.MediaMetadataCompat
5+
import com.junnanhao.next.data.model.Cluster
56
import com.junnanhao.next.data.model.MutableMediaMetadata
67
import io.reactivex.Observable
78
import java.util.*
@@ -17,14 +18,21 @@ class MusicProvider(source: MusicProviderSource) {
1718

1819
private val _source: MusicProviderSource = source
1920

21+
fun getMusic(mediaId: Long?): MediaMetadataCompat? {
22+
return mMusicListById2[mediaId]
23+
}
24+
25+
2026
fun getMusic(mediaId: String?): MediaMetadataCompat? {
2127
return if (mMusicListById.containsKey(mediaId)) mMusicListById[mediaId]?.metadata else null
2228
}
2329

2430
private val mMusicListById: ConcurrentMap<String, MutableMediaMetadata>
31+
private val mMusicListById2: ConcurrentMap<Long, MediaMetadataCompat>
2532

2633
init {
2734
mMusicListById = ConcurrentHashMap<String, MutableMediaMetadata>()
35+
mMusicListById2 = ConcurrentHashMap<Long, MediaMetadataCompat>()
2836
}
2937

3038
/**
@@ -40,7 +48,7 @@ class MusicProvider(source: MusicProviderSource) {
4048
}
4149
}
4250

43-
fun retrieveMusic() {
51+
private fun retrieveMusic() {
4452
try {
4553
if (mCurrentState == State.NON_INITIALIZED) {
4654
mCurrentState = State.INITIALIZING
@@ -115,5 +123,10 @@ class MusicProvider(source: MusicProviderSource) {
115123
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
116124
}
117125

126+
fun getShuffledCluster(calendar: Calendar?, nothing: Nothing?): Cluster {
127+
128+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
129+
}
130+
118131

119132
}

app/src/main/java/com/junnanhao/next/data/MusicProviderSource.kt

+1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ interface MusicProviderSource {
88
val CUSTOM_METADATA_MBID: String = "__mbid__"
99
}
1010

11+
1112
operator fun iterator(): Iterator<MediaMetadataCompat>
1213
}

app/src/main/java/com/junnanhao/next/data/ObjectBoxMusicSource.kt

-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import retrofit2.converter.gson.GsonConverterFactory
2525
class ObjectBoxMusicSource(application: Application) : MusicProviderSource {
2626

2727
private val source: SongsDataSource = SongsRepository(application)
28-
29-
3028
private val retrofit: Retrofit
3129
private val service: LastFmService
3230
private val songBox: Box<Song> = (application as App).boxStore.boxFor(Song::class.java)
@@ -45,7 +43,6 @@ class ObjectBoxMusicSource(application: Application) : MusicProviderSource {
4543
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
4644
.build()
4745
service = retrofit.create<LastFmService>(LastFmService::class.java)
48-
4946
}
5047

5148

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.junnanhao.next.data.model
2+
3+
import io.objectbox.annotation.Backlink
4+
import io.objectbox.annotation.Entity
5+
import io.objectbox.annotation.Id
6+
import io.objectbox.relation.ToMany
7+
8+
/**
9+
* Created by jonashao on 2017/9/26.
10+
* a cluster of songs
11+
*/
12+
@Entity
13+
data class Cluster(
14+
@Id var id: Long,
15+
@Backlink val songs: ToMany<ClusterMembership>
16+
// val similar: ToMany<Map.Entry<Cluster, Double>>
17+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.junnanhao.next.data.model
2+
3+
import io.objectbox.annotation.Entity
4+
import io.objectbox.annotation.Id
5+
import io.objectbox.relation.ToOne
6+
7+
/**
8+
* Created by jonashao on 2017/9/26.
9+
* relationship between cluster and song, and their membership
10+
*/
11+
@Entity
12+
data class ClusterMembership(
13+
@Id private var id: Long,
14+
val cluster: ToOne<Cluster>,
15+
val song: ToOne<Song>,
16+
var membership: Double
17+
) {
18+
companion object {
19+
val MIN_MEMBERSHIP: Double = 0.001
20+
}
21+
}
22+
23+
//@Entity
24+
//data class ClusterFriendship(
25+
// @Id private var id: Long,
26+
// private val one: ToOne<Cluster>,
27+
// private var friendship: Double,
28+
// private val another: ToOne<Cluster>
29+
//)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.junnanhao.next.playback
2+
3+
import android.support.v4.media.MediaMetadataCompat
4+
import java.util.*
5+
6+
/**
7+
* Created by jonashao on 2017/9/26.
8+
* manage play history, and to decide next music
9+
*/
10+
11+
interface ChainManager {
12+
fun current(): MediaMetadataCompat?
13+
fun updateMetaData()
14+
fun next(isSkip:Boolean): Boolean
15+
fun guess(calendar: Calendar?, nothing: Nothing?)
16+
}

0 commit comments

Comments
 (0)