[optimize] Refactoring API client injection architecture
This commit is contained in:
@@ -8,17 +8,17 @@ class Comic(
|
|||||||
val token: String
|
val token: String
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
fun getPage(pageNumber: Int): String
|
fun getPage(pageNumber: Int, api: ApiClient): String
|
||||||
{
|
{
|
||||||
return "${ApiClient.getBase()}api/image/$id/${comic.list[pageNumber]}?token=$token"
|
return "${api.getBase()}api/image/$id/${comic.list[pageNumber]}?token=$token"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPage(pageName: String): String?
|
fun getPage(pageName: String, api: ApiClient): String?
|
||||||
{
|
{
|
||||||
val v = comic.list.indexOf(pageName)
|
val v = comic.list.indexOf(pageName)
|
||||||
if(v >= 0)
|
if(v >= 0)
|
||||||
{
|
{
|
||||||
return getPage(v)
|
return getPage(v, api)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ class Comic(
|
|||||||
var v = comic.list.indexOf(pageName)
|
var v = comic.list.indexOf(pageName)
|
||||||
if(v >= 0)
|
if(v >= 0)
|
||||||
{
|
{
|
||||||
var r: Int = 1
|
var r = 1
|
||||||
v+=1
|
v+=1
|
||||||
while(v < comic.list.size && !comic.bookmarks.any{
|
while(v < comic.list.size && !comic.bookmarks.any{
|
||||||
x -> x.page == comic.list[v]
|
x -> x.page == comic.list[v]
|
||||||
|
|||||||
@@ -14,28 +14,28 @@ class Video(
|
|||||||
val token: String,
|
val token: String,
|
||||||
val video: VideoResponse
|
val video: VideoResponse
|
||||||
) {
|
) {
|
||||||
fun getCover(): String {
|
fun getCover(api: ApiClient): String {
|
||||||
return if (isLocal)
|
return if (isLocal)
|
||||||
"$localBase/videos/$klass/$id/cover.jpg"
|
"$localBase/videos/$klass/$id/cover.jpg"
|
||||||
else
|
else
|
||||||
"${ApiClient.getBase()}api/video/$klass/$id/cover?token=$token"
|
"${api.getBase()}api/video/$klass/$id/cover?token=$token"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVideo(): String {
|
fun getVideo(api: ApiClient): String {
|
||||||
return if (isLocal)
|
return if (isLocal)
|
||||||
"$localBase/videos/$klass/$id/video.mp4"
|
"$localBase/videos/$klass/$id/video.mp4"
|
||||||
else
|
else
|
||||||
"${ApiClient.getBase()}api/video/$klass/$id/av?token=$token"
|
"${api.getBase()}api/video/$klass/$id/av?token=$token"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSubtitle(): String {
|
fun getSubtitle(api: ApiClient): String {
|
||||||
return if (isLocal)
|
return if (isLocal)
|
||||||
"$localBase/videos/$klass/$id/subtitle.vtt"
|
"$localBase/videos/$klass/$id/subtitle.vtt"
|
||||||
else
|
else
|
||||||
"${ApiClient.getBase()}api/video/$klass/$id/subtitle?token=$token"
|
"${api.getBase()}api/video/$klass/$id/subtitle?token=$token"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGallery(): List<KeyImage> {
|
fun getGallery(api: ApiClient): List<KeyImage> {
|
||||||
return if (isLocal)
|
return if (isLocal)
|
||||||
video.gallery.map {
|
video.gallery.map {
|
||||||
KeyImage(
|
KeyImage(
|
||||||
@@ -46,7 +46,7 @@ class Video(
|
|||||||
} else video.gallery.map {
|
} else video.gallery.map {
|
||||||
KeyImage(
|
KeyImage(
|
||||||
name = it,
|
name = it,
|
||||||
url = "${ApiClient.getBase()}api/video/$klass/$id/gallery/$it?token=$token",
|
url = "${api.getBase()}api/video/$klass/$id/gallery/$it?token=$token",
|
||||||
key = "$klass/$id/gallery/$it"
|
key = "$klass/$id/gallery/$it"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package com.acitelight.aether.service
|
package com.acitelight.aether.service
|
||||||
|
|
||||||
import com.acitelight.aether.service.AuthManager.db64
|
|
||||||
import com.acitelight.aether.service.AuthManager.signChallenge
|
|
||||||
import com.acitelight.aether.service.AuthManager.signChallengeByte
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@@ -44,7 +41,7 @@ class AbyssStream private constructor(
|
|||||||
* Create and perform handshake on an already-connected socket.
|
* Create and perform handshake on an already-connected socket.
|
||||||
* If privateKeyRaw is provided, it must be 32 bytes.
|
* If privateKeyRaw is provided, it must be 32 bytes.
|
||||||
*/
|
*/
|
||||||
suspend fun create(socket: Socket, privateKeyRaw: ByteArray? = null): AbyssStream = withContext(Dispatchers.IO) {
|
suspend fun create(authManager: AuthManager, socket: Socket, privateKeyRaw: ByteArray? = null): AbyssStream = withContext(Dispatchers.IO) {
|
||||||
if (!socket.isConnected) throw IllegalArgumentException("socket is not connected")
|
if (!socket.isConnected) throw IllegalArgumentException("socket is not connected")
|
||||||
val inStream = socket.getInputStream()
|
val inStream = socket.getInputStream()
|
||||||
val outStream = socket.getOutputStream()
|
val outStream = socket.getOutputStream()
|
||||||
@@ -69,7 +66,7 @@ class AbyssStream private constructor(
|
|||||||
|
|
||||||
val ch = ByteArray(32)
|
val ch = ByteArray(32)
|
||||||
readExact(inStream, ch, 0, 32)
|
readExact(inStream, ch, 0, 32)
|
||||||
val signed = signChallengeByte(localPriv, ch)
|
val signed = authManager.signChallengeByte(localPriv, ch)
|
||||||
writeExact(outStream, signed, 0, signed.size)
|
writeExact(outStream, signed, 0, signed.size)
|
||||||
readExact(inStream, ch, 0, 16)
|
readExact(inStream, ch, 0, 16)
|
||||||
|
|
||||||
@@ -222,7 +219,7 @@ class AbyssStream private constructor(
|
|||||||
val header = ByteArray(4)
|
val header = ByteArray(4)
|
||||||
try {
|
try {
|
||||||
readExact(input, header, 0, 4)
|
readExact(input, header, 0, 4)
|
||||||
} catch (e: EOFException) {
|
} catch (_: EOFException) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val payloadLen = ByteBuffer.wrap(header).int and 0xffffffff.toInt()
|
val payloadLen = ByteBuffer.wrap(header).int and 0xffffffff.toInt()
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package com.acitelight.aether.service
|
package com.acitelight.aether.service
|
||||||
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.acitelight.aether.service.AuthManager.db64
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.selects.select
|
import kotlinx.coroutines.selects.select
|
||||||
@@ -17,7 +14,8 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class AbyssTunnelProxy @Inject constructor(
|
class AbyssTunnelProxy @Inject constructor(
|
||||||
private val settingsDataStoreManager: SettingsDataStoreManager
|
private val settingsDataStoreManager: SettingsDataStoreManager,
|
||||||
|
private val authManager: AuthManager
|
||||||
) {
|
) {
|
||||||
private val coroutineContext: CoroutineContext = Dispatchers.IO
|
private val coroutineContext: CoroutineContext = Dispatchers.IO
|
||||||
private var serverHost: String = ""
|
private var serverHost: String = ""
|
||||||
@@ -48,7 +46,7 @@ class AbyssTunnelProxy @Inject constructor(
|
|||||||
|
|
||||||
launch {
|
launch {
|
||||||
try { handleLocalConnection(client) }
|
try { handleLocalConnection(client) }
|
||||||
catch (ex: Exception) { /* ignore */ }
|
catch (_: Exception) { /* ignore */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
@@ -72,14 +70,14 @@ class AbyssTunnelProxy @Inject constructor(
|
|||||||
var abyssStream: AbyssStream? = null
|
var abyssStream: AbyssStream? = null
|
||||||
try {
|
try {
|
||||||
abyssSocket = Socket(serverHost, serverPort)
|
abyssSocket = Socket(serverHost, serverPort)
|
||||||
abyssStream = AbyssStream.create(abyssSocket, db64(settingsDataStoreManager.privateKeyFlow.first()))
|
abyssStream = AbyssStream.create(authManager, abyssSocket, authManager.db64(settingsDataStoreManager.privateKeyFlow.first()))
|
||||||
|
|
||||||
// concurrently copy in both directions
|
// concurrently copy in both directions
|
||||||
val job1 = launch { copyExactSuspend(localIn, abyssStream) } // local -> abyss
|
val job1 = launch { copyExactSuspend(localIn, abyssStream) } // local -> abyss
|
||||||
val job2 = launch { copyFromAbyssToLocal(abyssStream, localOut) } // abyss -> local
|
val job2 = launch { copyFromAbyssToLocal(abyssStream, localOut) } // abyss -> local
|
||||||
|
|
||||||
// wait for either direction to finish
|
// wait for either direction to finish
|
||||||
select<Unit> {
|
select {
|
||||||
job1.onJoin { /* completed */ }
|
job1.onJoin { /* completed */ }
|
||||||
job2.onJoin { /* completed */ }
|
job2.onJoin { /* completed */ }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.ConnectionSpec
|
import okhttp3.ConnectionSpec
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.CookieJar
|
||||||
import okhttp3.EventListener
|
import okhttp3.EventListener
|
||||||
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@@ -24,23 +27,27 @@ import java.security.KeyStore
|
|||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.TrustManagerFactory
|
import javax.net.ssl.TrustManagerFactory
|
||||||
import javax.net.ssl.X509TrustManager
|
import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class ApiClient @Inject constructor(
|
||||||
|
|
||||||
object ApiClient {
|
) {
|
||||||
fun getBase(): String{
|
fun getBase(): String{
|
||||||
return replaceAbyssProtocol(base)
|
return replaceAbyssProtocol(base)
|
||||||
}
|
}
|
||||||
private var base: String = ""
|
private var base: String = ""
|
||||||
var domain: String = ""
|
private var domain: String = ""
|
||||||
var cert: String = ""
|
private var cert: String = ""
|
||||||
private val json = Json {
|
private val json = Json {
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceAbyssProtocol(uri: String): String {
|
private fun replaceAbyssProtocol(uri: String): String {
|
||||||
return uri.replaceFirst("^abyss://".toRegex(), "https://")
|
return uri.replaceFirst("^abyss://".toRegex(), "https://")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +59,7 @@ object ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadCertificateFromString(pemString: String): X509Certificate {
|
private fun loadCertificateFromString(pemString: String): X509Certificate {
|
||||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
val decodedPem = pemString
|
val decodedPem = pemString
|
||||||
.replace("-----BEGIN CERTIFICATE-----", "")
|
.replace("-----BEGIN CERTIFICATE-----", "")
|
||||||
@@ -66,7 +73,7 @@ object ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createOkHttpClientWithDynamicCert(trustedCert: X509Certificate?): OkHttpClient {
|
private fun createOkHttpClientWithDynamicCert(trustedCert: X509Certificate?): OkHttpClient {
|
||||||
try {
|
try {
|
||||||
val defaultTmFactory = TrustManagerFactory.getInstance(
|
val defaultTmFactory = TrustManagerFactory.getInstance(
|
||||||
TrustManagerFactory.getDefaultAlgorithm()
|
TrustManagerFactory.getDefaultAlgorithm()
|
||||||
@@ -86,7 +93,7 @@ object ApiClient {
|
|||||||
).apply {
|
).apply {
|
||||||
init(keyStore)
|
init(keyStore)
|
||||||
}
|
}
|
||||||
tmf.trustManagers.first { it is X509TrustManager } as X509TrustManager
|
tmf.trustManagers.first { i -> i is X509TrustManager } as X509TrustManager
|
||||||
}
|
}
|
||||||
|
|
||||||
val combinedTm = object : X509TrustManager {
|
val combinedTm = object : X509TrustManager {
|
||||||
@@ -157,11 +164,22 @@ object ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createOkHttp(): OkHttpClient {
|
private fun createOkHttp(): OkHttpClient {
|
||||||
return if (cert == "")
|
return if (cert == "")
|
||||||
if (base.startsWith("abyss://"))
|
if (base.startsWith("abyss://"))
|
||||||
OkHttpClient
|
OkHttpClient
|
||||||
.Builder()
|
.Builder()
|
||||||
|
.cookieJar(object : CookieJar {
|
||||||
|
private val cookieStore = mutableMapOf<HttpUrl, List<Cookie>>()
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
|
cookieStore[url] = cookies
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
|
return cookieStore[url] ?: emptyList()
|
||||||
|
}
|
||||||
|
})
|
||||||
.proxy(
|
.proxy(
|
||||||
Proxy(
|
Proxy(
|
||||||
Proxy.Type.HTTP,
|
Proxy.Type.HTTP,
|
||||||
@@ -174,6 +192,17 @@ object ApiClient {
|
|||||||
else
|
else
|
||||||
OkHttpClient
|
OkHttpClient
|
||||||
.Builder()
|
.Builder()
|
||||||
|
.cookieJar(object : CookieJar {
|
||||||
|
private val cookieStore = mutableMapOf<HttpUrl, List<Cookie>>()
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
|
cookieStore[url] = cookies
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
|
return cookieStore[url] ?: emptyList()
|
||||||
|
}
|
||||||
|
})
|
||||||
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
|
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
|
||||||
.eventListener(dnsEventListener)
|
.eventListener(dnsEventListener)
|
||||||
.build()
|
.build()
|
||||||
@@ -183,20 +212,22 @@ object ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createRetrofit(): Retrofit {
|
private fun createRetrofit(): Retrofit {
|
||||||
val okHttpClient = createOkHttp()
|
client = createOkHttp()
|
||||||
val b = replaceAbyssProtocol(base)
|
val b = replaceAbyssProtocol(base)
|
||||||
|
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.baseUrl(b)
|
.baseUrl(b)
|
||||||
.client(okHttpClient)
|
.client(client!!)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var client: OkHttpClient? = null
|
||||||
var api: ApiInterface? = null
|
var api: ApiInterface? = null
|
||||||
|
|
||||||
|
fun getClient() = client!!
|
||||||
|
|
||||||
suspend fun apply(context: Context, urls: String, crt: String): String? {
|
suspend fun apply(context: Context, urls: String, crt: String): String? {
|
||||||
try {
|
try {
|
||||||
val urlList = urls.split(";").map { it.trim() }
|
val urlList = urls.split(";").map { it.trim() }
|
||||||
@@ -219,7 +250,7 @@ object ApiClient {
|
|||||||
base = selectedUrl
|
base = selectedUrl
|
||||||
withContext(Dispatchers.IO)
|
withContext(Dispatchers.IO)
|
||||||
{
|
{
|
||||||
(context as AetherApp).abyssService?.proxy?.config(ApiClient.getBase().toUri().host!!, 4096)
|
(context as AetherApp).abyssService?.proxy?.config(getBase().toUri().host!!, 4096)
|
||||||
}
|
}
|
||||||
api = createRetrofit().create(ApiInterface::class.java)
|
api = createRetrofit().create(ApiInterface::class.java)
|
||||||
|
|
||||||
@@ -228,7 +259,7 @@ object ApiClient {
|
|||||||
Log.i("Delay Analyze", "Abyss Hello: ${h.string()}")
|
Log.i("Delay Analyze", "Abyss Hello: ${h.string()}")
|
||||||
|
|
||||||
return base
|
return base
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
api = null
|
api = null
|
||||||
base = ""
|
base = ""
|
||||||
domain = ""
|
domain = ""
|
||||||
@@ -241,7 +272,7 @@ object ApiClient {
|
|||||||
return@withContext try {
|
return@withContext try {
|
||||||
val address = InetAddress.getByName(host)
|
val address = InetAddress.getByName(host)
|
||||||
address.isReachable(200)
|
address.isReachable(200)
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ import java.lang.reflect.Proxy
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.Signature
|
import java.security.Signature
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
object AuthManager {
|
@Singleton
|
||||||
|
class AuthManager @Inject constructor(
|
||||||
|
private val apiClient: ApiClient
|
||||||
|
) {
|
||||||
suspend fun fetchToken(username: String, privateKey: String): String? {
|
suspend fun fetchToken(username: String, privateKey: String): String? {
|
||||||
val api = ApiClient.api
|
val api = apiClient.api
|
||||||
var challengeBase64 = ""
|
var challengeBase64 = ""
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
|||||||
@@ -1,48 +1,37 @@
|
|||||||
package com.acitelight.aether.service
|
package com.acitelight.aether.service
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import com.acitelight.aether.Screen
|
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
|
||||||
import com.tonyodev.fetch2.Download
|
import com.tonyodev.fetch2.Download
|
||||||
import com.tonyodev.fetch2.Fetch
|
import com.tonyodev.fetch2.Fetch
|
||||||
import com.tonyodev.fetch2.FetchConfiguration
|
import com.tonyodev.fetch2.FetchConfiguration
|
||||||
import com.tonyodev.fetch2.FetchListener
|
import com.tonyodev.fetch2.FetchListener
|
||||||
import com.tonyodev.fetch2.Request
|
import com.tonyodev.fetch2.Request
|
||||||
import com.tonyodev.fetch2.Status
|
|
||||||
import com.tonyodev.fetch2core.Extras
|
import com.tonyodev.fetch2core.Extras
|
||||||
import com.tonyodev.fetch2okhttp.OkHttpDownloader
|
import com.tonyodev.fetch2okhttp.OkHttpDownloader
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class FetchManager @Inject constructor(
|
class FetchManager @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context,
|
||||||
|
private val apiClient: ApiClient
|
||||||
) {
|
) {
|
||||||
private var fetch: Fetch? = null
|
private var fetch: Fetch? = null
|
||||||
private var listener: FetchListener? = null
|
private var listener: FetchListener? = null
|
||||||
private var client: OkHttpClient? = null
|
|
||||||
val configured = MutableStateFlow(false)
|
val configured = MutableStateFlow(false)
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
client = createOkHttp()
|
|
||||||
val fetchConfiguration = FetchConfiguration.Builder(context)
|
val fetchConfiguration = FetchConfiguration.Builder(context)
|
||||||
.setDownloadConcurrentLimit(8)
|
.setDownloadConcurrentLimit(8)
|
||||||
.setHttpDownloader(OkHttpDownloader(client))
|
.setHttpDownloader(OkHttpDownloader(apiClient.getClient()))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fetch = Fetch.Impl.getInstance(fetchConfiguration)
|
fetch = Fetch.Impl.getInstance(fetchConfiguration)
|
||||||
@@ -65,12 +54,6 @@ class FetchManager @Inject constructor(
|
|||||||
listener = null
|
listener = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// query downloads
|
|
||||||
suspend fun getAllDownloads(callback: (List<Download>) -> Unit) {
|
|
||||||
configured.filter { it }.first()
|
|
||||||
fetch?.getDownloads { list -> callback(list) } ?: callback(emptyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getAllDownloadsAsync(): List<Download> {
|
suspend fun getAllDownloadsAsync(): List<Download> {
|
||||||
configured.filter { it }.first()
|
configured.filter { it }.first()
|
||||||
val completed = MutableStateFlow(false)
|
val completed = MutableStateFlow(false)
|
||||||
@@ -140,7 +123,7 @@ class FetchManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val requests = mutableListOf(
|
val requests = mutableListOf(
|
||||||
Request(video.getVideo(), videoPath.path).apply {
|
Request(video.getVideo(apiClient), videoPath.path).apply {
|
||||||
extras = Extras(
|
extras = Extras(
|
||||||
mapOf(
|
mapOf(
|
||||||
"name" to video.video.name,
|
"name" to video.video.name,
|
||||||
@@ -150,7 +133,7 @@ class FetchManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Request(video.getCover(), coverPath.path).apply {
|
Request(video.getCover(apiClient), coverPath.path).apply {
|
||||||
extras = Extras(
|
extras = Extras(
|
||||||
mapOf(
|
mapOf(
|
||||||
"name" to video.video.name,
|
"name" to video.video.name,
|
||||||
@@ -160,7 +143,7 @@ class FetchManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Request(video.getSubtitle(), subtitlePath.path).apply {
|
Request(video.getSubtitle(apiClient), subtitlePath.path).apply {
|
||||||
extras = Extras(
|
extras = Extras(
|
||||||
mapOf(
|
mapOf(
|
||||||
"name" to video.video.name,
|
"name" to video.video.name,
|
||||||
@@ -171,7 +154,7 @@ class FetchManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
for (p in video.getGallery()) {
|
for (p in video.getGallery(apiClient)) {
|
||||||
requests.add(
|
requests.add(
|
||||||
Request(p.url, File(
|
Request(p.url, File(
|
||||||
context.getExternalFilesDir(null),
|
context.getExternalFilesDir(null),
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class MediaManager @Inject constructor(
|
class MediaManager @Inject constructor(
|
||||||
val fetchManager: FetchManager,
|
val fetchManager: FetchManager,
|
||||||
@ApplicationContext val context: Context
|
@ApplicationContext val context: Context,
|
||||||
|
private val apiClient: ApiClient
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var token: String = "null"
|
var token: String = "null"
|
||||||
@@ -25,7 +26,7 @@ class MediaManager @Inject constructor(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
val j = ApiClient.api!!.getVideoClasses(token)
|
val j = apiClient.api!!.getVideoClasses(token)
|
||||||
return j.toList()
|
return j.toList()
|
||||||
}catch(_: Exception)
|
}catch(_: Exception)
|
||||||
{
|
{
|
||||||
@@ -37,7 +38,7 @@ class MediaManager @Inject constructor(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
val j = ApiClient.api!!.queryVideoClasses(klass, token)
|
val j = apiClient.api!!.queryVideoClasses(klass, token)
|
||||||
return j.toList()
|
return j.toList()
|
||||||
}catch(_: Exception)
|
}catch(_: Exception)
|
||||||
{
|
{
|
||||||
@@ -57,7 +58,7 @@ class MediaManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val j = ApiClient.api!!.queryVideo(klass, id, token)
|
val j = apiClient.api!!.queryVideo(klass, id, token)
|
||||||
return Video(klass = klass, id = id, token=token, isLocal = false, localBase = "", video = j)
|
return Video(klass = klass, id = id, token=token, isLocal = false, localBase = "", video = j)
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
@@ -83,7 +84,7 @@ class MediaManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val j = ApiClient.api!!.queryVideo(klass, id, token)
|
val j = apiClient.api!!.queryVideo(klass, id, token)
|
||||||
return Video(klass = klass, id = id, token=token, isLocal = false, localBase = "", video = j)
|
return Video(klass = klass, id = id, token=token, isLocal = false, localBase = "", video = j)
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
@@ -133,7 +134,7 @@ class MediaManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val remoteVideos = if (remoteIds.isNotEmpty()) {
|
val remoteVideos = if (remoteIds.isNotEmpty()) {
|
||||||
val j = ApiClient.api!!.queryVideoBulk(klass, remoteIds, token)
|
val j = apiClient.api!!.queryVideoBulk(klass, remoteIds, token)
|
||||||
j.zip(remoteIds).map {
|
j.zip(remoteIds).map {
|
||||||
Video(
|
Video(
|
||||||
klass = klass,
|
klass = klass,
|
||||||
@@ -157,7 +158,7 @@ class MediaManager @Inject constructor(
|
|||||||
suspend fun listComics() : List<String>
|
suspend fun listComics() : List<String>
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
val j = ApiClient.api!!.getComics(token)
|
val j = apiClient.api!!.getComics(token)
|
||||||
return j
|
return j
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
@@ -168,7 +169,7 @@ class MediaManager @Inject constructor(
|
|||||||
suspend fun queryComicInfoSingle(id: String) : Comic?
|
suspend fun queryComicInfoSingle(id: String) : Comic?
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
val j = ApiClient.api!!.queryComicInfo(id, token)
|
val j = apiClient.api!!.queryComicInfo(id, token)
|
||||||
return Comic(id = id, comic = j, token = token)
|
return Comic(id = id, comic = j, token = token)
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
@@ -179,7 +180,7 @@ class MediaManager @Inject constructor(
|
|||||||
suspend fun queryComicInfoBulk(id: List<String>) : List<Comic>?
|
suspend fun queryComicInfoBulk(id: List<String>) : List<Comic>?
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
val j = ApiClient.api!!.queryComicInfoBulk(id, token)
|
val j = apiClient.api!!.queryComicInfoBulk(id, token)
|
||||||
return j.zip(id).map { Comic(id = it.second, comic = it.first, token = token) }
|
return j.zip(id).map { Comic(id = it.second, comic = it.first, token = token) }
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
@@ -190,7 +191,7 @@ class MediaManager @Inject constructor(
|
|||||||
suspend fun postBookmark(id: String, bookMark: BookMark): Boolean
|
suspend fun postBookmark(id: String, bookMark: BookMark): Boolean
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
ApiClient.api!!.postBookmark(id, token, bookMark)
|
apiClient.api!!.postBookmark(id, token, bookMark)
|
||||||
return true
|
return true
|
||||||
}catch (_: Exception)
|
}catch (_: Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
@@ -157,7 +156,6 @@ fun ComicGridView(
|
|||||||
comicGridViewModel.updateProcess(comicId.hexToString())
|
comicGridViewModel.updateProcess(comicId.hexToString())
|
||||||
{
|
{
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
val k = comic!!.getPageChapterIndex(record!!.position)
|
|
||||||
val route = "comic_page_route/${comic!!.id.toHex()}/${
|
val route = "comic_page_route/${comic!!.id.toHex()}/${
|
||||||
record!!.position
|
record!!.position
|
||||||
}"
|
}"
|
||||||
@@ -248,7 +246,7 @@ fun ChapterCard(
|
|||||||
{
|
{
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(comic.getPage(c.page))
|
.data(comic.getPage(c.page, comicGridViewModel.apiClient))
|
||||||
.memoryCacheKey("${comic.id}/${c.page}")
|
.memoryCacheKey("${comic.id}/${c.page}")
|
||||||
.diskCacheKey("${comic.id}/${c.page}")
|
.diskCacheKey("${comic.id}/${c.page}")
|
||||||
.build(),
|
.build(),
|
||||||
@@ -300,13 +298,13 @@ fun ChapterCard(
|
|||||||
.padding(horizontal = 6.dp),
|
.padding(horizontal = 6.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
val route =
|
val route =
|
||||||
"comic_page_route/${"${comic.id}".toHex()}/${comic.getPageIndex(r)}"
|
"comic_page_route/${comic.id.toHex()}/${comic.getPageIndex(r)}"
|
||||||
navController.navigate(route)
|
navController.navigate(route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(comic.getPage(r))
|
.data(comic.getPage(r, comicGridViewModel.apiClient))
|
||||||
.memoryCacheKey("${comic.id}/${r}")
|
.memoryCacheKey("${comic.id}/${r}")
|
||||||
.diskCacheKey("${comic.id}/${r}")
|
.diskCacheKey("${comic.id}/${r}")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ fun ComicPageView(
|
|||||||
) { page ->
|
) { page ->
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(it.getPage(page))
|
.data(it.getPage(page, comicPageViewModel.apiClient))
|
||||||
.memoryCacheKey("${it.id}/${page}")
|
.memoryCacheKey("${it.id}/${page}")
|
||||||
.diskCacheKey("${it.id}/${page}")
|
.diskCacheKey("${it.id}/${page}")
|
||||||
.build(),
|
.build(),
|
||||||
@@ -252,7 +252,7 @@ fun ComicPageView(
|
|||||||
{
|
{
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(it.getPage(r))
|
.data(it.getPage(r, comicPageViewModel.apiClient))
|
||||||
.memoryCacheKey("${it.id}/${r}")
|
.memoryCacheKey("${it.id}/${r}")
|
||||||
.diskCacheKey("${it.id}/${r}")
|
.diskCacheKey("${it.id}/${r}")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package com.acitelight.aether.view
|
package com.acitelight.aether.view
|
||||||
|
|
||||||
import android.nfc.Tag
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -16,10 +14,6 @@ import androidx.compose.foundation.layout.heightIn
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
|
||||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||||
@@ -28,42 +22,28 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Tab
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
import com.acitelight.aether.model.Video
|
|
||||||
import com.acitelight.aether.viewModel.VideoScreenViewModel
|
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.layout.Layout
|
import androidx.compose.ui.layout.Layout
|
||||||
import androidx.compose.ui.layout.Placeable
|
import androidx.compose.ui.layout.Placeable
|
||||||
import androidx.compose.ui.modifier.modifierLocalOf
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.acitelight.aether.Global
|
|
||||||
import com.acitelight.aether.model.Comic
|
import com.acitelight.aether.model.Comic
|
||||||
import com.acitelight.aether.viewModel.ComicScreenViewModel
|
import com.acitelight.aether.viewModel.ComicScreenViewModel
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VariableGrid(
|
fun VariableGrid(
|
||||||
@@ -223,7 +203,7 @@ fun ComicCard(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
onClick = {
|
onClick = {
|
||||||
val route = "comic_grid_route/${"${comic.id}".toHex()}"
|
val route = "comic_grid_route/${comic.id.toHex()}"
|
||||||
navController.navigate(route)
|
navController.navigate(route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -234,7 +214,7 @@ fun ComicCard(
|
|||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(comic.getPage(0))
|
.data(comic.getPage(0, comicScreenViewModel.apiClient))
|
||||||
.memoryCacheKey("${comic.id}/${0}")
|
.memoryCacheKey("${comic.id}/${0}")
|
||||||
.diskCacheKey("${comic.id}/${0}")
|
.diskCacheKey("${comic.id}/${0}")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.acitelight.aether.view
|
package com.acitelight.aether.view
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -37,35 +36,34 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.acitelight.aether.Global.updateRelate
|
import com.acitelight.aether.Global.updateRelate
|
||||||
import com.acitelight.aether.model.Comic
|
import com.acitelight.aether.model.Comic
|
||||||
import com.acitelight.aether.viewModel.ComicScreenViewModel
|
|
||||||
import com.acitelight.aether.viewModel.HomeScreenViewModel
|
import com.acitelight.aether.viewModel.HomeScreenViewModel
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
homeScreenViewModel: HomeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<HomeScreenViewModel>(),
|
homeScreenViewModel: HomeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<HomeScreenViewModel>(),
|
||||||
navController: NavHostController)
|
navController: NavHostController
|
||||||
{
|
) {
|
||||||
val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 })
|
val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 })
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
modifier = Modifier.fillMaxSize().background(Color.Black)
|
modifier = Modifier
|
||||||
){
|
.fillMaxSize()
|
||||||
p ->
|
.background(Color.Black)
|
||||||
if(p == 0)
|
) { p ->
|
||||||
{
|
if (p == 0) {
|
||||||
Column(Modifier.fillMaxHeight()) {
|
Column(Modifier.fillMaxHeight()) {
|
||||||
Text(
|
Text(
|
||||||
text = "Videos",
|
text = "Videos",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
modifier = Modifier.padding(8.dp).align(Alignment.Start)
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.align(Alignment.Start)
|
||||||
)
|
)
|
||||||
|
|
||||||
HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color)
|
HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color)
|
||||||
@@ -73,27 +71,38 @@ fun HomeScreen(
|
|||||||
LazyColumn(modifier = Modifier.fillMaxWidth())
|
LazyColumn(modifier = Modifier.fillMaxWidth())
|
||||||
{
|
{
|
||||||
items(homeScreenViewModel.recentManager.recentVideo)
|
items(homeScreenViewModel.recentManager.recentVideo)
|
||||||
{
|
{ i ->
|
||||||
i ->
|
|
||||||
MiniVideoCard(
|
MiniVideoCard(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 12.dp),
|
.padding(horizontal = 12.dp),
|
||||||
i,
|
i,
|
||||||
{
|
apiClient = homeScreenViewModel.apiClient,
|
||||||
updateRelate(homeScreenViewModel.recentManager.recentVideo, i)
|
imageLoader = homeScreenViewModel.imageLoader!!
|
||||||
|
)
|
||||||
|
{
|
||||||
|
updateRelate(homeScreenViewModel.recentManager.recentVideo, i)
|
||||||
|
|
||||||
val playList = mutableListOf<String>()
|
val playList = mutableListOf<String>()
|
||||||
val fv = homeScreenViewModel.videoLibrary.classesMap.map { it.value }.flatten()
|
val fv = homeScreenViewModel.videoLibrary.classesMap.map { it.value }
|
||||||
|
.flatten()
|
||||||
|
|
||||||
val group = fv.filter { it.klass == i.klass && it.video.group == i.video.group }
|
val group =
|
||||||
for (i in group) {
|
fv.filter { it.klass == i.klass && it.video.group == i.video.group }
|
||||||
playList.add("${i.klass}/${i.id}")
|
for (i in group) {
|
||||||
}
|
playList.add("${i.klass}/${i.id}")
|
||||||
|
}
|
||||||
|
|
||||||
val route = "video_player_route/${(playList.joinToString(",") + "|${i.id}").toHex()}"
|
val route =
|
||||||
navController.navigate(route)
|
"video_player_route/${(playList.joinToString(",") + "|${i.id}").toHex()}"
|
||||||
}, homeScreenViewModel.imageLoader!!)
|
navController.navigate(route)
|
||||||
HorizontalDivider(Modifier.padding(vertical = 8.dp).alpha(0.4f), 1.dp, DividerDefaults.color)
|
}
|
||||||
|
HorizontalDivider(
|
||||||
|
Modifier
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.alpha(0.4f),
|
||||||
|
1.dp,
|
||||||
|
DividerDefaults.color
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +111,9 @@ fun HomeScreen(
|
|||||||
Text(
|
Text(
|
||||||
text = "Comics",
|
text = "Comics",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
modifier = Modifier.padding(8.dp).align(Alignment.Start)
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.align(Alignment.Start)
|
||||||
)
|
)
|
||||||
|
|
||||||
HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color)
|
HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color)
|
||||||
@@ -115,8 +126,7 @@ fun HomeScreen(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
items(homeScreenViewModel.recentManager.recentComic)
|
items(homeScreenViewModel.recentManager.recentComic)
|
||||||
{
|
{ comic ->
|
||||||
comic ->
|
|
||||||
ComicCardRecent(comic, navController, homeScreenViewModel)
|
ComicCardRecent(comic, navController, homeScreenViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +148,7 @@ fun ComicCardRecent(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
onClick = {
|
onClick = {
|
||||||
val route = "comic_grid_route/${"${comic.id}".toHex()}"
|
val route = "comic_grid_route/${comic.id.toHex()}"
|
||||||
navController.navigate(route)
|
navController.navigate(route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -149,7 +159,7 @@ fun ComicCardRecent(
|
|||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(comic.getPage(0))
|
.data(comic.getPage(0, homeScreenViewModel.apiClient))
|
||||||
.memoryCacheKey("${comic.id}/${0}")
|
.memoryCacheKey("${comic.id}/${0}")
|
||||||
.diskCacheKey("${comic.id}/${0}")
|
.diskCacheKey("${comic.id}/${0}")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.acitelight.aether.view
|
package com.acitelight.aether.view
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -17,7 +15,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Key
|
import androidx.compose.material.icons.filled.Key
|
||||||
import androidx.compose.material.icons.filled.Link
|
import androidx.compose.material.icons.filled.Link
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.Security
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
@@ -31,25 +28,16 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.acitelight.aether.service.ApiClient.api
|
|
||||||
import com.acitelight.aether.viewModel.MeScreenViewModel
|
import com.acitelight.aether.viewModel.MeScreenViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<MeScreenViewModel>()) {
|
fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<MeScreenViewModel>()) {
|
||||||
val context = LocalContext.current
|
var username by meScreenViewModel.username
|
||||||
var username by meScreenViewModel.username;
|
var privateKey by meScreenViewModel.privateKey
|
||||||
var privateKey by meScreenViewModel.privateKey;
|
|
||||||
var url by meScreenViewModel.url
|
var url by meScreenViewModel.url
|
||||||
var cert by meScreenViewModel.cert
|
var cert by meScreenViewModel.cert
|
||||||
|
|
||||||
@@ -204,19 +192,6 @@ fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.view
|
|||||||
) {
|
) {
|
||||||
Text("Save")
|
Text("Save")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
meScreenViewModel.viewModelScope.launch {
|
|
||||||
Log.i("Delay Analyze", "Start Abyss Hello")
|
|
||||||
val h = api!!.hello()
|
|
||||||
Log.i("Delay Analyze", "Abyss Hello: ${h.string()}")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.weight(0.5f).padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text("Ping")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ import coil3.ImageLoader
|
|||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
|
import com.acitelight.aether.service.ApiClient
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MiniPlaylistCard(modifier: Modifier, video: Video, imageLoader: ImageLoader, selected: Boolean, onClick: () -> Unit) {
|
fun MiniPlaylistCard(modifier: Modifier, video: Video, imageLoader: ImageLoader, selected: Boolean, apiClient: ApiClient, onClick: () -> Unit) {
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.height(80.dp)
|
.height(80.dp)
|
||||||
@@ -58,7 +58,7 @@ fun MiniPlaylistCard(modifier: Modifier, video: Video, imageLoader: ImageLoader,
|
|||||||
{
|
{
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(video.getCover())
|
.data(video.getCover(apiClient))
|
||||||
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.diskCacheKey("${video.klass}/${video.id}/cover")
|
.diskCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.listener(
|
.listener(
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ import coil3.ImageLoader
|
|||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
|
import com.acitelight.aether.service.ApiClient
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MiniVideoCard(modifier: Modifier, video: Video, onClick: () -> Unit, imageLoader: ImageLoader) {
|
fun MiniVideoCard(modifier: Modifier, video: Video, imageLoader: ImageLoader, apiClient: ApiClient, onClick: () -> Unit) {
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.height(80.dp)
|
.height(80.dp)
|
||||||
@@ -49,7 +50,7 @@ fun MiniVideoCard(modifier: Modifier, video: Video, onClick: () -> Unit, imageLo
|
|||||||
{
|
{
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(video.getCover())
|
.data(video.getCover(apiClient))
|
||||||
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.diskCacheKey("${video.klass}/${video.id}/cover")
|
.diskCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.listener(
|
.listener(
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@@ -40,7 +39,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.room.util.TableInfo
|
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.acitelight.aether.Global.updateRelate
|
import com.acitelight.aether.Global.updateRelate
|
||||||
@@ -120,7 +118,7 @@ fun TransmissionScreen(
|
|||||||
for (i in downloadToGroup(
|
for (i in downloadToGroup(
|
||||||
item,
|
item,
|
||||||
downloads
|
downloads
|
||||||
)) transmissionScreenViewModel.delete(i.id, true)
|
)) transmissionScreenViewModel.delete(i.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -247,7 +245,7 @@ private fun VideoDownloadCard(
|
|||||||
else {
|
else {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(video.getCover())
|
.data(video.getCover(viewModel.apiClient))
|
||||||
.memoryCacheKey("${model.klass}/${model.vid}/cover")
|
.memoryCacheKey("${model.klass}/${model.vid}/cover")
|
||||||
.diskCacheKey("${model.klass}/${model.vid}/cover")
|
.diskCacheKey("${model.klass}/${model.vid}/cover")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) {
|
|||||||
LazyColumn(contentPadding = PaddingValues(vertical = 4.dp)) {
|
LazyColumn(contentPadding = PaddingValues(vertical = 4.dp)) {
|
||||||
items(videoPlayerViewModel.videos) { item ->
|
items(videoPlayerViewModel.videos) { item ->
|
||||||
MiniPlaylistCard(Modifier.padding(4.dp), video = item, imageLoader = videoPlayerViewModel.imageLoader!!,
|
MiniPlaylistCard(Modifier.padding(4.dp), video = item, imageLoader = videoPlayerViewModel.imageLoader!!,
|
||||||
selected = id == item.id)
|
selected = id == item.id, apiClient = videoPlayerViewModel.apiClient)
|
||||||
{
|
{
|
||||||
if (name == item.video.name)
|
if (name == item.video.name)
|
||||||
return@MiniPlaylistCard
|
return@MiniPlaylistCard
|
||||||
|
|||||||
@@ -245,22 +245,25 @@ fun VideoPlayerPortal(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 12.dp),
|
.padding(horizontal = 12.dp),
|
||||||
i,
|
i,
|
||||||
{
|
apiClient = videoPlayerViewModel.apiClient,
|
||||||
videoPlayerViewModel.isPlaying = false
|
imageLoader = videoPlayerViewModel.imageLoader!!
|
||||||
videoPlayerViewModel.player?.pause()
|
) {
|
||||||
|
videoPlayerViewModel.isPlaying = false
|
||||||
|
videoPlayerViewModel.player?.pause()
|
||||||
|
|
||||||
val playList = mutableListOf<String>()
|
val playList = mutableListOf<String>()
|
||||||
val fv = videoPlayerViewModel.videoLibrary.classesMap.map { it.value }.flatten()
|
val fv =
|
||||||
|
videoPlayerViewModel.videoLibrary.classesMap.map { it.value }.flatten()
|
||||||
|
|
||||||
val group = fv.filter { it.klass == i.klass && it.video.group == i.video.group }
|
val group =
|
||||||
for (i in group) {
|
fv.filter { it.klass == i.klass && it.video.group == i.video.group }
|
||||||
playList.add("${i.klass}/${i.id}")
|
for (i in group) {
|
||||||
}
|
playList.add("${i.klass}/${i.id}")
|
||||||
|
}
|
||||||
|
|
||||||
val route = "video_player_route/${playList.joinToString(",").toHex()}"
|
val route = "video_player_route/${playList.joinToString(",").toHex()}"
|
||||||
navController.navigate(route)
|
navController.navigate(route)
|
||||||
}, videoPlayerViewModel.imageLoader!!
|
}
|
||||||
)
|
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(vertical = 8.dp)
|
.padding(vertical = 8.dp)
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ fun VideoCard(
|
|||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
.data(video.getCover())
|
.data(video.getCover(videoScreenViewModel.apiClient))
|
||||||
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
.memoryCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.diskCacheKey("${video.klass}/${video.id}/cover")
|
.diskCacheKey("${video.klass}/${video.id}/cover")
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package com.acitelight.aether.viewModel
|
package com.acitelight.aether.viewModel
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
@@ -14,7 +11,7 @@ import com.acitelight.aether.model.BookMark
|
|||||||
import com.acitelight.aether.model.Comic
|
import com.acitelight.aether.model.Comic
|
||||||
import com.acitelight.aether.model.ComicRecord
|
import com.acitelight.aether.model.ComicRecord
|
||||||
import com.acitelight.aether.model.ComicRecordDatabase
|
import com.acitelight.aether.model.ComicRecordDatabase
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import com.acitelight.aether.service.RecentManager
|
import com.acitelight.aether.service.RecentManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -26,7 +23,8 @@ import javax.inject.Inject
|
|||||||
class ComicGridViewModel @Inject constructor(
|
class ComicGridViewModel @Inject constructor(
|
||||||
@ApplicationContext val context: Context,
|
@ApplicationContext val context: Context,
|
||||||
val mediaManager: MediaManager,
|
val mediaManager: MediaManager,
|
||||||
val recentManager: RecentManager
|
val recentManager: RecentManager,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel()
|
) : ViewModel()
|
||||||
{
|
{
|
||||||
var imageLoader: ImageLoader? = null
|
var imageLoader: ImageLoader? = null
|
||||||
@@ -38,7 +36,7 @@ class ComicGridViewModel @Inject constructor(
|
|||||||
init {
|
init {
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
db = try{
|
db = try{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
|||||||
import com.acitelight.aether.model.Comic
|
import com.acitelight.aether.model.Comic
|
||||||
import com.acitelight.aether.model.ComicRecord
|
import com.acitelight.aether.model.ComicRecord
|
||||||
import com.acitelight.aether.model.ComicRecordDatabase
|
import com.acitelight.aether.model.ComicRecordDatabase
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
@@ -28,7 +28,8 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ComicPageViewModel @Inject constructor(
|
class ComicPageViewModel @Inject constructor(
|
||||||
val mediaManager: MediaManager,
|
val mediaManager: MediaManager,
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel()
|
) : ViewModel()
|
||||||
{
|
{
|
||||||
var imageLoader: ImageLoader? = null
|
var imageLoader: ImageLoader? = null
|
||||||
@@ -43,7 +44,7 @@ class ComicPageViewModel @Inject constructor(
|
|||||||
init{
|
init{
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
listState = LazyListState(0, 0)
|
listState = LazyListState(0, 0)
|
||||||
|
|||||||
@@ -1,28 +1,24 @@
|
|||||||
package com.acitelight.aether.viewModel
|
package com.acitelight.aether.viewModel
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import com.acitelight.aether.model.Comic
|
import com.acitelight.aether.model.Comic
|
||||||
import com.acitelight.aether.model.ComicResponse
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ComicScreenViewModel @Inject constructor(
|
class ComicScreenViewModel @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
val mediaManager: MediaManager
|
val mediaManager: MediaManager,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var imageLoader: ImageLoader? = null;
|
var imageLoader: ImageLoader? = null;
|
||||||
@@ -54,7 +50,7 @@ class ComicScreenViewModel @Inject constructor(
|
|||||||
init {
|
init {
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.RecentManager
|
import com.acitelight.aether.service.RecentManager
|
||||||
import com.acitelight.aether.service.VideoLibrary
|
import com.acitelight.aether.service.VideoLibrary
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -19,6 +19,7 @@ class HomeScreenViewModel @Inject constructor(
|
|||||||
val recentManager: RecentManager,
|
val recentManager: RecentManager,
|
||||||
@ApplicationContext val context: Context,
|
@ApplicationContext val context: Context,
|
||||||
val videoLibrary: VideoLibrary,
|
val videoLibrary: VideoLibrary,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel()
|
) : ViewModel()
|
||||||
{
|
{
|
||||||
var imageLoader: ImageLoader? = null
|
var imageLoader: ImageLoader? = null
|
||||||
@@ -26,7 +27,7 @@ class HomeScreenViewModel @Inject constructor(
|
|||||||
init{
|
init{
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
@@ -1,45 +1,37 @@
|
|||||||
package com.acitelight.aether.viewModel
|
package com.acitelight.aether.viewModel
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.datastore.preferences.core.edit
|
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.acitelight.aether.AetherApp
|
import com.acitelight.aether.AetherApp
|
||||||
import com.acitelight.aether.Global
|
import com.acitelight.aether.Global
|
||||||
import com.acitelight.aether.dataStore
|
|
||||||
import com.acitelight.aether.model.Video
|
|
||||||
import com.acitelight.aether.service.ApiClient
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.AuthManager
|
import com.acitelight.aether.service.AuthManager
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import kotlinx.coroutines.flow.Flow
|
import com.acitelight.aether.service.SettingsDataStoreManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
import com.acitelight.aether.service.*
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MeScreenViewModel @Inject constructor(
|
class MeScreenViewModel @Inject constructor(
|
||||||
private val settingsDataStoreManager: SettingsDataStoreManager,
|
private val settingsDataStoreManager: SettingsDataStoreManager,
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
val mediaManager: MediaManager
|
val mediaManager: MediaManager,
|
||||||
|
private val apiClient: ApiClient,
|
||||||
|
private val authManager: AuthManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val username = mutableStateOf("");
|
val username = mutableStateOf("")
|
||||||
val privateKey = mutableStateOf("")
|
val privateKey = mutableStateOf("")
|
||||||
val url = mutableStateOf("");
|
val url = mutableStateOf("")
|
||||||
val cert = mutableStateOf("")
|
val cert = mutableStateOf("")
|
||||||
|
|
||||||
val uss = settingsDataStoreManager.useSelfSignedFlow
|
val uss = settingsDataStoreManager.useSelfSignedFlow
|
||||||
@@ -54,10 +46,10 @@ class MeScreenViewModel @Inject constructor(
|
|||||||
if(username.value=="" || privateKey.value=="" || url.value=="") return@launch
|
if(username.value=="" || privateKey.value=="" || url.value=="") return@launch
|
||||||
|
|
||||||
try{
|
try{
|
||||||
val usedUrl = ApiClient.apply(context, url.value, if(uss.first()) cert.value else "")
|
apiClient.apply(context, url.value, if(uss.first()) cert.value else "")
|
||||||
|
|
||||||
if (mediaManager.token == "null")
|
if (mediaManager.token == "null")
|
||||||
mediaManager.token = AuthManager.fetchToken(
|
mediaManager.token = authManager.fetchToken(
|
||||||
username.value,
|
username.value,
|
||||||
settingsDataStoreManager.privateKeyFlow.first()
|
settingsDataStoreManager.privateKeyFlow.first()
|
||||||
)!!
|
)!!
|
||||||
@@ -65,7 +57,7 @@ class MeScreenViewModel @Inject constructor(
|
|||||||
Global.loggedIn = true
|
Global.loggedIn = true
|
||||||
withContext(Dispatchers.IO)
|
withContext(Dispatchers.IO)
|
||||||
{
|
{
|
||||||
(context as AetherApp).abyssService?.proxy?.config(ApiClient.getBase().toUri().host!!, 4096)
|
(context as AetherApp).abyssService?.proxy?.config(apiClient.getBase().toUri().host!!, 4096)
|
||||||
context.abyssService?.downloader?.init()
|
context.abyssService?.downloader?.init()
|
||||||
}
|
}
|
||||||
}catch(e: Exception)
|
}catch(e: Exception)
|
||||||
@@ -100,10 +92,10 @@ class MeScreenViewModel @Inject constructor(
|
|||||||
if (u == "" || p == "" || us == "") return@launch
|
if (u == "" || p == "" || us == "") return@launch
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val usedUrl = ApiClient.apply(context, u, if(uss.first()) c else "")
|
val usedUrl = apiClient.apply(context, u, if(uss.first()) c else "")
|
||||||
(context as AetherApp).abyssService?.proxy?.config(ApiClient.getBase().toUri().host!!, 4096)
|
(context as AetherApp).abyssService?.proxy?.config(apiClient.getBase().toUri().host!!, 4096)
|
||||||
context.abyssService?.downloader?.init()
|
context.abyssService?.downloader?.init()
|
||||||
mediaManager.token = AuthManager.fetchToken(
|
mediaManager.token = authManager.fetchToken(
|
||||||
us,
|
us,
|
||||||
p
|
p
|
||||||
)!!
|
)!!
|
||||||
@@ -133,7 +125,7 @@ class MeScreenViewModel @Inject constructor(
|
|||||||
if (u == "" || p == "" || ur == "") return@launch
|
if (u == "" || p == "" || ur == "") return@launch
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mediaManager.token = AuthManager.fetchToken(
|
mediaManager.token = authManager.fetchToken(
|
||||||
u,
|
u,
|
||||||
p
|
p
|
||||||
)!!
|
)!!
|
||||||
@@ -141,7 +133,7 @@ class MeScreenViewModel @Inject constructor(
|
|||||||
Global.loggedIn = true
|
Global.loggedIn = true
|
||||||
withContext(Dispatchers.IO)
|
withContext(Dispatchers.IO)
|
||||||
{
|
{
|
||||||
(context as AetherApp).abyssService?.proxy?.config(ApiClient.getBase().toUri().host!!, 4096)
|
(context as AetherApp).abyssService?.proxy?.config(apiClient.getBase().toUri().host!!, 4096)
|
||||||
context.abyssService?.downloader?.init()
|
context.abyssService?.downloader?.init()
|
||||||
}
|
}
|
||||||
Toast.makeText(context, "Account Updated", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Account Updated", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import coil3.ImageLoader
|
|||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
import com.acitelight.aether.model.VideoDownloadItemState
|
import com.acitelight.aether.model.VideoDownloadItemState
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.FetchManager
|
import com.acitelight.aether.service.FetchManager
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import com.acitelight.aether.service.VideoLibrary
|
import com.acitelight.aether.service.VideoLibrary
|
||||||
@@ -20,7 +20,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -29,6 +28,7 @@ class TransmissionScreenViewModel @Inject constructor(
|
|||||||
@ApplicationContext val context: Context,
|
@ApplicationContext val context: Context,
|
||||||
val videoLibrary: VideoLibrary,
|
val videoLibrary: VideoLibrary,
|
||||||
val mediaManager: MediaManager,
|
val mediaManager: MediaManager,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var imageLoader: ImageLoader? = null
|
var imageLoader: ImageLoader? = null
|
||||||
val downloads: SnapshotStateList<VideoDownloadItemState> = mutableStateListOf()
|
val downloads: SnapshotStateList<VideoDownloadItemState> = mutableStateListOf()
|
||||||
@@ -205,7 +205,7 @@ class TransmissionScreenViewModel @Inject constructor(
|
|||||||
fun pause(id: Int) = fetchManager.pause(id)
|
fun pause(id: Int) = fetchManager.pause(id)
|
||||||
fun resume(id: Int) = fetchManager.resume(id)
|
fun resume(id: Int) = fetchManager.resume(id)
|
||||||
fun cancel(id: Int) = fetchManager.cancel(id)
|
fun cancel(id: Int) = fetchManager.cancel(id)
|
||||||
fun delete(id: Int, deleteFile: Boolean = true) {
|
fun delete(id: Int) {
|
||||||
fetchManager.delete(id) {
|
fetchManager.delete(id) {
|
||||||
viewModelScope.launch(Dispatchers.Main) { removeOnMain(id) }
|
viewModelScope.launch(Dispatchers.Main) { removeOnMain(id) }
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ class TransmissionScreenViewModel @Inject constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
imageLoader = ImageLoader.Builder(context).components {
|
imageLoader = ImageLoader.Builder(context).components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
@@ -8,29 +8,34 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableLongStateOf
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.PlaybackException
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.Player.STATE_READY
|
import androidx.media3.common.Player.STATE_READY
|
||||||
|
import androidx.media3.common.Tracks
|
||||||
import androidx.media3.common.text.Cue
|
import androidx.media3.common.text.Cue
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.media3.datasource.DefaultDataSource
|
||||||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
|
import com.acitelight.aether.model.KeyImage
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
import com.acitelight.aether.model.VideoQueryIndex
|
import com.acitelight.aether.model.VideoQueryIndex
|
||||||
import com.acitelight.aether.model.VideoRecord
|
import com.acitelight.aether.model.VideoRecord
|
||||||
import com.acitelight.aether.model.VideoRecordDatabase
|
import com.acitelight.aether.model.VideoRecordDatabase
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import com.acitelight.aether.service.RecentManager
|
import com.acitelight.aether.service.RecentManager
|
||||||
|
import com.acitelight.aether.service.VideoLibrary
|
||||||
import com.acitelight.aether.view.formatTime
|
import com.acitelight.aether.view.formatTime
|
||||||
import com.acitelight.aether.view.hexToString
|
import com.acitelight.aether.view.hexToString
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -38,20 +43,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.media3.common.Tracks
|
|
||||||
import androidx.media3.datasource.DefaultDataSource
|
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
|
||||||
import com.acitelight.aether.Global
|
|
||||||
import com.acitelight.aether.model.KeyImage
|
|
||||||
import com.acitelight.aether.service.VideoLibrary
|
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class VideoPlayerViewModel @Inject constructor(
|
class VideoPlayerViewModel @Inject constructor(
|
||||||
@@ -59,6 +56,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
val mediaManager: MediaManager,
|
val mediaManager: MediaManager,
|
||||||
val recentManager: RecentManager,
|
val recentManager: RecentManager,
|
||||||
val videoLibrary: VideoLibrary,
|
val videoLibrary: VideoLibrary,
|
||||||
|
val apiClient: ApiClient
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var showPlaylist by mutableStateOf(false)
|
var showPlaylist by mutableStateOf(false)
|
||||||
var isLandscape by mutableStateOf(false)
|
var isLandscape by mutableStateOf(false)
|
||||||
@@ -79,7 +77,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
var renderedFirst = false
|
var renderedFirst = false
|
||||||
var videos: List<Video> = listOf()
|
var videos: List<Video> = listOf()
|
||||||
|
|
||||||
private val httpDataSourceFactory = OkHttpDataSource.Factory(createOkHttp())
|
private val httpDataSourceFactory = OkHttpDataSource.Factory(apiClient.getClient())
|
||||||
private val defaultDataSourceFactory by lazy {
|
private val defaultDataSourceFactory by lazy {
|
||||||
DefaultDataSource.Factory(
|
DefaultDataSource.Factory(
|
||||||
context,
|
context,
|
||||||
@@ -117,7 +115,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
|
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -156,7 +154,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val client = createOkHttp()
|
val client = apiClient.getClient()
|
||||||
|
|
||||||
val headReq = Request.Builder().url(trimmed).head().build()
|
val headReq = Request.Builder().url(trimmed).head().build()
|
||||||
val headResp = try {
|
val headResp = try {
|
||||||
@@ -240,7 +238,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
currentKlass.value = video.klass
|
currentKlass.value = video.klass
|
||||||
currentName.value = video.video.name
|
currentName.value = video.video.name
|
||||||
currentDuration.longValue = video.video.duration
|
currentDuration.longValue = video.video.duration
|
||||||
currentGallery.value = video.getGallery()
|
currentGallery.value = video.getGallery(apiClient)
|
||||||
|
|
||||||
player?.apply {
|
player?.apply {
|
||||||
stop()
|
stop()
|
||||||
@@ -249,7 +247,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
|
|
||||||
recentManager.pushVideo(context, VideoQueryIndex(video.klass, video.id))
|
recentManager.pushVideo(context, VideoQueryIndex(video.klass, video.id))
|
||||||
|
|
||||||
val subtitleCandidate = video.getSubtitle().trim()
|
val subtitleCandidate = video.getSubtitle(apiClient).trim()
|
||||||
val subtitleUri = tryResolveSubtitleUri(subtitleCandidate)
|
val subtitleUri = tryResolveSubtitleUri(subtitleCandidate)
|
||||||
|
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
@@ -303,7 +301,7 @@ class VideoPlayerViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = video.getVideo()
|
val url = video.getVideo(apiClient)
|
||||||
val videoUri = if (video.isLocal) Uri.fromFile(File(url)) else url.toUri()
|
val videoUri = if (video.isLocal) Uri.fromFile(File(url)) else url.toUri()
|
||||||
|
|
||||||
val mediaItem: MediaItem = if (subtitleUri != null) {
|
val mediaItem: MediaItem = if (subtitleUri != null) {
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
package com.acitelight.aether.viewModel
|
package com.acitelight.aether.viewModel
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import coil3.ImageLoader
|
import coil3.ImageLoader
|
||||||
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import com.acitelight.aether.Global
|
import com.acitelight.aether.Global
|
||||||
import com.acitelight.aether.model.Video
|
import com.acitelight.aether.model.Video
|
||||||
import com.acitelight.aether.service.ApiClient.createOkHttp
|
import com.acitelight.aether.service.ApiClient
|
||||||
import com.acitelight.aether.service.FetchManager
|
import com.acitelight.aether.service.FetchManager
|
||||||
import com.acitelight.aether.service.MediaManager
|
import com.acitelight.aether.service.MediaManager
|
||||||
import com.acitelight.aether.service.RecentManager
|
|
||||||
import com.acitelight.aether.service.VideoLibrary
|
import com.acitelight.aether.service.VideoLibrary
|
||||||
import com.tonyodev.fetch2.Status
|
import com.tonyodev.fetch2.Status
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -31,7 +25,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -39,12 +32,12 @@ class VideoScreenViewModel @Inject constructor(
|
|||||||
val fetchManager: FetchManager,
|
val fetchManager: FetchManager,
|
||||||
@ApplicationContext val context: Context,
|
@ApplicationContext val context: Context,
|
||||||
val mediaManager: MediaManager,
|
val mediaManager: MediaManager,
|
||||||
val recentManager: RecentManager,
|
val videoLibrary: VideoLibrary,
|
||||||
val videoLibrary: VideoLibrary
|
val apiClient: ApiClient
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _tabIndex = mutableIntStateOf(0)
|
private val _tabIndex = mutableIntStateOf(0)
|
||||||
val tabIndex: State<Int> = _tabIndex
|
val tabIndex: State<Int> = _tabIndex
|
||||||
var imageLoader: ImageLoader? = null;
|
var imageLoader: ImageLoader? = null
|
||||||
var menuVisibility = mutableStateOf(false)
|
var menuVisibility = mutableStateOf(false)
|
||||||
var searchFilter = mutableStateOf("")
|
var searchFilter = mutableStateOf("")
|
||||||
var doneInit = mutableStateOf(false)
|
var doneInit = mutableStateOf(false)
|
||||||
@@ -79,7 +72,7 @@ class VideoScreenViewModel @Inject constructor(
|
|||||||
else {
|
else {
|
||||||
videoLibrary.classes.add("Offline")
|
videoLibrary.classes.add("Offline")
|
||||||
videoLibrary.updatingMap[0] = true
|
videoLibrary.updatingMap[0] = true
|
||||||
videoLibrary.classesMap["Offline"] = mutableStateListOf<Video>()
|
videoLibrary.classesMap["Offline"] = mutableStateListOf()
|
||||||
|
|
||||||
val downloaded = fetchManager.getAllDownloadsAsync().filter {
|
val downloaded = fetchManager.getAllDownloadsAsync().filter {
|
||||||
it.status == Status.COMPLETED &&
|
it.status == Status.COMPLETED &&
|
||||||
@@ -101,7 +94,7 @@ class VideoScreenViewModel @Inject constructor(
|
|||||||
fun setTabIndex(index: Int) {
|
fun setTabIndex(index: Int) {
|
||||||
viewModelScope.launch()
|
viewModelScope.launch()
|
||||||
{
|
{
|
||||||
_tabIndex.intValue = index;
|
_tabIndex.intValue = index
|
||||||
if (videoLibrary.updatingMap[index] == true) return@launch
|
if (videoLibrary.updatingMap[index] == true) return@launch
|
||||||
|
|
||||||
videoLibrary.updatingMap[index] = true
|
videoLibrary.updatingMap[index] = true
|
||||||
@@ -126,7 +119,7 @@ class VideoScreenViewModel @Inject constructor(
|
|||||||
init {
|
init {
|
||||||
imageLoader = ImageLoader.Builder(context)
|
imageLoader = ImageLoader.Builder(context)
|
||||||
.components {
|
.components {
|
||||||
add(OkHttpNetworkFetcherFactory(createOkHttp()))
|
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user