From a5582a7a67b0e331e1fb406f4cd267ab05128c42 Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Tue, 2 Jul 2024 03:04:36 +0530 Subject: [PATCH] feat(ui): ability to play any local video from files using file chooser (#1158) --- .../ui/download/DownloadFragment.kt | 44 +++++++++++-- .../ui/player/DownloadedPlayerActivity.kt | 63 ++++--------------- .../cloudstream3/ui/player/LinkGenerator.kt | 9 ++- .../ui/player/OfflinePlaybackHelper.kt | 43 +++++++++++++ .../main/res/drawable/ic_network_stream.xml | 11 ++++ .../main/res/layout/fragment_downloads.xml | 23 ++++++- app/src/main/res/values/strings.xml | 1 + 7 files changed, 135 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/player/OfflinePlaybackHelper.kt create mode 100644 app/src/main/res/drawable/ic_network_stream.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index de2d4f3c..0186a651 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -1,8 +1,11 @@ package com.lagradost.cloudstream3.ui.download +import android.app.Activity import android.app.Dialog import android.content.ClipboardManager import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.os.Build import android.os.Bundle import android.text.format.Formatter.formatShortFileSize @@ -12,6 +15,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isGone @@ -30,6 +34,7 @@ import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownload import com.lagradost.cloudstream3.ui.player.BasicLink import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.LinkGenerator +import com.lagradost.cloudstream3.ui.player.OfflinePlaybackHelper.playUri import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.settings.Globals.TV @@ -137,9 +142,15 @@ class DownloadFragment : Fragment() { ) } - binding?.downloadStreamButton?.apply { - isGone = isLayout(TV) - setOnClickListener { showStreamInputDialog(it.context) } + binding?.apply { + openLocalVideoButton.apply { + isGone = isLayout(TV) + setOnClickListener { openLocalVideo() } + } + downloadStreamButton.apply { + isGone = isLayout(TV) + setOnClickListener { showStreamInputDialog(it.context) } + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -189,6 +200,22 @@ class DownloadFragment : Fragment() { view?.setLayoutWidth(bytes) } + private fun openLocalVideo() { + val intent = Intent() + .setAction(Intent.ACTION_GET_CONTENT) + .setType("video/*") + .addCategory(Intent.CATEGORY_OPENABLE) + .addFlags(FLAG_GRANT_READ_URI_PERMISSION) // Request temporary access + normalSafeApiCall { + videoResultLauncher.launch( + Intent.createChooser( + intent, + getString(R.string.open_local_video) + ) + ) + } + } + private fun showStreamInputDialog(context: Context) { val dialog = Dialog(context, R.style.AlertDialogCustom) val binding = StreamInputBinding.inflate(dialog.layoutInflater) @@ -247,4 +274,13 @@ class DownloadFragment : Fragment() { binding?.downloadStreamButton?.extend() } } -} \ No newline at end of file + + // Open local video from files using content provider x safeFile + private val videoResultLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult + val selectedVideoUri = result?.data?.data ?: return@registerForActivityResult + playUri(activity ?: return@registerForActivityResult, selectedVideoUri) + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt index 4d8860f8..4279b542 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt @@ -1,8 +1,6 @@ package com.lagradost.cloudstream3.ui.player -import android.content.ContentUris import android.content.Intent -import android.net.Uri import android.os.Bundle import android.util.Log import android.view.KeyEvent @@ -10,13 +8,13 @@ import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.utils.ExtractorUri -import com.lagradost.cloudstream3.utils.UIHelper.navigate -import com.lagradost.safefile.SafeFile - -const val DTAG = "PlayerActivity" +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.ui.player.OfflinePlaybackHelper.playLink +import com.lagradost.cloudstream3.ui.player.OfflinePlaybackHelper.playUri class DownloadedPlayerActivity : AppCompatActivity() { + private val dTAG = "DownloadedPlayerAct" + override fun dispatchKeyEvent(event: KeyEvent): Boolean { CommonActivity.dispatchKeyEvent(this, event)?.let { return it @@ -35,53 +33,18 @@ class DownloadedPlayerActivity : AppCompatActivity() { CommonActivity.onUserLeaveHint(this) } - private fun playLink(url: String) { - this.navigate( - R.id.global_to_navigation_player, GeneratorPlayer.newInstance( - LinkGenerator( - listOf( - BasicLink(url) - ) - ) - ) - ) - } - - private fun playUri(uri: Uri) { - val name = SafeFile.fromUri(this, uri)?.name() - this.navigate( - R.id.global_to_navigation_player, GeneratorPlayer.newInstance( - DownloadFileGenerator( - listOf( - ExtractorUri( - uri = uri, - name = name ?: getString(R.string.downloaded_file), - // well not the same as a normal id, but we take it as users may want to - // play downloaded files and save the location - id = kotlin.runCatching { ContentUris.parseId(uri) }.getOrNull()?.hashCode() - ) - ) - ) - ) - ) - } - override fun onCreate(savedInstanceState: Bundle?) { - Log.i(DTAG, "onCreate") - - CommonActivity.loadThemes(this) super.onCreate(savedInstanceState) + CommonActivity.loadThemes(this) CommonActivity.init(this) - setContentView(R.layout.empty_layout) + Log.i(dTAG, "onCreate") val data = intent.data if (intent?.action == Intent.ACTION_SEND) { - val extraText = try { // I dont trust android + val extraText = normalSafeApiCall { // I dont trust android intent.getStringExtra(Intent.EXTRA_TEXT) - } catch (e: Exception) { - null } val cd = intent.clipData val item = if (cd != null && cd.itemCount > 0) cd.getItemAt(0) else null @@ -89,19 +52,19 @@ class DownloadedPlayerActivity : AppCompatActivity() { // idk what I am doing, just hope any of these work if (item?.uri != null) - playUri(item.uri) + playUri(this, item.uri) else if (url != null) - playLink(url) + playLink(this, url) else if (data != null) - playUri(data) + playUri(this, data) else if (extraText != null) - playLink(extraText) + playLink(this, extraText) else { finish() return } } else if (data?.scheme == "content") { - playUri(data) + playUri(this, data) } else { finish() return diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt index ca2d9c81..02f44eb9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt @@ -1,9 +1,12 @@ package com.lagradost.cloudstream3.ui.player import com.lagradost.cloudstream3.amap -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.utils.* -import java.net.URI +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.ExtractorUri +import com.lagradost.cloudstream3.utils.INFER_TYPE +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.unshortenLinkSafe /** * Used to open the player more easily with the LinkGenerator diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/OfflinePlaybackHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/OfflinePlaybackHelper.kt new file mode 100644 index 00000000..a52ce160 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/OfflinePlaybackHelper.kt @@ -0,0 +1,43 @@ +package com.lagradost.cloudstream3.ui.player + +import android.app.Activity +import android.content.ContentUris +import android.net.Uri +import androidx.core.content.ContextCompat.getString +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.utils.ExtractorUri +import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.safefile.SafeFile + +object OfflinePlaybackHelper { + fun playLink(activity: Activity, url: String) { + activity.navigate( + R.id.global_to_navigation_player, GeneratorPlayer.newInstance( + LinkGenerator( + listOf( + BasicLink(url) + ) + ) + ) + ) + } + + fun playUri(activity: Activity, uri: Uri) { + val name = SafeFile.fromUri(activity, uri)?.name() + activity.navigate( + R.id.global_to_navigation_player, GeneratorPlayer.newInstance( + DownloadFileGenerator( + listOf( + ExtractorUri( + uri = uri, + name = name ?: getString(activity, R.string.downloaded_file), + // well not the same as a normal id, but we take it as users may want to + // play downloaded files and save the location + id = kotlin.runCatching { ContentUris.parseId(uri) }.getOrNull()?.hashCode() + ) + ) + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_network_stream.xml b/app/src/main/res/drawable/ic_network_stream.xml new file mode 100644 index 00000000..8e21fd25 --- /dev/null +++ b/app/src/main/res/drawable/ic_network_stream.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml index 5623bc7e..9dd07cfd 100644 --- a/app/src/main/res/layout/fragment_downloads.xml +++ b/app/src/main/res/layout/fragment_downloads.xml @@ -198,11 +198,30 @@ + + + + + app:icon="@drawable/ic_network_stream" + android:contentDescription="@string/stream" /> + \ 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 f577d6e1..b4a2e751 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,7 @@ %s - %s Update Started Network stream + Open local video Error Loading Links Links Reloaded Internal Storage