Skip to content

Commit 31459a3

Browse files
author
Tregub Artem
committed
[feature] implement playlist showing below video
1 parent b6335a2 commit 31459a3

18 files changed

Lines changed: 189 additions & 56 deletions

app/src/main/java/com/rollncode/backtube/logic/Extension.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,11 @@ fun toLog(a: Any?, tag: String = "aLog"): Unit = whenDebug {
3636
inline val Activity.canDrawOverlays: Boolean
3737
get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this)
3838

39-
fun Intent.toPendingActivity(context: Context, requestCode: Int = 0xA): PendingIntent =
40-
PendingIntent.getActivity(context, requestCode, this, PendingIntent.FLAG_UPDATE_CURRENT)
41-
?: PendingIntent.getActivity(context, requestCode, this, 0)
42-
43-
fun Intent.toPendingBroadcast(context: Context, requestCode: Int = 0xB): PendingIntent =
39+
fun Intent.toPendingBroadcast(context: Context, requestCode: Int = 0xA): PendingIntent =
4440
PendingIntent.getBroadcast(context, requestCode, this, PendingIntent.FLAG_UPDATE_CURRENT)
4541
?: PendingIntent.getBroadcast(context, requestCode, this, 0)
4642

47-
val Context.actionBarHeight: Int
43+
inline val Context.actionBarHeight: Int
4844
@SuppressLint("PrivateResource")
4945
get() {
5046
val value = TypedValue()
@@ -53,7 +49,8 @@ val Context.actionBarHeight: Int
5349
else
5450
resources.getDimensionPixelSize(dimen.abc_action_bar_default_height_material)
5551
}
56-
val Context.statusBarHeight: Int
52+
53+
inline val Context.statusBarHeight: Int
5754
get() {
5855
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
5956
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0

app/src/main/java/com/rollncode/backtube/logic/PlayerController.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ class PlayerController(val listener: OnPlayerControllerListener) :
4646
play()
4747
}
4848

