2021-07-19 13:19:47 +00:00
package com.lagradost.cloudstream3.utils
2022-02-13 00:53:40 +00:00
import android.annotation.SuppressLint
2021-07-30 01:14:53 +00:00
import android.app.Activity
2022-08-16 19:07:49 +00:00
import android.app.Activity.RESULT_CANCELED
2021-07-19 13:19:47 +00:00
import android.content.ContentValues
import android.content.Context
2021-11-07 22:10:19 +00:00
import android.content.Intent
2021-07-30 01:14:53 +00:00
import android.content.pm.PackageManager
2022-01-07 19:27:25 +00:00
import android.database.Cursor
2021-07-30 01:14:53 +00:00
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
2022-02-13 00:53:40 +00:00
import android.media.tv.TvContract.Channels.COLUMN_INTERNAL_PROVIDER_ID
2021-07-30 01:14:53 +00:00
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
2021-07-19 13:19:47 +00:00
import android.net.Uri
2021-07-30 01:14:53 +00:00
import android.os.Build
2022-01-07 19:27:25 +00:00
import android.os.Environment
import android.os.ParcelFileDescriptor
2021-07-19 13:19:47 +00:00
import android.provider.MediaStore
2022-06-29 01:20:23 +00:00
import android.text.Spanned
2022-01-07 19:27:25 +00:00
import android.util.Log
2022-08-15 23:24:19 +00:00
import android.widget.Toast
2022-08-16 19:07:49 +00:00
import androidx.activity.result.contract.ActivityResultContracts
2022-02-13 00:53:40 +00:00
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
2022-08-18 18:46:20 +00:00
import androidx.appcompat.app.AlertDialog
2021-07-30 01:14:53 +00:00
import androidx.appcompat.app.AppCompatActivity
2022-04-01 20:05:34 +00:00
import androidx.core.content.ContextCompat
2022-06-29 01:20:23 +00:00
import androidx.core.text.HtmlCompat
import androidx.core.text.toSpanned
2022-08-15 23:24:19 +00:00
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
2022-08-02 16:26:16 +00:00
import androidx.recyclerview.widget.LinearLayoutManager
2022-07-30 15:30:23 +00:00
import androidx.recyclerview.widget.RecyclerView
2022-02-13 00:53:40 +00:00
import androidx.tvprovider.media.tv.PreviewChannelHelper
import androidx.tvprovider.media.tv.TvContractCompat
import androidx.tvprovider.media.tv.WatchNextProgram
import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor
2022-01-14 18:14:24 +00:00
import com.fasterxml.jackson.module.kotlin.readValue
2021-07-30 01:14:53 +00:00
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.wrappers.Wrappers
2022-08-16 19:07:49 +00:00
import com.lagradost.cloudstream3.*
2022-08-15 23:24:19 +00:00
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
2021-12-16 23:45:20 +00:00
import com.lagradost.cloudstream3.mvvm.logError
2022-08-16 19:07:49 +00:00
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
2022-08-15 23:24:19 +00:00
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.WebviewFragment
2021-07-30 01:14:53 +00:00
import com.lagradost.cloudstream3.ui.result.ResultFragment
2022-08-05 23:41:35 +00:00
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
2022-08-18 18:46:20 +00:00
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll
2022-08-15 23:24:19 +00:00
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
2022-02-13 00:53:40 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
2022-08-15 23:24:19 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.main
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.toClassDir
import com.lagradost.cloudstream3.utils.JsUnpacker.Companion.load
2021-09-20 21:11:36 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.navigate
2022-01-07 19:27:25 +00:00
import okhttp3.Cache
import java.io.*
2021-11-07 22:10:19 +00:00
import java.net.URL
import java.net.URLDecoder
2021-07-19 13:19:47 +00:00
object AppUtils {
2022-07-30 15:30:23 +00:00
fun RecyclerView . setMaxViewPoolSize ( maxViewTypeId : Int , maxPoolSize : Int ) {
for ( i in 0. . maxViewTypeId )
recycledViewPool . setMaxRecycledViews ( i , maxPoolSize )
}
2022-08-02 16:26:16 +00:00
fun RecyclerView . isRecyclerScrollable ( ) : Boolean {
val layoutManager =
this . layoutManager as ? LinearLayoutManager ?
val adapter = adapter
2022-08-19 18:28:03 +00:00
return if ( layoutManager == null || adapter == null ) false else layoutManager . findLastCompletelyVisibleItemPosition ( ) < adapter . itemCount - 7 // bit more than 1 to make it more seamless
2022-08-02 16:26:16 +00:00
}
2022-02-13 00:53:40 +00:00
//fun Context.deleteFavorite(data: SearchResponse) {
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
// normalSafeApiCall {
// val existingId =
// getWatchNextProgramByVideoId(data.url, this).second ?: return@normalSafeApiCall
// contentResolver.delete(
//
// TvContractCompat.buildWatchNextProgramUri(existingId),
// null, null
// )
// }
//}
2022-06-29 01:20:23 +00:00
fun String ?. html ( ) : Spanned {
return getHtmlText ( this ?: return " " . toSpanned ( ) )
}
private fun getHtmlText ( text : String ) : Spanned {
return try {
// I have no idea if this can throw any error, but I dont want to try
HtmlCompat . fromHtml (
text , HtmlCompat . FROM _HTML _MODE _LEGACY
)
} catch ( e : Exception ) {
logError ( e )
text . toSpanned ( )
}
}
2022-02-13 00:53:40 +00:00
@SuppressLint ( " RestrictedApi " )
private fun buildWatchNextProgramUri (
context : Context ,
card : DataStoreHelper . ResumeWatchingResult
) : WatchNextProgram {
2022-03-20 23:59:17 +00:00
val isSeries = card . type ?. isMovieType ( ) == false
2022-02-13 00:53:40 +00:00
val title = if ( isSeries ) {
context . getNameFull ( card . name , card . episode , card . season )
} else {
card . name
}
val builder = WatchNextProgram . Builder ( )
. setEpisodeTitle ( title )
. setType (
if ( isSeries ) {
TvContractCompat . WatchNextPrograms . TYPE _TV _EPISODE
} else TvContractCompat . WatchNextPrograms . TYPE _MOVIE
)
. setWatchNextType ( TvContractCompat . WatchNextPrograms . WATCH _NEXT _TYPE _CONTINUE )
. setTitle ( title )
. setPosterArtUri ( Uri . parse ( card . posterUrl ) )
. setIntentUri ( Uri . parse ( card . url ) ) //TODO FIX intent
. setInternalProviderId ( card . url )
//.setLastEngagementTimeUtcMillis(System.currentTimeMillis())
card . watchPos ?. let {
builder . setDurationMillis ( it . duration . toInt ( ) )
builder . setLastPlaybackPositionMillis ( it . position . toInt ( ) )
}
// .setLastEngagementTimeUtcMillis() //TODO
if ( isSeries )
card . episode ?. let {
builder . setEpisodeNumber ( it )
}
return builder . build ( )
}
/ * *
* Find the Watch Next program for given id .
* Returns the first instance available .
* /
@SuppressLint ( " RestrictedApi " )
// Suppress RestrictedApi due to https://issuetracker.google.com/138150076
fun findFirstWatchNextProgram ( context : Context , predicate : ( Cursor ) -> Boolean ) :
Pair < WatchNextProgram ? , Long ? > {
val COLUMN _WATCH _NEXT _ID _INDEX = 0
// val COLUMN_WATCH_NEXT_INTERNAL_PROVIDER_ID_INDEX = 1
// val COLUMN_WATCH_NEXT_COLUMN_BROWSABLE_INDEX = 2
val cursor = context . contentResolver . query (
TvContractCompat . WatchNextPrograms . CONTENT _URI ,
WatchNextProgram . PROJECTION ,
/* selection = */ null ,
/* selectionArgs = */ null ,
/* sortOrder= */ null
)
cursor ?. use {
if ( it . moveToFirst ( ) ) {
do {
if ( predicate ( cursor ) ) {
return fromCursor ( cursor ) to cursor . getLong ( COLUMN _WATCH _NEXT _ID _INDEX )
}
} while ( it . moveToNext ( ) )
}
}
return null to null
}
/ * *
* Query the Watch Next list and find the program with given videoId .
* Return null if not found .
* /
@RequiresApi ( Build . VERSION_CODES . O )
@SuppressLint ( " Range " )
@Synchronized
private fun getWatchNextProgramByVideoId (
id : String ,
context : Context
) : Pair < WatchNextProgram ? , Long ? > {
return findFirstWatchNextProgram ( context ) { cursor ->
( cursor . getString ( cursor . getColumnIndex ( COLUMN _INTERNAL _PROVIDER _ID ) ) == id )
}
}
// https://github.com/googlearchive/leanback-homescreen-channels/blob/master/app/src/main/java/com/google/android/tvhomescreenchannels/SampleTvProvider.java
@SuppressLint ( " RestrictedApi " )
@WorkerThread
fun Context . addProgramsToContinueWatching ( data : List < DataStoreHelper . ResumeWatchingResult > ) {
if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . O ) return
2022-08-03 00:04:03 +00:00
val context = this
2022-02-13 00:53:40 +00:00
ioSafe {
data . forEach { episodeInfo ->
try {
2022-08-03 00:04:03 +00:00
val ( program , id ) = getWatchNextProgramByVideoId ( episodeInfo . url , context )
val nextProgram = buildWatchNextProgramUri ( context , episodeInfo )
2022-02-13 00:53:40 +00:00
// If the program is already in the Watch Next row, update it
if ( program != null && id != null ) {
2022-08-03 00:04:03 +00:00
PreviewChannelHelper ( context ) . updateWatchNextProgram (
2022-02-13 00:53:40 +00:00
nextProgram ,
id ,
)
} else {
2022-08-03 00:04:03 +00:00
PreviewChannelHelper ( context )
2022-02-13 00:53:40 +00:00
. publishWatchNextProgram ( nextProgram )
}
} catch ( e : Exception ) {
logError ( e )
}
}
}
}
@SuppressLint ( " Range " )
2021-07-19 13:19:47 +00:00
fun getVideoContentUri ( context : Context , videoFilePath : String ) : Uri ? {
val cursor = context . contentResolver . query (
MediaStore . Video . Media . EXTERNAL _CONTENT _URI , arrayOf ( MediaStore . Video . Media . _ID ) ,
MediaStore . Video . Media . DATA + " =? " , arrayOf ( videoFilePath ) , null
)
return if ( cursor != null && cursor . moveToFirst ( ) ) {
val id = cursor . getInt ( cursor . getColumnIndex ( MediaStore . MediaColumns . _ID ) )
cursor . close ( )
Uri . withAppendedPath ( MediaStore . Video . Media . EXTERNAL _CONTENT _URI , " " + id )
} else {
val values = ContentValues ( )
values . put ( MediaStore . Video . Media . DATA , videoFilePath )
context . contentResolver . insert (
MediaStore . Video . Media . EXTERNAL _CONTENT _URI , values
)
}
}
2022-08-16 19:07:49 +00:00
2022-08-15 23:24:19 +00:00
fun Activity . loadRepository ( url : String ) {
ioSafe {
val repo = RepositoryManager . parseRepository ( url ) ?: return @ioSafe
RepositoryManager . addRepository (
RepositoryData (
repo . name ,
url
)
)
main {
showToast (
this @loadRepository ,
getString ( R . string . player _loaded _subtitles , repo . name ) ,
Toast . LENGTH _LONG
)
}
afterRepositoryLoadedEvent . invoke ( true )
2022-08-18 18:46:20 +00:00
downloadAllPluginsDialog ( url , repo . name )
}
}
fun Activity . downloadAllPluginsDialog ( repositoryUrl : String , repositoryName : String ) {
runOnUiThread {
val context = this
val builder : AlertDialog . Builder = AlertDialog . Builder ( this )
builder . setTitle (
repositoryName
)
builder . setMessage (
R . string . download _all _plugins _from _repo
)
builder . apply {
setPositiveButton ( R . string . download ) { _ , _ ->
downloadAll ( context , repositoryUrl , null )
}
setNegativeButton ( R . string . cancel ) { _ , _ -> }
}
builder . show ( )
2022-08-15 23:24:19 +00:00
}
}
2022-08-16 19:07:49 +00:00
private fun Context . hasWebView ( ) : Boolean {
return this . packageManager . hasSystemFeature ( " android.software.webview " )
}
private fun openWebView ( fragment : Fragment ? , url : String ) {
if ( fragment ?. context ?. hasWebView ( ) == true )
2022-08-18 00:54:05 +00:00
normalSafeApiCall {
fragment
. findNavController ( )
. navigate ( R . id . navigation _webview , WebviewFragment . newInstance ( url ) )
}
2022-08-16 19:07:49 +00:00
}
2021-07-30 01:14:53 +00:00
2022-08-15 23:24:19 +00:00
/ * *
* If fallbackWebview is true and a fragment is supplied then it will open a webview with the url if the browser fails .
* * /
fun Context . openBrowser (
url : String ,
fallbackWebview : Boolean = false ,
2022-08-16 19:07:49 +00:00
fragment : Fragment ? = null ,
2022-08-15 23:24:19 +00:00
) {
2021-12-16 23:45:20 +00:00
try {
val intent = Intent ( Intent . ACTION _VIEW )
intent . data = Uri . parse ( url )
2022-04-01 20:05:34 +00:00
intent . addFlags ( Intent . FLAG _ACTIVITY _NEW _TASK or Intent . FLAG _ACTIVITY _CLEAR _TASK )
2022-08-16 19:07:49 +00:00
// activityResultRegistry is used to fall back to webview if a browser is missing
// On older versions the startActivity just crashes, but on newer android versions
// You need to check the result to make sure it failed
val activityResultRegistry = fragment ?. activity ?. activityResultRegistry
if ( activityResultRegistry != null ) {
activityResultRegistry . register (
url ,
ActivityResultContracts . StartActivityForResult ( )
) { result ->
if ( result . resultCode == RESULT _CANCELED && fallbackWebview ) {
openWebView ( fragment , url )
}
} . launch ( intent )
} else {
ContextCompat . startActivity ( this , intent , null )
}
2022-01-07 19:27:25 +00:00
} catch ( e : Exception ) {
2021-12-16 23:45:20 +00:00
logError ( e )
2022-08-15 23:24:19 +00:00
if ( fallbackWebview ) {
2022-08-16 19:07:49 +00:00
openWebView ( fragment , url )
2022-08-15 23:24:19 +00:00
}
2021-12-16 23:45:20 +00:00
}
2021-11-07 22:10:19 +00:00
}
fun splitQuery ( url : URL ) : Map < String , String > {
val queryPairs : MutableMap < String , String > = LinkedHashMap ( )
val query : String = url . query
val pairs = query . split ( " & " ) . toTypedArray ( )
for ( pair in pairs ) {
val idx = pair . indexOf ( " = " )
queryPairs [ URLDecoder . decode ( pair . substring ( 0 , idx ) , " UTF-8 " ) ] =
URLDecoder . decode ( pair . substring ( idx + 1 ) , " UTF-8 " )
}
return queryPairs
}
2021-11-28 16:10:19 +00:00
/** Any object as json string */
fun Any . toJson ( ) : String {
2022-06-29 01:20:23 +00:00
if ( this is String ) return this
2021-11-28 16:10:19 +00:00
return mapper . writeValueAsString ( this )
}
2022-02-13 00:53:40 +00:00
inline fun < reified T > parseJson ( value : String ) : T {
2022-01-14 18:14:24 +00:00
return mapper . readValue ( value )
}
2022-04-02 19:01:00 +00:00
inline fun < reified T > tryParseJson ( value : String ? ) : T ? {
2022-01-16 22:31:42 +00:00
return try {
2022-04-02 19:01:00 +00:00
parseJson ( value ?: return null )
2022-02-13 00:53:40 +00:00
} catch ( _ : Exception ) {
2022-01-16 22:31:42 +00:00
null
}
}
2021-08-09 14:40:20 +00:00
/ * * | S1 : E2 Hello World
* | Episode 2. Hello world
* | Hello World
* | Season 1 - Episode 2
* | Episode 2
* * * /
2021-09-02 13:19:50 +00:00
fun Context . getNameFull ( name : String ? , episode : Int ? , season : Int ? ) : String {
2021-08-25 15:28:25 +00:00
val rEpisode = if ( episode == 0 ) null else episode
val rSeason = if ( season == 0 ) null else season
2021-08-11 18:26:19 +00:00
2021-09-02 13:19:50 +00:00
val seasonName = getString ( R . string . season )
val episodeName = getString ( R . string . episode )
val seasonNameShort = getString ( R . string . season _short )
val episodeNameShort = getString ( R . string . episode _short )
2021-08-09 14:40:20 +00:00
if ( name != null ) {
2021-08-25 15:28:25 +00:00
return if ( rEpisode != null && rSeason != null ) {
2021-09-02 13:19:50 +00:00
" $seasonNameShort ${rSeason} : $episodeNameShort ${rEpisode} $name "
2021-08-25 15:28:25 +00:00
} else if ( rEpisode != null ) {
2021-09-02 13:19:50 +00:00
" $episodeName $rEpisode . $name "
2021-08-09 14:40:20 +00:00
} else {
name
}
} else {
2021-08-25 15:28:25 +00:00
if ( rEpisode != null && rSeason != null ) {
2021-09-02 13:19:50 +00:00
return " $seasonName $rSeason - $episodeName $rEpisode "
2021-08-25 15:28:25 +00:00
} else if ( rSeason == null ) {
2021-09-02 13:19:50 +00:00
return " $episodeName $rEpisode "
2021-08-09 14:40:20 +00:00
}
}
return " "
}
2022-01-07 19:27:25 +00:00
fun Activity ?. loadCache ( ) {
try {
cacheClass ( " android.net.NetworkCapabilities " . load ( ) )
} catch ( _ : Exception ) {
}
}
2021-11-05 21:39:56 +00:00
//private val viewModel: ResultViewModel by activityViewModels()
2022-08-15 23:24:19 +00:00
private fun getResultsId ( context : Context ) : Int {
return if ( context . isTrueTvSettings ( ) ) {
2022-08-05 23:41:35 +00:00
R . id . global _to _navigation _results _tv
} else {
R . id . global _to _navigation _results _phone
}
2022-08-04 22:26:33 +00:00
}
2021-11-28 16:10:19 +00:00
fun AppCompatActivity . loadResult (
url : String ,
apiName : String ,
startAction : Int = 0 ,
startValue : Int = 0
) {
2021-07-30 01:14:53 +00:00
this . runOnUiThread {
2021-11-07 22:10:19 +00:00
// viewModelStore.clear()
this . navigate (
2022-08-04 22:26:33 +00:00
getResultsId ( this . applicationContext ?: return @runOnUiThread ) ,
2021-11-07 22:10:19 +00:00
ResultFragment . newInstance ( url , apiName , startAction , startValue )
)
2021-07-30 01:14:53 +00:00
}
}
2021-11-28 16:10:19 +00:00
fun Activity ?. loadSearchResult (
card : SearchResponse ,
startAction : Int = 0 ,
2022-05-02 21:32:28 +00:00
startValue : Int ? = null ,
2021-11-28 16:10:19 +00:00
) {
2022-05-02 21:32:28 +00:00
this ?. runOnUiThread {
// viewModelStore.clear()
this . navigate (
2022-08-04 22:26:33 +00:00
getResultsId ( this ) ,
2022-05-02 21:32:28 +00:00
ResultFragment . newInstance ( card , startAction , startValue )
)
}
//(this as? AppCompatActivity?)?.loadResult(card.url, card.apiName, startAction, startValue)
2021-07-30 01:14:53 +00:00
}
fun Activity . requestLocalAudioFocus ( focusRequest : AudioFocusRequest ? ) {
if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . O && focusRequest != null ) {
val audioManager = getSystemService ( Context . AUDIO _SERVICE ) as AudioManager
audioManager . requestAudioFocus ( focusRequest )
} else {
val audioManager : AudioManager =
getSystemService ( Context . AUDIO _SERVICE ) as AudioManager
audioManager . requestAudioFocus (
null ,
AudioManager . STREAM _MUSIC ,
AudioManager . AUDIOFOCUS _GAIN _TRANSIENT _MAY _DUCK
)
}
}
private var currentAudioFocusRequest : AudioFocusRequest ? = null
private var currentAudioFocusChangeListener : AudioManager . OnAudioFocusChangeListener ? = null
var onAudioFocusEvent = Event < Boolean > ( )
private fun getAudioListener ( ) : AudioManager . OnAudioFocusChangeListener ? {
if ( currentAudioFocusChangeListener != null ) return currentAudioFocusChangeListener
currentAudioFocusChangeListener = AudioManager . OnAudioFocusChangeListener {
onAudioFocusEvent . invoke (
when ( it ) {
AudioManager . AUDIOFOCUS _GAIN -> false
AudioManager . AUDIOFOCUS _GAIN _TRANSIENT _EXCLUSIVE -> false
AudioManager . AUDIOFOCUS _GAIN _TRANSIENT -> false
else -> true
}
)
}
return currentAudioFocusChangeListener
}
fun Context . isCastApiAvailable ( ) : Boolean {
val isCastApiAvailable =
GoogleApiAvailability . getInstance ( )
. isGooglePlayServicesAvailable ( applicationContext ) == ConnectionResult . SUCCESS
try {
applicationContext ?. let { CastContext . getSharedInstance ( it ) }
} catch ( e : Exception ) {
println ( e )
// track non-fatal
return false
}
return isCastApiAvailable
}
fun Context . isConnectedToChromecast ( ) : Boolean {
if ( isCastApiAvailable ( ) ) {
val castContext = CastContext . getSharedInstance ( this )
if ( castContext . castState == CastState . CONNECTED ) {
return true
}
}
return false
}
2022-01-07 19:27:25 +00:00
// Copied from https://github.com/videolan/vlc-android/blob/master/application/vlc-android/src/org/videolan/vlc/util/FileUtils.kt
2022-02-13 00:53:40 +00:00
@SuppressLint ( " Range " )
2022-01-07 19:27:25 +00:00
fun Context . getUri ( data : Uri ? ) : Uri ? {
var uri = data
val ctx = this
if ( data != null && data . scheme == " content " ) {
// Mail-based apps - download the stream to a temporary file and play it
if ( " com.fsck.k9.attachmentprovider " == data . host || " gmail-ls " == data . host ) {
var inputStream : InputStream ? = null
var os : OutputStream ? = null
var cursor : Cursor ? = null
try {
cursor = ctx . contentResolver . query (
data ,
arrayOf ( MediaStore . MediaColumns . DISPLAY _NAME ) , null , null , null
)
if ( cursor != null && cursor . moveToFirst ( ) ) {
2022-02-13 00:53:40 +00:00
val filename =
cursor . getString ( cursor . getColumnIndex ( MediaStore . MediaColumns . DISPLAY _NAME ) )
. replace ( " / " , " " )
2022-01-07 19:27:25 +00:00
inputStream = ctx . contentResolver . openInputStream ( data )
if ( inputStream == null ) return data
2022-02-13 00:53:40 +00:00
os =
FileOutputStream ( Environment . getExternalStorageDirectory ( ) . path + " /Download/ " + filename )
2022-01-07 19:27:25 +00:00
val buffer = ByteArray ( 1024 )
var bytesRead = inputStream . read ( buffer )
while ( bytesRead >= 0 ) {
os . write ( buffer , 0 , bytesRead )
bytesRead = inputStream . read ( buffer )
}
uri =
Uri . fromFile ( File ( Environment . getExternalStorageDirectory ( ) . path + " /Download/ " + filename ) )
}
} catch ( e : Exception ) {
return null
} finally {
inputStream ?. close ( )
os ?. close ( )
cursor ?. close ( )
}
} else if ( data . authority == " media " ) {
uri = this . contentResolver . query (
data ,
arrayOf ( MediaStore . Video . Media . DATA ) , null , null , null
) ?. use {
val columnIndex = it . getColumnIndexOrThrow ( MediaStore . Video . Media . DATA )
2022-02-13 00:53:40 +00:00
if ( it . moveToFirst ( ) ) Uri . fromFile ( File ( it . getString ( columnIndex ) ) )
?: data else data
2022-01-07 19:27:25 +00:00
}
//uri = MediaUtils.getContentMediaUri(data)
/ * } else if ( data . authority == ctx . getString ( R . string . tv _provider _authority ) ) {
println ( " TV AUTHORITY " )
//val medialibrary = Medialibrary.getInstance()
//val media = medialibrary.getMedia(data.lastPathSegment!!.toLong())
uri = null //media.uri*/
} else {
val inputPFD : ParcelFileDescriptor ?
try {
inputPFD = ctx . contentResolver . openFileDescriptor ( data , " r " )
if ( inputPFD == null ) return data
uri = Uri . parse ( " fd:// " + inputPFD . fd )
// Cursor returnCursor =
// getContentResolver().query(data, null, null, null, null);
// if (returnCursor != null) {
// if (returnCursor.getCount() > 0) {
// int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
// if (nameIndex > -1) {
// returnCursor.moveToFirst();
// title = returnCursor.getString(nameIndex);
// }
// }
// returnCursor.close();
// }
} catch ( e : FileNotFoundException ) {
Log . e ( " TAG " , " ${e.message} for $data " , e )
return null
} catch ( e : IllegalArgumentException ) {
Log . e ( " TAG " , " ${e.message} for $data " , e )
return null
} catch ( e : IllegalStateException ) {
Log . e ( " TAG " , " ${e.message} for $data " , e )
return null
} catch ( e : NullPointerException ) {
Log . e ( " TAG " , " ${e.message} for $data " , e )
return null
} catch ( e : SecurityException ) {
Log . e ( " TAG " , " ${e.message} for $data " , e )
return null
}
} // Media or MMS URI
}
return uri
}
2021-07-30 01:14:53 +00:00
fun Context . isUsingMobileData ( ) : Boolean {
val conManager = getSystemService ( Context . CONNECTIVITY _SERVICE ) as ConnectivityManager
val networkInfo = conManager . allNetworks
return networkInfo . any {
2021-11-28 16:10:19 +00:00
conManager . getNetworkCapabilities ( it )
?. hasTransport ( NetworkCapabilities . TRANSPORT _CELLULAR ) == true
2021-07-30 01:14:53 +00:00
}
}
2022-01-07 19:27:25 +00:00
private fun Activity ?. cacheClass ( clazz : String ? ) {
clazz ?. let { c ->
this ?. cacheDir ?. let {
Cache (
directory = File ( it , c . toClassDir ( ) ) ,
maxSize = 20L * 1024L * 1024L // 20 MiB
)
}
}
}
2021-07-30 01:14:53 +00:00
fun Context . isAppInstalled ( uri : String ) : Boolean {
val pm = Wrappers . packageManager ( this )
2022-05-05 21:50:08 +00:00
return try {
pm . getPackageInfo ( uri , 0 ) // PackageManager.GET_ACTIVITIES
2021-07-30 01:14:53 +00:00
true
} catch ( e : PackageManager . NameNotFoundException ) {
false
}
}
fun getFocusRequest ( ) : AudioFocusRequest ? {
if ( currentAudioFocusRequest != null ) return currentAudioFocusRequest
currentAudioFocusRequest = if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . O ) {
AudioFocusRequest . Builder ( AudioManager . AUDIOFOCUS _GAIN ) . run {
setAudioAttributes ( AudioAttributes . Builder ( ) . run {
setUsage ( AudioAttributes . USAGE _MEDIA )
setContentType ( AudioAttributes . CONTENT _TYPE _MOVIE )
build ( )
} )
setAcceptsDelayedFocusGain ( true )
getAudioListener ( ) ?. let {
setOnAudioFocusChangeListener ( it )
}
build ( )
}
} else {
null
}
return currentAudioFocusRequest
}
2021-07-19 13:19:47 +00:00
}