Skip to content

Commit f1f1b82

Browse files
committed
Draft: using ViewModel to store Playable state.
1 parent 9df1140 commit f1f1b82

File tree

22 files changed

+601
-240
lines changed

22 files changed

+601
-240
lines changed

buildSrc/src/main/java/kohii/Dependencies.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ object Libs {
155155
val liveData = "androidx.lifecycle:lifecycle-livedata-ktx:$version"
156156
val viewModel = "androidx.lifecycle:lifecycle-viewmodel-ktx:$version"
157157
val service = "androidx.lifecycle:lifecycle-service:$version"
158+
val process = "androidx.lifecycle:lifecycle-process:$version"
158159
}
159160

160161
object Room {

kohii-core/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ dependencies {
7474
implementation Libs.AndroidX.coordinatorLayout
7575
implementation Libs.AndroidX.Lifecycle.extensions
7676
implementation Libs.AndroidX.Lifecycle.service
77+
implementation Libs.AndroidX.Lifecycle.process
7778
api Libs.AndroidX.Lifecycle.java8
7879

7980
testImplementation(Libs.Common.junit)

kohii-core/src/main/java/kohii/v1/core/AbstractPlayable.kt

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import kohii.v1.core.MemoryMode.HIGH
2222
import kohii.v1.core.MemoryMode.INFINITE
2323
import kohii.v1.core.MemoryMode.LOW
2424
import kohii.v1.core.MemoryMode.NORMAL
25+
import kohii.v1.debugOnly
2526
import kohii.v1.internal.PlayerParametersChangeListener
2627
import kohii.v1.logInfo
2728
import kohii.v1.logWarn
@@ -41,7 +42,7 @@ abstract class AbstractPlayable<RENDERER : Any>(
4142
private var playRequested: Boolean = false
4243

4344
override fun toString(): String {
44-
return "Playable([t=$tag][b=$bridge][h=${super.hashCode()}])"
45+
return "Playable([t=$tag][b=$bridge][h=${hashCode()}])"
4546
}
4647

4748
// Ensure the preparation for the playback
@@ -89,6 +90,7 @@ abstract class AbstractPlayable<RENDERER : Any>(
8990
}
9091

9192
override fun isPlaying(): Boolean {
93+
"Playable#isPlaying $this".logInfo()
9294
return bridge.isPlaying()
9395
}
9496

@@ -102,12 +104,12 @@ abstract class AbstractPlayable<RENDERER : Any>(
102104
val newManager = field
103105
if (oldManager === newManager) return
104106
"Playable#manager $oldManager --> $newManager, $this".logInfo()
107+
oldManager?.removePlayable(this)
108+
newManager?.addPlayable(this)
109+
// Setting Manager to null.
105110
if (newManager == null) {
106111
master.trySavePlaybackInfo(this)
107-
master.tearDown(
108-
playable = this,
109-
clearState = if (oldManager is Manager) !oldManager.isChangingConfigurations() else true
110-
)
112+
master.tearDown(playable = this)
111113
} else if (oldManager === null) {
112114
master.tryRestorePlaybackInfo(this)
113115
}
@@ -120,15 +122,7 @@ abstract class AbstractPlayable<RENDERER : Any>(
120122
val newPlayback = field
121123
if (oldPlayback === newPlayback) return
122124
"Playable#playback $oldPlayback --> $newPlayback, $this".logInfo()
123-
if (oldPlayback != null) {
124-
bridge.removeErrorListener(oldPlayback)
125-
bridge.removeEventListener(oldPlayback)
126-
oldPlayback.removeCallback(this)
127-
if (oldPlayback.playable === this) oldPlayback.playable = null
128-
if (oldPlayback.playerParametersChangeListener === this) {
129-
oldPlayback.playerParametersChangeListener = null
130-
}
131-
}
125+
oldPlayback?.let(::detachFromPlayback)
132126

133127
this.manager = if (newPlayback != null) {
134128
newPlayback.manager
@@ -150,30 +144,50 @@ abstract class AbstractPlayable<RENDERER : Any>(
150144
}
151145
}
152146

153-
if (newPlayback != null) {
154-
newPlayback.playable = this
155-
newPlayback.playerParametersChangeListener = this
156-
newPlayback.addCallback(this)
157-
newPlayback.config.callbacks.forEach { callback -> newPlayback.addCallback(callback) }
158-
159-
bridge.addEventListener(newPlayback)
160-
bridge.addErrorListener(newPlayback)
161-
162-
if (newPlayback.tag != Master.NO_TAG) {
163-
if (newPlayback.config.controller != null) {
164-
master.plannedManualPlayables.add(newPlayback.tag)
165-
} else {
166-
master.plannedManualPlayables.remove(newPlayback.tag)
167-
}
168-
}
169-
}
147+
newPlayback?.let(::attachToPlayback)
170148

171149
master.notifyPlaybackChanged(this, oldPlayback, newPlayback)
172150
}
173151

174152
override val playerState: Int
175153
get() = bridge.playerState
176154

155+
private fun attachToPlayback(playback: Playback) {
156+
playback.playable = this
157+
playback.playerParametersChangeListener = this
158+
playback.addCallback(this)
159+
playback.config.callbacks.forEach { callback -> playback.addCallback(callback) }
160+
161+
bridge.addEventListener(playback)
162+
bridge.addErrorListener(playback)
163+
164+
if (playback.tag != Master.NO_TAG) {
165+
if (playback.config.controller != null) {
166+
master.plannedManualPlayables.add(playback.tag)
167+
} else {
168+
master.plannedManualPlayables.remove(playback.tag)
169+
}
170+
}
171+
}
172+
173+
private fun detachFromPlayback(playback: Playback) {
174+
bridge.removeErrorListener(playback)
175+
bridge.removeEventListener(playback)
176+
playback.removeCallback(this)
177+
debugOnly {
178+
check(playback.playable === this) {
179+
"""
180+
Old playback of this playable ($this) is
181+
bound to a different playable: ${playback.playable}
182+
""".trimIndent()
183+
}
184+
}
185+
if (playback.playable === this) playback.playable = null
186+
if (playback.playerParametersChangeListener === this) {
187+
playback.playerParametersChangeListener = null
188+
}
189+
}
190+
177191
// Playback.Callback
178192

179193
override fun onActive(playback: Playback) {

kohii-core/src/main/java/kohii/v1/core/Binder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Binder(
8686
if (cache.config != config /* equals */) {
8787
// Scenario: client bind a Video of same tag/media but different Renderer type or Config.
8888
cache.playback = null // will also set Manager to null
89-
engine.master.tearDown(cache, true)
89+
engine.master.tearDown(cache)
9090
cache = null
9191
}
9292
}

kohii-core/src/main/java/kohii/v1/core/Bridge.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ interface Bridge<RENDERER : Any> {
5656
*/
5757
fun prepare(loadSource: Boolean)
5858

59-
// Ensure resource is ready to play. PlaybackDispatcher will require this for manual playback.
59+
/**
60+
* Ensure the resources is ready to play.
61+
*
62+
* Note: PlaybackDispatcher will require this for manual playback.
63+
*/
6064
fun ready()
6165

6266
fun play()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2021 Nam Nguyen, nam@ene.im
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kohii.v1.core
18+
19+
import androidx.annotation.MainThread
20+
21+
object CompatHelper {
22+
23+
private val defaultConfig: CompatConfig = CompatConfig()
24+
private var customConfig: CompatConfig? = null
25+
26+
val compatConfig: CompatConfig get() = customConfig ?: defaultConfig
27+
28+
@MainThread
29+
fun setCompatConfig(compatConfig: CompatConfig) {
30+
check(customConfig == null || customConfig == compatConfig) {
31+
"Another CompatConfig is already set."
32+
}
33+
customConfig = compatConfig
34+
}
35+
}
36+
37+
data class CompatConfig(
38+
val useLegacyPlaybackInfoStore: Boolean = false
39+
)

kohii-core/src/main/java/kohii/v1/core/Engine.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ import androidx.fragment.app.FragmentActivity
2727
import androidx.lifecycle.Lifecycle.State
2828
import androidx.lifecycle.Lifecycle.State.STARTED
2929
import androidx.lifecycle.LifecycleOwner
30+
import androidx.lifecycle.ViewModelProvider
3031
import kohii.v1.core.Binder.Options
3132
import kohii.v1.core.MemoryMode.LOW
3233
import kohii.v1.core.Scope.BUCKET
3334
import kohii.v1.core.Scope.GROUP
3435
import kohii.v1.core.Scope.MANAGER
3536
import kohii.v1.core.Scope.PLAYBACK
37+
import kohii.v1.internal.ManagerViewModel
3638
import kohii.v1.media.Media
3739
import kohii.v1.media.MediaItem
3840
import kohii.v1.media.VolumeInfo
@@ -47,9 +49,7 @@ abstract class Engine<RENDERER : Any> constructor(
4749
playableCreator: PlayableCreator<RENDERER>
4850
) : this(Master[context], playableCreator)
4951

50-
internal fun inject(group: Group) {
51-
group.managers.forEach { prepare(it) }
52-
}
52+
internal fun inject(group: Group) = group.managers.forEach(::prepare)
5353

5454
abstract fun prepare(manager: Manager)
5555

@@ -103,6 +103,7 @@ abstract class Engine<RENDERER : Any> constructor(
103103
activity = activity,
104104
host = fragment,
105105
managerLifecycleOwner = lifecycleOwner,
106+
viewModel = ViewModelProvider(fragment).get(ManagerViewModel::class.java),
106107
memoryMode = memoryMode,
107108
activeLifecycleState = activeLifecycleState
108109
)
@@ -117,6 +118,7 @@ abstract class Engine<RENDERER : Any> constructor(
117118
activity = activity,
118119
host = activity,
119120
managerLifecycleOwner = activity,
121+
viewModel = ViewModelProvider(activity).get(ManagerViewModel::class.java),
120122
memoryMode = memoryMode,
121123
activeLifecycleState = activeLifecycleState
122124
)

kohii-core/src/main/java/kohii/v1/core/Group.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package kohii.v1.core
1818

1919
import android.graphics.Rect
2020
import android.os.Handler
21+
import android.os.Looper
2122
import android.os.Message
2223
import android.view.ViewGroup
2324
import androidx.collection.arraySetOf
@@ -51,20 +52,21 @@ class Group(
5152

5253
private var stickyManager: Manager? = null
5354
set(value) {
54-
val from = field
55+
val prevStickyManager = field
5556
field = value
56-
val to = field
57-
if (from === to) return
58-
if (to != null) { // a Manager is promoted
59-
to.sticky = true
60-
managers.push(to)
57+
val nextStickyManager = field
58+
if (prevStickyManager === nextStickyManager) return
59+
if (nextStickyManager != null) { // a Manager is promoted
60+
nextStickyManager.sticky = true
61+
managers.push(nextStickyManager)
6162
} else {
62-
require(from != null && from.sticky)
63-
if (managers.peek() === from) {
64-
from.sticky = false
63+
require(prevStickyManager != null && prevStickyManager.sticky)
64+
if (managers.peek() === prevStickyManager) {
65+
prevStickyManager.sticky = false
6566
managers.pop()
6667
}
6768
}
69+
onRefresh()
6870
}
6971

7072
internal var groupVolumeInfo: VolumeInfo = VolumeInfo.DEFAULT_ACTIVE
@@ -83,7 +85,7 @@ class Group(
8385
managers.forEach { it.lock = value }
8486
}
8587

86-
private val handler = Handler(this)
88+
private val handler = Handler(/* looper */ Looper.getMainLooper(), /* callback */ this)
8789
private val dispatcher = PlayableDispatcher(master)
8890

8991
private val playbacks: Collection<Playback>
@@ -157,8 +159,8 @@ class Group(
157159
toPlay.addAll(canPlay)
158160
toPause.addAll(canPause)
159161
} else {
160-
managers.forEach {
161-
val (canPlay, canPause) = it.splitPlaybacks()
162+
for (manager in managers) {
163+
val (canPlay, canPause) = manager.splitPlaybacks()
162164
toPlay.addAll(canPlay)
163165
toPause.addAll(canPause)
164166
}
@@ -190,6 +192,7 @@ class Group(
190192
if (it.host is OnSelectionListener) {
191193
it.host to (grouped[it] ?: emptyList())
192194
} else {
195+
@Suppress("USELESS_CAST")
193196
null as Pair<OnSelectionListener, List<Playback>>?
194197
}
195198
}
@@ -231,7 +234,6 @@ class Group(
231234
internal fun onManagerDestroyed(manager: Manager) {
232235
if (stickyManager === manager) stickyManager = null
233236
if (managers.remove(manager)) master.onGroupUpdated(this)
234-
if (managers.size == 0) master.onLastManagerDestroyed(this)
235237
}
236238

237239
// This operation should:

kohii-core/src/main/java/kohii/v1/core/Interfaces.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,4 @@ interface DefaultTrackSelectorHolder {
180180
val trackSelector: DefaultTrackSelector
181181
}
182182

183-
interface PlayableManager
184-
185183
interface PlayableContainer

0 commit comments

Comments
 (0)