-
Notifications
You must be signed in to change notification settings - Fork 859
Expand file tree
/
Copy pathCloudStreamApp.kt
More file actions
210 lines (178 loc) · 7.51 KB
/
CloudStreamApp.kt
File metadata and controls
210 lines (178 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package com.lagradost.cloudstream3
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.os.Build
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.SingletonImageLoader
import com.lagradost.api.setContext
import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.openBrowser
import com.lagradost.cloudstream3.ui.car.CarStrings
import com.lagradost.cloudstream3.utils.AppDebug
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.ImageLoader.buildImageLoader
import kotlinx.coroutines.runBlocking
import java.io.File
import java.io.FileNotFoundException
import java.io.PrintStream
import java.lang.ref.WeakReference
import java.util.Locale
import kotlin.concurrent.thread
import kotlin.system.exitProcess
class ExceptionHandler(
val errorFile: File,
val onError: (() -> Unit)
) : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, error: Throwable) {
try {
val threadId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
thread.threadId()
} else {
@Suppress("DEPRECATION")
thread.id
}
PrintStream(errorFile).use { ps ->
ps.println("Currently loading extension: ${PluginManager.currentlyLoading ?: "none"}")
ps.println("Fatal exception on thread ${thread.name} ($threadId)")
error.printStackTrace(ps)
}
} catch (_: FileNotFoundException) {
}
try {
onError()
} catch (_: Exception) {
}
exitProcess(1)
}
}
@Prerelease
class CloudStreamApp : Application(), SingletonImageLoader.Factory {
override fun onCreate() {
super.onCreate()
app.initClient(this)
CarStrings.init(this)
// If we want to initialize Coil as early as possible, maybe when
// loading an image or GIF in a splash screen activity.
// buildImageLoader(applicationContext)
ExceptionHandler(filesDir.resolve("last_error")) {
val intent = context!!.packageManager.getLaunchIntentForPackage(context!!.packageName)
startActivity(Intent.makeRestartActivityTask(intent!!.component))
}.also {
exceptionHandler = it
Thread.setDefaultUncaughtExceptionHandler(it)
}
AppDebug.isDebug = BuildConfig.DEBUG
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(updateBaseContextLocale(base))
context = base
// This can be removed without deprecation after next stable
AcraApplication.context = context
}
override fun newImageLoader(context: PlatformContext): ImageLoader {
// Coil module will be initialized globally when first loadImage() is invoked.
return buildImageLoader(applicationContext)
}
companion object {
var exceptionHandler: ExceptionHandler? = null
fun updateBaseContextLocale(context: Context?): Context? {
if (context == null) return null
val settingsManager = androidx.preference.PreferenceManager.getDefaultSharedPreferences(context)
val localeCode = settingsManager.getString(context.getString(R.string.locale_key), null)
// DEBUG LOGGING
println("DEBUG: updateBaseContextLocale called. localeCode from prefs: '$localeCode'")
if (localeCode.isNullOrEmpty()) return context
// Use forLanguageTag to correctly parse BCP 47 tags (e.g. "es", "es-ES", "it")
val locale = Locale.forLanguageTag(localeCode)
println("DEBUG: parsed Locale: '$locale', language: '${locale.language}', country: '${locale.country}'")
Locale.setDefault(locale)
val config = android.content.res.Configuration(context.resources.configuration)
config.setLocale(locale)
config.setLayoutDirection(locale)
return context.createConfigurationContext(config)
}
/** Use to get Activity from Context. */
tailrec fun Context.getActivity(): Activity? {
return when (this) {
is Activity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
}
}
private var _context: WeakReference<Context>? = null
var context
get() = _context?.get()
private set(value) {
_context = WeakReference(value)
setContext(WeakReference(value))
}
fun <T : Any> getKeyClass(path: String, valueType: Class<T>): T? {
return context?.getKey(path, valueType)
}
fun <T : Any> setKeyClass(path: String, value: T) {
context?.setKey(path, value)
}
fun removeKeys(folder: String): Int? {
return context?.removeKeys(folder)
}
fun <T> setKey(path: String, value: T) {
context?.setKey(path, value)
}
fun <T> setKey(folder: String, path: String, value: T) {
context?.setKey(folder, path, value)
}
inline fun <reified T : Any> getKey(path: String, defVal: T?): T? {
return context?.getKey(path, defVal)
}
inline fun <reified T : Any> getKey(path: String): T? {
return context?.getKey(path)
}
inline fun <reified T : Any> getKey(folder: String, path: String): T? {
return context?.getKey(folder, path)
}
inline fun <reified T : Any> getKey(folder: String, path: String, defVal: T?): T? {
return context?.getKey(folder, path, defVal)
}
fun getKeys(folder: String): List<String>? {
return context?.getKeys(folder)
}
fun removeKey(folder: String, path: String) {
context?.removeKey(folder, path)
}
fun removeKey(path: String) {
context?.removeKey(path)
}
/** If fallbackWebView is true and a fragment is supplied then it will open a WebView with the URL if the browser fails. */
fun openBrowser(url: String, fallbackWebView: Boolean = false, fragment: Fragment? = null) {
context?.openBrowser(url, fallbackWebView, fragment)
}
/** Will fall back to WebView if in TV or emulator layout. */
fun openBrowser(url: String, activity: FragmentActivity?) {
openBrowser(
url,
isLayout(TV or EMULATOR),
activity?.supportFragmentManager?.fragments?.lastOrNull()
)
}
}
}