cloudstream/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt

651 lines
25 KiB
Kotlin
Raw Normal View History

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
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
import android.content.pm.PackageManager
2022-01-07 19:27:25 +00:00
import android.database.Cursor
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
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
2021-07-19 13:19:47 +00:00
import android.net.Uri
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
import androidx.appcompat.app.AlertDialog
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
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
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
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
import com.lagradost.cloudstream3.ui.result.ResultFragment
2022-08-05 23:41:35 +00:00
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
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 {
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)
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
}
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
) {
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) {
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-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
) {
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-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)
}
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
}
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
}
}
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
)
}
}
}
fun Context.isAppInstalled(uri: String): Boolean {
val pm = Wrappers.packageManager(this)
return try {
pm.getPackageInfo(uri, 0) // PackageManager.GET_ACTIVITIES
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
}