diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0e7c882..8a883db 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,6 +24,11 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/tpstreams/player/DownloadAdapter.kt b/app/src/main/java/com/tpstreams/player/DownloadAdapter.kt
new file mode 100644
index 0000000..359db5a
--- /dev/null
+++ b/app/src/main/java/com/tpstreams/player/DownloadAdapter.kt
@@ -0,0 +1,72 @@
+package com.tpstreams.player
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.media3.exoplayer.offline.Download
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.tpstreams.player.download.DownloadItem
+
+class DownloadAdapter : ListAdapter(DownloadDiffCallback()) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.item_download, parent, false)
+ return DownloadViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: DownloadViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+
+ class DownloadViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val titleText: TextView = itemView.findViewById(R.id.title_text)
+ private val assetIdText: TextView = itemView.findViewById(R.id.asset_id_text)
+ private val progressBar: ProgressBar = itemView.findViewById(R.id.download_progress)
+ private val progressText: TextView = itemView.findViewById(R.id.progress_text)
+ private val stateText: TextView = itemView.findViewById(R.id.state_text)
+
+ fun bind(downloadItem: DownloadItem) {
+ titleText.text = downloadItem.title
+ assetIdText.text = "Asset ID: ${downloadItem.assetId}"
+
+ // Set progress
+ val progress = downloadItem.progressPercentage.toInt()
+ progressBar.progress = progress
+
+ // Show only percentage
+ progressText.text = "$progress%"
+
+ // Set state text
+ stateText.text = getStateString(downloadItem.state)
+ }
+
+
+
+ private fun getStateString(state: Int): String {
+ return when (state) {
+ Download.STATE_COMPLETED -> "COMPLETED"
+ Download.STATE_DOWNLOADING -> "DOWNLOADING"
+ Download.STATE_FAILED -> "FAILED"
+ Download.STATE_QUEUED -> "QUEUED"
+ Download.STATE_REMOVING -> "REMOVING"
+ Download.STATE_RESTARTING -> "RESTARTING"
+ Download.STATE_STOPPED -> "PAUSED"
+ else -> "UNKNOWN"
+ }
+ }
+ }
+}
+
+class DownloadDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
+ return oldItem.assetId == newItem.assetId
+ }
+
+ override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
+ return oldItem == newItem
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tpstreams/player/DownloadViewModel.kt b/app/src/main/java/com/tpstreams/player/DownloadViewModel.kt
new file mode 100644
index 0000000..c005801
--- /dev/null
+++ b/app/src/main/java/com/tpstreams/player/DownloadViewModel.kt
@@ -0,0 +1,55 @@
+package com.tpstreams.player
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.media3.common.util.UnstableApi
+import com.tpstreams.player.download.DownloadItem
+import com.tpstreams.player.download.DownloadTracker
+
+@UnstableApi
+class DownloadViewModel(application: Application) : AndroidViewModel(application) {
+
+ private val _downloads = MutableLiveData>()
+ val downloads: LiveData> = _downloads
+
+ private val _isLoading = MutableLiveData()
+ val isLoading: LiveData = _isLoading
+
+ private val downloadTracker = DownloadTracker.getInstance(application)
+ private val downloadListener = object : DownloadTracker.Listener {
+ override fun onDownloadsChanged() {
+ loadDownloads()
+ }
+ }
+
+ init {
+ downloadTracker.addListener(downloadListener)
+ loadDownloads()
+ }
+
+ fun loadDownloads() {
+ _isLoading.value = true
+ val downloadItems = downloadTracker.getAllDownloadItems()
+ _downloads.value = downloadItems
+ _isLoading.value = false
+ }
+
+ fun pauseDownload(assetId: String) {
+ downloadTracker.pauseDownload(assetId)
+ }
+
+ fun resumeDownload(assetId: String) {
+ downloadTracker.resumeDownload(assetId)
+ }
+
+ fun removeDownload(assetId: String) {
+ downloadTracker.removeDownload(assetId)
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ downloadTracker.removeListener(downloadListener)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tpstreams/player/DownloadsActivity.kt b/app/src/main/java/com/tpstreams/player/DownloadsActivity.kt
new file mode 100644
index 0000000..804fe19
--- /dev/null
+++ b/app/src/main/java/com/tpstreams/player/DownloadsActivity.kt
@@ -0,0 +1,130 @@
+package com.tpstreams.player
+
+import android.os.Bundle
+import android.view.MenuItem
+import android.view.View
+import android.widget.PopupMenu
+import androidx.activity.viewModels
+import androidx.annotation.OptIn
+import androidx.appcompat.app.AppCompatActivity
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.offline.Download
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.tpstreams.player.databinding.ActivityDownloadsBinding
+import com.tpstreams.player.download.DownloadItem
+
+@OptIn(UnstableApi::class)
+class DownloadsActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityDownloadsBinding
+ private val viewModel: DownloadViewModel by viewModels()
+ private lateinit var adapter: DownloadAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityDownloadsBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ // Setup toolbar
+ setSupportActionBar(binding.toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+
+ // Setup RecyclerView
+ adapter = DownloadAdapter()
+ binding.downloadsRecyclerView.adapter = adapter
+ binding.downloadsRecyclerView.layoutManager = LinearLayoutManager(this)
+
+ // Setup item click listener
+ adapter.registerAdapterDataObserver(object : androidx.recyclerview.widget.RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ checkEmptyState()
+ }
+ })
+
+ // Observe downloads
+ viewModel.downloads.observe(this) { downloads ->
+ adapter.submitList(downloads)
+ checkEmptyState()
+ }
+
+ // Observe loading state
+ viewModel.isLoading.observe(this) { isLoading ->
+ binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
+ }
+
+ // Setup item click listener with popup menu
+ setupItemClickListener()
+ }
+
+ private fun setupItemClickListener() {
+ binding.downloadsRecyclerView.addOnItemTouchListener(
+ RecyclerItemClickListener(this, binding.downloadsRecyclerView,
+ object : RecyclerItemClickListener.OnItemClickListener {
+ override fun onItemClick(view: View, position: Int) {
+ val downloadItem = adapter.currentList[position]
+ showPopupMenu(view, downloadItem)
+ }
+
+ override fun onLongItemClick(view: View, position: Int) {
+ // Not needed for now
+ }
+ })
+ )
+ }
+
+ private fun showPopupMenu(view: View, downloadItem: DownloadItem) {
+ val popupMenu = PopupMenu(this, view)
+
+ when (downloadItem.state) {
+ Download.STATE_COMPLETED -> {
+ popupMenu.menu.add(getString(R.string.delete_download_title))
+ }
+ Download.STATE_DOWNLOADING -> {
+ popupMenu.menu.add(getString(R.string.pause_download))
+ popupMenu.menu.add(getString(R.string.cancel_download))
+ }
+ Download.STATE_STOPPED -> {
+ popupMenu.menu.add(getString(R.string.resume_download))
+ popupMenu.menu.add(getString(R.string.cancel_download))
+ }
+ else -> {
+ popupMenu.menu.add(getString(R.string.cancel_download))
+ }
+ }
+
+ popupMenu.setOnMenuItemClickListener { menuItem ->
+ when (menuItem.title) {
+ getString(R.string.delete_download_title), getString(R.string.cancel_download) -> {
+ viewModel.removeDownload(downloadItem.assetId)
+ }
+ getString(R.string.pause_download) -> {
+ viewModel.pauseDownload(downloadItem.assetId)
+ }
+ getString(R.string.resume_download) -> {
+ viewModel.resumeDownload(downloadItem.assetId)
+ }
+ }
+ true
+ }
+
+ popupMenu.show()
+ }
+
+ private fun checkEmptyState() {
+ if (adapter.itemCount == 0) {
+ binding.emptyView.visibility = View.VISIBLE
+ binding.downloadsRecyclerView.visibility = View.GONE
+ } else {
+ binding.emptyView.visibility = View.GONE
+ binding.downloadsRecyclerView.visibility = View.VISIBLE
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ finish()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tpstreams/player/MainActivity.kt b/app/src/main/java/com/tpstreams/player/MainActivity.kt
index 4fc1ed4..bccba26 100644
--- a/app/src/main/java/com/tpstreams/player/MainActivity.kt
+++ b/app/src/main/java/com/tpstreams/player/MainActivity.kt
@@ -1,5 +1,6 @@
package com.tpstreams.player
+import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
@@ -22,8 +23,14 @@ class MainActivity : AppCompatActivity() {
setContentView(binding.root)
// Initialize SDK once
- TPStreamsPlayer.init("6332n7")
+ TPStreamsPlayer.init("9q94nm")
binding.playerView.player = viewModel.player
+
+ // Set up downloads button
+ binding.downloadsButton.setOnClickListener {
+ val intent = Intent(this, DownloadsActivity::class.java)
+ startActivity(intent)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tpstreams/player/PlayerUIViewModel.kt b/app/src/main/java/com/tpstreams/player/PlayerUIViewModel.kt
index b9b3f6e..f68706b 100644
--- a/app/src/main/java/com/tpstreams/player/PlayerUIViewModel.kt
+++ b/app/src/main/java/com/tpstreams/player/PlayerUIViewModel.kt
@@ -12,8 +12,8 @@ class PlayerUIViewModel(application: Application) : AndroidViewModel(application
val player: TPStreamsPlayer by lazy {
TPStreamsPlayer.create(
context = application.applicationContext,
- assetId = "8rEx9apZHFF",
- accessToken = "19aa0055-d965-4654-8fce-b804e70a46b0",
+ assetId = "BEArYFdaFbt",
+ accessToken = "e6a1b485-daad-42eb-8cf2-6b6e51631092",
shouldAutoPlay = false
)
}
diff --git a/app/src/main/java/com/tpstreams/player/RecyclerItemClickListener.kt b/app/src/main/java/com/tpstreams/player/RecyclerItemClickListener.kt
new file mode 100644
index 0000000..7e123e3
--- /dev/null
+++ b/app/src/main/java/com/tpstreams/player/RecyclerItemClickListener.kt
@@ -0,0 +1,49 @@
+package com.tpstreams.player
+
+import android.content.Context
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+class RecyclerItemClickListener(
+ context: Context,
+ recyclerView: RecyclerView,
+ private val listener: OnItemClickListener?
+) : RecyclerView.OnItemTouchListener {
+
+ interface OnItemClickListener {
+ fun onItemClick(view: View, position: Int)
+ fun onLongItemClick(view: View, position: Int)
+ }
+
+ private val gestureDetector: GestureDetector
+
+ init {
+ gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ return true
+ }
+
+ override fun onLongPress(e: MotionEvent) {
+ val childView = recyclerView.findChildViewUnder(e.x, e.y)
+ if (childView != null && listener != null) {
+ listener.onLongItemClick(childView, recyclerView.getChildAdapterPosition(childView))
+ }
+ }
+ })
+ }
+
+ override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
+ val childView = view.findChildViewUnder(e.x, e.y)
+ if (childView != null && listener != null && gestureDetector.onTouchEvent(e)) {
+ listener.onItemClick(childView, view.getChildAdapterPosition(childView))
+ return true
+ }
+ return false
+ }
+
+ override fun onTouchEvent(view: RecyclerView, motionEvent: MotionEvent) {}
+
+ override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_downloads.xml b/app/src/main/res/layout/activity_downloads.xml
new file mode 100644
index 0000000..52b2a41
--- /dev/null
+++ b/app/src/main/res/layout/activity_downloads.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index ecd6e3f..8ca868a 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -16,4 +16,14 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_download.xml b/app/src/main/res/layout/item_download.xml
new file mode 100644
index 0000000..57c8bc9
--- /dev/null
+++ b/app/src/main/res/layout/item_download.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ebbf890..b0a285e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,22 @@
TPStreamsAndroidPlayer
+
+
+ Downloads
+ No downloads available
+ View Downloads
+
+
+ Delete Download
+ Do you want to delete this download?
+ Downloading
+ Your download is in progress
+ Pause Download
+ Resume Download
+ Cancel Download
+ Download
+ Select Download Quality
+ Size: %1$s
+ Download Status
+ Manage your download
\ No newline at end of file