49-
fun play() {
49+
fun play(index: Int? = null) {
50+
if (index != null && query.setCurrent(index))
51+
loadVideo = true
52+
5053
if (query == IQueryController.EMPTY || player == null || !playerReady) {
5154
shouldPlay = true
5255

app/src/main/java/com/rollncode/backtube/logic/QueryController.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.rollncode.backtube.logic
22

33
import com.rollncode.backtube.api.TubePlaylist
44
import com.rollncode.backtube.api.TubeVideo
5+
import com.rollncode.utility.receiver.ReceiverBus
56

67
interface IQueryController {
78

@@ -18,8 +19,18 @@ interface IQueryController {
1819
val query: List<TubeVideo>
1920
var currentIndex: Int
2021

21-
fun current(): TubeVideo =
22-
query[currentIndex]
22+
fun current(): TubeVideo {
23+
ReceiverBus.notify(TubeState.LIST_HIGHLIGHT_ITEM, currentIndex)
24+
return query[currentIndex]
25+
}
26+
27+
fun setCurrent(index: Int): Boolean {
28+
val success = index in (0 until query.size)
29+
if (success)
30+
currentIndex = index
31+
32+
return success
33+
}
2334

2435
fun toNext(): Boolean {
2536
val success = currentIndex + 1 < query.size

app/src/main/java/com/rollncode/backtube/logic/TubeState.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ object TubeState {
1616
const val WINDOW_SHOW = R.id.code_show
1717
const val WINDOW_HIDE = R.id.code_hide
1818

19+
const val LIST_SHOW = R.id.code_list_show
20+
const val LIST_HIGHLIGHT_ITEM = R.id.code_list_highlight_item
21+
1922
const val SERVICE_START = R.id.code_service_start
2023
const val SERVICE_STOP = R.id.code_service_stop
2124
const val RELEASE = R.id.code_release

app/src/main/java/com/rollncode/backtube/logic/ViewController.kt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,26 @@ package com.rollncode.backtube.logic
33
import android.annotation.SuppressLint
44
import android.app.Application
55
import android.content.Context
6+
import android.graphics.Color
67
import android.graphics.PixelFormat
78
import android.os.Build.VERSION
89
import android.os.Build.VERSION_CODES
10+
import android.support.v4.content.ContextCompat
11+
import android.text.TextUtils.TruncateAt.END
12+
import android.util.TypedValue
913
import android.view.LayoutInflater
14+
import android.view.View
15+
import android.view.ViewGroup
1016
import android.view.ViewGroup.MarginLayoutParams
1117
import android.view.WindowManager
1218
import android.view.WindowManager.LayoutParams
19+
import android.widget.BaseAdapter
20+
import android.widget.ListView
21+
import android.widget.TextView
1322
import com.pierfrancescosoffritti.androidyoutubeplayer.player.YouTubePlayerView
1423
import com.rollncode.backtube.R
24+
import com.rollncode.backtube.api.TubeVideo
25+
import com.rollncode.utility.receiver.ReceiverBus
1526

1627
@SuppressLint("InflateParams")
1728
class ViewController(context: Application, playerController: PlayerController) {
@@ -51,6 +62,16 @@ class ViewController(context: Application, playerController: PlayerController) {
5162
createWindowLayoutParams(min)
5263
}
5364

65+
private val listView by lazy {
66+
view.findViewById<ListView>(R.id.list_view).apply {
67+
setOnItemClickListener { _, _, position, _ -> ReceiverBus.notify(TubeState.PLAY, position) }
68+
}
69+
}
70+
71+
private val adapter by lazy {
72+
VideosAdapter().apply { listView.adapter = this }
73+
}
74+
5475
fun show() {
5576
toLog("ViewController.show")
5677
TubeState.windowShowed = true
@@ -74,6 +95,15 @@ class ViewController(context: Application, playerController: PlayerController) {
7495
wm.updateViewLayout(view, hideParams)
7596
}
7697

98+
fun showList(videos: List<TubeVideo>) {
99+
adapter.data = videos
100+
}
101+
102+
fun highlightCurrent(videoPosition: Int) {
103+
adapter.currentPosition = videoPosition
104+
listView.smoothScrollToPosition(videoPosition)
105+
}
106+
77107
fun release() = attempt {
78108
toLog("ViewController.release")
79109
TubeState.windowShowed = false
@@ -92,4 +122,51 @@ class ViewController(context: Application, playerController: PlayerController) {
92122
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
93123
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or LayoutParams.FLAG_LAYOUT_IN_SCREEN,
94124
PixelFormat.TRANSLUCENT)
125+
}
126+
127+
private class VideosAdapter : BaseAdapter() {
128+
129+
var data: List<TubeVideo> = mutableListOf()
130+
set(value) {
131+
field = value
132+
notifyDataSetChanged()
133+
}
134+
135+
var currentPosition = -1
136+
set(value) {
137+
val notifyDataSetChanged = field != value
138+
field = value
139+
140+
if (notifyDataSetChanged)
141+
notifyDataSetChanged()
142+
}
143+
144+
private var grayColor: Int = Color.LTGRAY
145+
146+
override fun getView(position: Int, cV: View?, parent: ViewGroup): View {
147+
val view: TextView =
148+
if (cV == null)
149+
TextView(parent.context).apply {
150+
setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimensionPixelSize(R.dimen.text_title).toFloat())
151+
grayColor = ContextCompat.getColor(context, R.color.gray)
152+
153+
val padding = resources.getDimensionPixelSize(R.dimen.spacing)
154+
setPadding(padding, padding, padding, padding)
155+
compoundDrawablePadding = padding
156+
157+
ellipsize = END
158+
maxLines = 3
159+
}
160+
else
161+
cV as TextView
162+
163+
view.text = getItem(position).title
164+
view.setTextColor(if (currentPosition == position) Color.WHITE else grayColor)
165+
166+
return view
167+
}
168+
169+
override fun getCount() = data.size
170+
override fun getItem(position: Int) = data[position]
171+
override fun getItemId(position: Int) = position.toLong()
95172
}

app/src/main/java/com/rollncode/backtube/player/TubePlayer.kt

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import android.app.Service
66
import android.content.Context
77
import android.net.Uri
88
import com.rollncode.backtube.api.TubeApi
9+
import com.rollncode.backtube.api.TubeVideo
910
import com.rollncode.backtube.logic.NotificationController
1011
import com.rollncode.backtube.logic.PlayerController
1112
import com.rollncode.backtube.logic.TUBE_PLAYLIST
1213
import com.rollncode.backtube.logic.TUBE_VIDEO
1314
import com.rollncode.backtube.logic.TubeState
1415
import com.rollncode.backtube.logic.TubeUri
1516
import com.rollncode.backtube.logic.ViewController
17+
import com.rollncode.backtube.logic.toLog
1618
import com.rollncode.backtube.logic.whenDebug
1719
import com.rollncode.utility.ICoroutines
1820
import com.rollncode.utility.receiver.ObjectsReceiver
@@ -36,8 +38,11 @@ object TubePlayer : ObjectsReceiver, ICoroutines {
3638

3739
init {
3840
ReceiverBus.subscribe(this,
39-
TubeState.PLAY, TubeState.PAUSE, TubeState.SERVICE_START, TubeState.SERVICE_STOP, TubeState.RELEASE,
40-
TubeState.WINDOW_SHOW, TubeState.WINDOW_HIDE, TubeState.PREVIOUS, TubeState.NEXT)
41+
TubeState.PLAY, TubeState.PAUSE, TubeState.SERVICE_START,
42+
TubeState.SERVICE_STOP, TubeState.RELEASE,
43+
TubeState.WINDOW_SHOW, TubeState.WINDOW_HIDE,
44+
TubeState.LIST_SHOW, TubeState.LIST_HIGHLIGHT_ITEM,
45+
TubeState.PREVIOUS, TubeState.NEXT)
4146
}
4247

4348
fun attachContext(context: Context) {
@@ -64,13 +69,27 @@ object TubePlayer : ObjectsReceiver, ICoroutines {
6469
when (tubeUri.type) {
6570
TUBE_VIDEO -> execute {
6671
TubeApi.requestVideo(tubeUri.id,
67-
{ playerController?.play(it) },
68-
{ ReceiverBus.notify(TubeState.SERVICE_STOP) })
72+
{
73+
toLog("requestVideo: $it")
74+
75+
playerController?.play(it)
76+
ReceiverBus.notify(TubeState.LIST_SHOW, emptyList<TubeVideo>())
77+
},
78+
{
79+
ReceiverBus.notify(TubeState.SERVICE_STOP)
80+
})
6981
}
7082
TUBE_PLAYLIST -> execute {
7183
TubeApi.requestPlaylist(tubeUri.id,
72-
{ playerController?.play(it) },
73-
{ ReceiverBus.notify(TubeState.SERVICE_STOP) })
84+
{
85+
toLog("requestPlaylist: $it")
86+
87+
playerController?.play(it)
88+
ReceiverBus.notify(TubeState.LIST_SHOW, it.videos)
89+
},
90+
{
91+
ReceiverBus.notify(TubeState.SERVICE_STOP)
92+
})
7493
}
7594
else -> ReceiverBus.notify(TubeState.SERVICE_STOP)
7695
}
@@ -88,19 +107,31 @@ object TubePlayer : ObjectsReceiver, ICoroutines {
88107

89108
override fun onObjectsReceive(event: Int, vararg objects: Any) {
90109
when (event) {
91-
TubeState.PLAY -> playerController?.play()
92-
TubeState.PAUSE -> playerController?.pause()
93-
TubeState.PREVIOUS -> playerController?.previous()
94-
TubeState.NEXT -> playerController?.next()
95-
96-
TubeState.WINDOW_SHOW -> viewController?.show()
97-
TubeState.WINDOW_HIDE -> viewController?.hide()
98-
99-
TubeState.SERVICE_START -> TubeService.start(context)
100-
TubeState.SERVICE_STOP -> onServiceDestroy()
101-
TubeState.RELEASE -> release()
110+
TubeState.PLAY ->
111+
if (objects.isEmpty())
112+
playerController?.play()
113+
else
114+
playerController?.play(objects.first() as Int)
115+
116+
TubeState.PAUSE -> playerController?.pause()
117+
TubeState.PREVIOUS -> playerController?.previous()
118+
TubeState.NEXT -> playerController?.next()
119+
120+
TubeState.WINDOW_SHOW -> viewController?.show()
121+
TubeState.WINDOW_HIDE -> viewController?.hide()
122+
123+
TubeState.LIST_SHOW -> {
124+
@Suppress("UNCHECKED_CAST")
125+
viewController?.showList(objects.first() as List<TubeVideo>)
126+
}
127+
TubeState.LIST_HIGHLIGHT_ITEM -> {
128+
viewController?.highlightCurrent(objects.first() as Int)
129+
}
130+
TubeState.SERVICE_START -> TubeService.start(context)
131+
TubeState.SERVICE_STOP -> onServiceDestroy()
132+
TubeState.RELEASE -> release()
102133

103-
else -> whenDebug { throw IllegalStateException("Unknown event: $event") }
134+
else -> whenDebug { throw IllegalStateException("Unknown event: $event") }
104135
}
105136
}
106137

app/src/main/java/com/rollncode/backtube/screen/TubeActivity.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import android.content.Context
44
import android.content.Intent
55
import android.os.Bundle
66
import android.support.v7.app.AppCompatActivity
7+
import com.rollncode.backtube.logic.toLog
8+
import java.nio.charset.Charset
79

810
class TubeActivity : AppCompatActivity() {
911

@@ -17,6 +19,16 @@ class TubeActivity : AppCompatActivity() {
1719
override fun onCreate(b: Bundle?) {
1820
super.onCreate(b)
1921
controller = TubeScreenController(this, b)
22+
23+
val string ="𝐓𝐡𝐞 𝐁𝐞𝐬𝐭 𝐎𝐟 𝐃𝐞𝐩𝐞𝐜𝐡𝐞 𝐌𝐨𝐝𝐞 𝐕𝐨𝐥𝐮𝐦𝐞 𝟏 (𝐑𝐞𝐦𝐚𝐬𝐭𝐞𝐫𝐞𝐝)"
24+
val bytesUTF32 = string.toByteArray(Charset.forName("UTF-32"))
25+
val bytesUTF8 = string.toByteArray(Charset.forName("UTF-8"))
26+
27+
toLog(String(bytesUTF32, Charset.forName("UTF-8")))
28+
toLog(String(bytesUTF8, Charset.forName("UTF-32")))
29+
30+
'\uD835'
31+
'a'//97
2032
}
2133

2234
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:color="@color/gray">
3+
<item
4+
android:id="@android:id/mask"
5+
android:drawable="@android:color/white"/>
6+
</ripple>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:drawable="@color/white_40" android:state_pressed="true"/>
4+
<item android:drawable="@android:color/transparent"/>
5+
</selector>

app/src/main/res/drawable/sel_white.xml

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)