From 393419afd78ade90c2f588467b36c387e31b3f98 Mon Sep 17 00:00:00 2001 From: acite <1498045907@qq.com> Date: Sun, 28 Sep 2025 14:31:03 +0800 Subject: [PATCH] [optimize] Refactoring API client injection architecture --- .../java/com/acitelight/aether/model/Comic.kt | 10 +-- .../java/com/acitelight/aether/model/Video.kt | 16 ++--- .../acitelight/aether/service/AbyssStream.kt | 9 +-- .../aether/service/AbyssTunnelProxy.kt | 12 ++-- .../acitelight/aether/service/ApiClient.kt | 59 +++++++++++---- .../acitelight/aether/service/AuthManager.kt | 9 ++- .../acitelight/aether/service/FetchManager.kt | 31 ++------ .../acitelight/aether/service/MediaManager.kt | 21 +++--- .../acitelight/aether/view/ComicGridView.kt | 8 +-- .../acitelight/aether/view/ComicPageView.kt | 4 +- .../com/acitelight/aether/view/ComicScreen.kt | 36 +++------- .../com/acitelight/aether/view/HomeScreen.kt | 72 +++++++++++-------- .../com/acitelight/aether/view/MeScreen.kt | 29 +------- .../aether/view/MiniPlaylistCard.kt | 6 +- .../acitelight/aether/view/MiniVideoCard.kt | 5 +- .../aether/view/TransmissionScreen.kt | 6 +- .../aether/view/VideoPlayerLandscape.kt | 2 +- .../aether/view/VideoPlayerPortal.kt | 29 ++++---- .../com/acitelight/aether/view/VideoScreen.kt | 2 +- .../aether/viewModel/ComicGridViewModel.kt | 10 ++- .../aether/viewModel/ComicPageViewModel.kt | 7 +- .../aether/viewModel/ComicScreenViewModel.kt | 12 ++-- .../aether/viewModel/HomeScreenViewModel.kt | 5 +- .../aether/viewModel/MeScreenViewModel.kt | 42 +++++------ .../viewModel/TransmissionScreenViewModel.kt | 8 +-- .../aether/viewModel/VideoPlayerViewModel.kt | 32 ++++----- .../aether/viewModel/VideoScreenViewModel.kt | 21 ++---- 27 files changed, 231 insertions(+), 272 deletions(-) diff --git a/app/src/main/java/com/acitelight/aether/model/Comic.kt b/app/src/main/java/com/acitelight/aether/model/Comic.kt index 65fd634..be47664 100644 --- a/app/src/main/java/com/acitelight/aether/model/Comic.kt +++ b/app/src/main/java/com/acitelight/aether/model/Comic.kt @@ -8,17 +8,17 @@ class Comic( 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) if(v >= 0) { - return getPage(v) + return getPage(v, api) } return null } @@ -33,7 +33,7 @@ class Comic( var v = comic.list.indexOf(pageName) if(v >= 0) { - var r: Int = 1 + var r = 1 v+=1 while(v < comic.list.size && !comic.bookmarks.any{ x -> x.page == comic.list[v] diff --git a/app/src/main/java/com/acitelight/aether/model/Video.kt b/app/src/main/java/com/acitelight/aether/model/Video.kt index cefa579..8966105 100644 --- a/app/src/main/java/com/acitelight/aether/model/Video.kt +++ b/app/src/main/java/com/acitelight/aether/model/Video.kt @@ -14,28 +14,28 @@ class Video( val token: String, val video: VideoResponse ) { - fun getCover(): String { + fun getCover(api: ApiClient): String { return if (isLocal) "$localBase/videos/$klass/$id/cover.jpg" 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) "$localBase/videos/$klass/$id/video.mp4" 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) "$localBase/videos/$klass/$id/subtitle.vtt" else - "${ApiClient.getBase()}api/video/$klass/$id/subtitle?token=$token" + "${api.getBase()}api/video/$klass/$id/subtitle?token=$token" } - fun getGallery(): List { + fun getGallery(api: ApiClient): List { return if (isLocal) video.gallery.map { KeyImage( @@ -46,7 +46,7 @@ class Video( } else video.gallery.map { KeyImage( 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" ) } diff --git a/app/src/main/java/com/acitelight/aether/service/AbyssStream.kt b/app/src/main/java/com/acitelight/aether/service/AbyssStream.kt index 20b124f..49c3946 100644 --- a/app/src/main/java/com/acitelight/aether/service/AbyssStream.kt +++ b/app/src/main/java/com/acitelight/aether/service/AbyssStream.kt @@ -1,8 +1,5 @@ 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 java.io.InputStream import java.io.OutputStream @@ -44,7 +41,7 @@ class AbyssStream private constructor( * Create and perform handshake on an already-connected socket. * 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") val inStream = socket.getInputStream() val outStream = socket.getOutputStream() @@ -69,7 +66,7 @@ class AbyssStream private constructor( val ch = ByteArray(32) readExact(inStream, ch, 0, 32) - val signed = signChallengeByte(localPriv, ch) + val signed = authManager.signChallengeByte(localPriv, ch) writeExact(outStream, signed, 0, signed.size) readExact(inStream, ch, 0, 16) @@ -222,7 +219,7 @@ class AbyssStream private constructor( val header = ByteArray(4) try { readExact(input, header, 0, 4) - } catch (e: EOFException) { + } catch (_: EOFException) { return null } val payloadLen = ByteBuffer.wrap(header).int and 0xffffffff.toInt() diff --git a/app/src/main/java/com/acitelight/aether/service/AbyssTunnelProxy.kt b/app/src/main/java/com/acitelight/aether/service/AbyssTunnelProxy.kt index 0d55689..fa670dc 100644 --- a/app/src/main/java/com/acitelight/aether/service/AbyssTunnelProxy.kt +++ b/app/src/main/java/com/acitelight/aether/service/AbyssTunnelProxy.kt @@ -1,8 +1,5 @@ package com.acitelight.aether.service - -import android.util.Log -import com.acitelight.aether.service.AuthManager.db64 import kotlinx.coroutines.* import kotlinx.coroutines.flow.first import kotlinx.coroutines.selects.select @@ -17,7 +14,8 @@ import kotlin.coroutines.CoroutineContext @Singleton class AbyssTunnelProxy @Inject constructor( - private val settingsDataStoreManager: SettingsDataStoreManager + private val settingsDataStoreManager: SettingsDataStoreManager, + private val authManager: AuthManager ) { private val coroutineContext: CoroutineContext = Dispatchers.IO private var serverHost: String = "" @@ -48,7 +46,7 @@ class AbyssTunnelProxy @Inject constructor( launch { try { handleLocalConnection(client) } - catch (ex: Exception) { /* ignore */ } + catch (_: Exception) { /* ignore */ } } } } catch (ex: Exception) { @@ -72,14 +70,14 @@ class AbyssTunnelProxy @Inject constructor( var abyssStream: AbyssStream? = null try { 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 val job1 = launch { copyExactSuspend(localIn, abyssStream) } // local -> abyss val job2 = launch { copyFromAbyssToLocal(abyssStream, localOut) } // abyss -> local // wait for either direction to finish - select { + select { job1.onJoin { /* completed */ } job2.onJoin { /* completed */ } } diff --git a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt index 925f8cd..64503f8 100644 --- a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt +++ b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt @@ -10,7 +10,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import okhttp3.ConnectionSpec +import okhttp3.Cookie +import okhttp3.CookieJar import okhttp3.EventListener +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient @@ -24,23 +27,27 @@ import java.security.KeyStore import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate +import javax.inject.Inject +import javax.inject.Singleton import javax.net.ssl.SSLContext import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager +@Singleton +class ApiClient @Inject constructor( -object ApiClient { +) { fun getBase(): String{ return replaceAbyssProtocol(base) } private var base: String = "" - var domain: String = "" - var cert: String = "" + private var domain: String = "" + private var cert: String = "" private val json = Json { ignoreUnknownKeys = true } - fun replaceAbyssProtocol(uri: String): String { + private fun replaceAbyssProtocol(uri: String): String { 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 decodedPem = pemString .replace("-----BEGIN CERTIFICATE-----", "") @@ -66,7 +73,7 @@ object ApiClient { } } - fun createOkHttpClientWithDynamicCert(trustedCert: X509Certificate?): OkHttpClient { + private fun createOkHttpClientWithDynamicCert(trustedCert: X509Certificate?): OkHttpClient { try { val defaultTmFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() @@ -86,7 +93,7 @@ object ApiClient { ).apply { init(keyStore) } - tmf.trustManagers.first { it is X509TrustManager } as X509TrustManager + tmf.trustManagers.first { i -> i is X509TrustManager } as X509TrustManager } val combinedTm = object : X509TrustManager { @@ -157,11 +164,22 @@ object ApiClient { } } - fun createOkHttp(): OkHttpClient { + private fun createOkHttp(): OkHttpClient { return if (cert == "") if (base.startsWith("abyss://")) OkHttpClient .Builder() + .cookieJar(object : CookieJar { + private val cookieStore = mutableMapOf>() + + override fun saveFromResponse(url: HttpUrl, cookies: List) { + cookieStore[url] = cookies + } + + override fun loadForRequest(url: HttpUrl): List { + return cookieStore[url] ?: emptyList() + } + }) .proxy( Proxy( Proxy.Type.HTTP, @@ -174,6 +192,17 @@ object ApiClient { else OkHttpClient .Builder() + .cookieJar(object : CookieJar { + private val cookieStore = mutableMapOf>() + + override fun saveFromResponse(url: HttpUrl, cookies: List) { + cookieStore[url] = cookies + } + + override fun loadForRequest(url: HttpUrl): List { + return cookieStore[url] ?: emptyList() + } + }) .connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)) .eventListener(dnsEventListener) .build() @@ -183,20 +212,22 @@ object ApiClient { } private fun createRetrofit(): Retrofit { - val okHttpClient = createOkHttp() + client = createOkHttp() val b = replaceAbyssProtocol(base) return Retrofit.Builder() .baseUrl(b) - .client(okHttpClient) + .client(client!!) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) .build() } - + private var client: OkHttpClient? = null var api: ApiInterface? = null + fun getClient() = client!! + suspend fun apply(context: Context, urls: String, crt: String): String? { try { val urlList = urls.split(";").map { it.trim() } @@ -219,7 +250,7 @@ object ApiClient { base = selectedUrl 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) @@ -228,7 +259,7 @@ object ApiClient { Log.i("Delay Analyze", "Abyss Hello: ${h.string()}") return base - } catch (e: Exception) { + } catch (_: Exception) { api = null base = "" domain = "" @@ -241,7 +272,7 @@ object ApiClient { return@withContext try { val address = InetAddress.getByName(host) address.isReachable(200) - } catch (e: Exception) { + } catch (_: Exception) { false } } diff --git a/app/src/main/java/com/acitelight/aether/service/AuthManager.kt b/app/src/main/java/com/acitelight/aether/service/AuthManager.kt index ab33e2f..a227f31 100644 --- a/app/src/main/java/com/acitelight/aether/service/AuthManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/AuthManager.kt @@ -18,10 +18,15 @@ import java.lang.reflect.Proxy import java.net.InetSocketAddress import java.security.PrivateKey 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? { - val api = ApiClient.api + val api = apiClient.api var challengeBase64 = "" try{ diff --git a/app/src/main/java/com/acitelight/aether/service/FetchManager.kt b/app/src/main/java/com/acitelight/aether/service/FetchManager.kt index 1a8f78e..c9f7150 100644 --- a/app/src/main/java/com/acitelight/aether/service/FetchManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/FetchManager.kt @@ -1,48 +1,37 @@ package com.acitelight.aether.service import android.content.Context -import androidx.compose.runtime.mutableStateOf -import com.acitelight.aether.Screen import com.acitelight.aether.model.Video -import com.acitelight.aether.service.ApiClient.createOkHttp import com.tonyodev.fetch2.Download import com.tonyodev.fetch2.Fetch import com.tonyodev.fetch2.FetchConfiguration import com.tonyodev.fetch2.FetchListener import com.tonyodev.fetch2.Request -import com.tonyodev.fetch2.Status import com.tonyodev.fetch2core.Extras import com.tonyodev.fetch2okhttp.OkHttpDownloader import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update -import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json -import okhttp3.OkHttpClient import java.io.File -import java.io.IOException import javax.inject.Inject import javax.inject.Singleton @Singleton 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 listener: FetchListener? = null - private var client: OkHttpClient? = null val configured = MutableStateFlow(false) fun init() { - client = createOkHttp() val fetchConfiguration = FetchConfiguration.Builder(context) .setDownloadConcurrentLimit(8) - .setHttpDownloader(OkHttpDownloader(client)) + .setHttpDownloader(OkHttpDownloader(apiClient.getClient())) .build() fetch = Fetch.Impl.getInstance(fetchConfiguration) @@ -65,12 +54,6 @@ class FetchManager @Inject constructor( listener = null } - // query downloads - suspend fun getAllDownloads(callback: (List) -> Unit) { - configured.filter { it }.first() - fetch?.getDownloads { list -> callback(list) } ?: callback(emptyList()) - } - suspend fun getAllDownloadsAsync(): List { configured.filter { it }.first() val completed = MutableStateFlow(false) @@ -140,7 +123,7 @@ class FetchManager @Inject constructor( ) val requests = mutableListOf( - Request(video.getVideo(), videoPath.path).apply { + Request(video.getVideo(apiClient), videoPath.path).apply { extras = Extras( mapOf( "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( mapOf( "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( mapOf( "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( Request(p.url, File( context.getExternalFilesDir(null), diff --git a/app/src/main/java/com/acitelight/aether/service/MediaManager.kt b/app/src/main/java/com/acitelight/aether/service/MediaManager.kt index 32f36f0..c0d0a57 100644 --- a/app/src/main/java/com/acitelight/aether/service/MediaManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/MediaManager.kt @@ -16,7 +16,8 @@ import javax.inject.Singleton @Singleton class MediaManager @Inject constructor( val fetchManager: FetchManager, - @ApplicationContext val context: Context + @ApplicationContext val context: Context, + private val apiClient: ApiClient ) { var token: String = "null" @@ -25,7 +26,7 @@ class MediaManager @Inject constructor( { try { - val j = ApiClient.api!!.getVideoClasses(token) + val j = apiClient.api!!.getVideoClasses(token) return j.toList() }catch(_: Exception) { @@ -37,7 +38,7 @@ class MediaManager @Inject constructor( { try { - val j = ApiClient.api!!.queryVideoClasses(klass, token) + val j = apiClient.api!!.queryVideoClasses(klass, token) return j.toList() }catch(_: Exception) { @@ -57,7 +58,7 @@ class MediaManager @Inject constructor( } 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) }catch (_: Exception) { @@ -83,7 +84,7 @@ class MediaManager @Inject constructor( } 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) }catch (_: Exception) { @@ -133,7 +134,7 @@ class MediaManager @Inject constructor( } 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 { Video( klass = klass, @@ -157,7 +158,7 @@ class MediaManager @Inject constructor( suspend fun listComics() : List { try{ - val j = ApiClient.api!!.getComics(token) + val j = apiClient.api!!.getComics(token) return j }catch (_: Exception) { @@ -168,7 +169,7 @@ class MediaManager @Inject constructor( suspend fun queryComicInfoSingle(id: String) : Comic? { try{ - val j = ApiClient.api!!.queryComicInfo(id, token) + val j = apiClient.api!!.queryComicInfo(id, token) return Comic(id = id, comic = j, token = token) }catch (_: Exception) { @@ -179,7 +180,7 @@ class MediaManager @Inject constructor( suspend fun queryComicInfoBulk(id: List) : List? { 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) } }catch (_: Exception) { @@ -190,7 +191,7 @@ class MediaManager @Inject constructor( suspend fun postBookmark(id: String, bookMark: BookMark): Boolean { try{ - ApiClient.api!!.postBookmark(id, token, bookMark) + apiClient.api!!.postBookmark(id, token, bookMark) return true }catch (_: Exception) { diff --git a/app/src/main/java/com/acitelight/aether/view/ComicGridView.kt b/app/src/main/java/com/acitelight/aether/view/ComicGridView.kt index 1f88645..06fbdf0 100644 --- a/app/src/main/java/com/acitelight/aether/view/ComicGridView.kt +++ b/app/src/main/java/com/acitelight/aether/view/ComicGridView.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import coil3.compose.AsyncImage import coil3.request.ImageRequest @@ -157,7 +156,6 @@ fun ComicGridView( comicGridViewModel.updateProcess(comicId.hexToString()) { if (record != null) { - val k = comic!!.getPageChapterIndex(record!!.position) val route = "comic_page_route/${comic!!.id.toHex()}/${ record!!.position }" @@ -248,7 +246,7 @@ fun ChapterCard( { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(comic.getPage(c.page)) + .data(comic.getPage(c.page, comicGridViewModel.apiClient)) .memoryCacheKey("${comic.id}/${c.page}") .diskCacheKey("${comic.id}/${c.page}") .build(), @@ -300,13 +298,13 @@ fun ChapterCard( .padding(horizontal = 6.dp), onClick = { val route = - "comic_page_route/${"${comic.id}".toHex()}/${comic.getPageIndex(r)}" + "comic_page_route/${comic.id.toHex()}/${comic.getPageIndex(r)}" navController.navigate(route) } ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(comic.getPage(r)) + .data(comic.getPage(r, comicGridViewModel.apiClient)) .memoryCacheKey("${comic.id}/${r}") .diskCacheKey("${comic.id}/${r}") .build(), diff --git a/app/src/main/java/com/acitelight/aether/view/ComicPageView.kt b/app/src/main/java/com/acitelight/aether/view/ComicPageView.kt index 6c4e088..a13e542 100644 --- a/app/src/main/java/com/acitelight/aether/view/ComicPageView.kt +++ b/app/src/main/java/com/acitelight/aether/view/ComicPageView.kt @@ -89,7 +89,7 @@ fun ComicPageView( ) { page -> AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(it.getPage(page)) + .data(it.getPage(page, comicPageViewModel.apiClient)) .memoryCacheKey("${it.id}/${page}") .diskCacheKey("${it.id}/${page}") .build(), @@ -252,7 +252,7 @@ fun ComicPageView( { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(it.getPage(r)) + .data(it.getPage(r, comicPageViewModel.apiClient)) .memoryCacheKey("${it.id}/${r}") .diskCacheKey("${it.id}/${r}") .build(), diff --git a/app/src/main/java/com/acitelight/aether/view/ComicScreen.kt b/app/src/main/java/com/acitelight/aether/view/ComicScreen.kt index 5bfbaf4..db5ac1e 100644 --- a/app/src/main/java/com/acitelight/aether/view/ComicScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/ComicScreen.kt @@ -1,13 +1,11 @@ package com.acitelight.aether.view -import android.nfc.Tag import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize 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.widthIn 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.StaggeredGridCells 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.verticalScroll import androidx.compose.material3.Card -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color 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.Placeable -import androidx.compose.ui.modifier.modifierLocalOf 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.sp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.NavHostController +import coil3.compose.AsyncImage import coil3.request.ImageRequest -import com.acitelight.aether.Global import com.acitelight.aether.model.Comic import com.acitelight.aether.viewModel.ComicScreenViewModel -import java.nio.charset.Charset @Composable fun VariableGrid( @@ -223,7 +203,7 @@ fun ComicCard( .fillMaxWidth() .wrapContentHeight(), onClick = { - val route = "comic_grid_route/${"${comic.id}".toHex()}" + val route = "comic_grid_route/${comic.id.toHex()}" navController.navigate(route) } ) { @@ -234,7 +214,7 @@ fun ComicCard( Box(modifier = Modifier.fillMaxSize()) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(comic.getPage(0)) + .data(comic.getPage(0, comicScreenViewModel.apiClient)) .memoryCacheKey("${comic.id}/${0}") .diskCacheKey("${comic.id}/${0}") .build(), diff --git a/app/src/main/java/com/acitelight/aether/view/HomeScreen.kt b/app/src/main/java/com/acitelight/aether/view/HomeScreen.kt index 8023230..ebb0466 100644 --- a/app/src/main/java/com/acitelight/aether/view/HomeScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/HomeScreen.kt @@ -1,7 +1,6 @@ package com.acitelight.aether.view import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.NavController import androidx.navigation.NavHostController import coil3.compose.AsyncImage import coil3.request.ImageRequest import com.acitelight.aether.Global.updateRelate import com.acitelight.aether.model.Comic -import com.acitelight.aether.viewModel.ComicScreenViewModel import com.acitelight.aether.viewModel.HomeScreenViewModel -import kotlinx.coroutines.launch @Composable fun HomeScreen( homeScreenViewModel: HomeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel(), - navController: NavHostController) -{ + navController: NavHostController +) { val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 }) HorizontalPager( state = pagerState, - modifier = Modifier.fillMaxSize().background(Color.Black) - ){ - p -> - if(p == 0) - { + modifier = Modifier + .fillMaxSize() + .background(Color.Black) + ) { p -> + if (p == 0) { Column(Modifier.fillMaxHeight()) { Text( text = "Videos", 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) @@ -73,27 +71,38 @@ fun HomeScreen( LazyColumn(modifier = Modifier.fillMaxWidth()) { items(homeScreenViewModel.recentManager.recentVideo) - { - i -> + { i -> MiniVideoCard( modifier = Modifier .padding(horizontal = 12.dp), i, - { - updateRelate(homeScreenViewModel.recentManager.recentVideo, i) + apiClient = homeScreenViewModel.apiClient, + imageLoader = homeScreenViewModel.imageLoader!! + ) + { + updateRelate(homeScreenViewModel.recentManager.recentVideo, i) - val playList = mutableListOf() - val fv = homeScreenViewModel.videoLibrary.classesMap.map { it.value }.flatten() + val playList = mutableListOf() + val fv = homeScreenViewModel.videoLibrary.classesMap.map { it.value } + .flatten() - val group = fv.filter { it.klass == i.klass && it.video.group == i.video.group } - for (i in group) { - playList.add("${i.klass}/${i.id}") - } + val group = + fv.filter { it.klass == i.klass && it.video.group == i.video.group } + for (i in group) { + playList.add("${i.klass}/${i.id}") + } - val route = "video_player_route/${(playList.joinToString(",") + "|${i.id}").toHex()}" - navController.navigate(route) - }, homeScreenViewModel.imageLoader!!) - HorizontalDivider(Modifier.padding(vertical = 8.dp).alpha(0.4f), 1.dp, DividerDefaults.color) + val route = + "video_player_route/${(playList.joinToString(",") + "|${i.id}").toHex()}" + navController.navigate(route) + } + HorizontalDivider( + Modifier + .padding(vertical = 8.dp) + .alpha(0.4f), + 1.dp, + DividerDefaults.color + ) } } } @@ -102,7 +111,9 @@ fun HomeScreen( Text( text = "Comics", 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) @@ -115,8 +126,7 @@ fun HomeScreen( ) { items(homeScreenViewModel.recentManager.recentComic) - { - comic -> + { comic -> ComicCardRecent(comic, navController, homeScreenViewModel) } } @@ -138,7 +148,7 @@ fun ComicCardRecent( .fillMaxWidth() .wrapContentHeight(), onClick = { - val route = "comic_grid_route/${"${comic.id}".toHex()}" + val route = "comic_grid_route/${comic.id.toHex()}" navController.navigate(route) } ) { @@ -149,7 +159,7 @@ fun ComicCardRecent( Box(modifier = Modifier.fillMaxSize()) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(comic.getPage(0)) + .data(comic.getPage(0, homeScreenViewModel.apiClient)) .memoryCacheKey("${comic.id}/${0}") .diskCacheKey("${comic.id}/${0}") .build(), diff --git a/app/src/main/java/com/acitelight/aether/view/MeScreen.kt b/app/src/main/java/com/acitelight/aether/view/MeScreen.kt index 554b854..aeecf67 100644 --- a/app/src/main/java/com/acitelight/aether/view/MeScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/MeScreen.kt @@ -1,7 +1,5 @@ package com.acitelight.aether.view -import android.util.Log -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column 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.Link import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Security import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.Checkbox @@ -31,25 +28,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp 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 kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @Composable fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel()) { - val context = LocalContext.current - var username by meScreenViewModel.username; - var privateKey by meScreenViewModel.privateKey; + var username by meScreenViewModel.username + var privateKey by meScreenViewModel.privateKey var url by meScreenViewModel.url var cert by meScreenViewModel.cert @@ -204,19 +192,6 @@ fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.view ) { 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") - } } } } diff --git a/app/src/main/java/com/acitelight/aether/view/MiniPlaylistCard.kt b/app/src/main/java/com/acitelight/aether/view/MiniPlaylistCard.kt index f648ac3..d61f875 100644 --- a/app/src/main/java/com/acitelight/aether/view/MiniPlaylistCard.kt +++ b/app/src/main/java/com/acitelight/aether/view/MiniPlaylistCard.kt @@ -35,12 +35,12 @@ import coil3.ImageLoader import coil3.compose.AsyncImage import coil3.request.ImageRequest import com.acitelight.aether.model.Video +import com.acitelight.aether.service.ApiClient @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 - Card( modifier = modifier .height(80.dp) @@ -58,7 +58,7 @@ fun MiniPlaylistCard(modifier: Modifier, video: Video, imageLoader: ImageLoader, { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(video.getCover()) + .data(video.getCover(apiClient)) .memoryCacheKey("${video.klass}/${video.id}/cover") .diskCacheKey("${video.klass}/${video.id}/cover") .listener( diff --git a/app/src/main/java/com/acitelight/aether/view/MiniVideoCard.kt b/app/src/main/java/com/acitelight/aether/view/MiniVideoCard.kt index b44bdf3..5f1768d 100644 --- a/app/src/main/java/com/acitelight/aether/view/MiniVideoCard.kt +++ b/app/src/main/java/com/acitelight/aether/view/MiniVideoCard.kt @@ -28,10 +28,11 @@ import coil3.ImageLoader import coil3.compose.AsyncImage import coil3.request.ImageRequest import com.acitelight.aether.model.Video +import com.acitelight.aether.service.ApiClient @Composable -fun MiniVideoCard(modifier: Modifier, video: Video, onClick: () -> Unit, imageLoader: ImageLoader) { +fun MiniVideoCard(modifier: Modifier, video: Video, imageLoader: ImageLoader, apiClient: ApiClient, onClick: () -> Unit) { Card( modifier = modifier .height(80.dp) @@ -49,7 +50,7 @@ fun MiniVideoCard(modifier: Modifier, video: Video, onClick: () -> Unit, imageLo { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(video.getCover()) + .data(video.getCover(apiClient)) .memoryCacheKey("${video.klass}/${video.id}/cover") .diskCacheKey("${video.klass}/${video.id}/cover") .listener( diff --git a/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt b/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt index fd874f8..2c18261 100644 --- a/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/TransmissionScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn 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.lifecycle.viewModelScope import androidx.navigation.NavHostController -import androidx.room.util.TableInfo import coil3.compose.AsyncImage import coil3.request.ImageRequest import com.acitelight.aether.Global.updateRelate @@ -120,7 +118,7 @@ fun TransmissionScreen( for (i in downloadToGroup( item, downloads - )) transmissionScreenViewModel.delete(i.id, true) + )) transmissionScreenViewModel.delete(i.id) } ) } @@ -247,7 +245,7 @@ private fun VideoDownloadCard( else { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(video.getCover()) + .data(video.getCover(viewModel.apiClient)) .memoryCacheKey("${model.klass}/${model.vid}/cover") .diskCacheKey("${model.klass}/${model.vid}/cover") .build(), diff --git a/app/src/main/java/com/acitelight/aether/view/VideoPlayerLandscape.kt b/app/src/main/java/com/acitelight/aether/view/VideoPlayerLandscape.kt index 05f8289..8846bc2 100644 --- a/app/src/main/java/com/acitelight/aether/view/VideoPlayerLandscape.kt +++ b/app/src/main/java/com/acitelight/aether/view/VideoPlayerLandscape.kt @@ -557,7 +557,7 @@ fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) { LazyColumn(contentPadding = PaddingValues(vertical = 4.dp)) { items(videoPlayerViewModel.videos) { item -> 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) return@MiniPlaylistCard diff --git a/app/src/main/java/com/acitelight/aether/view/VideoPlayerPortal.kt b/app/src/main/java/com/acitelight/aether/view/VideoPlayerPortal.kt index c8afddb..1490508 100644 --- a/app/src/main/java/com/acitelight/aether/view/VideoPlayerPortal.kt +++ b/app/src/main/java/com/acitelight/aether/view/VideoPlayerPortal.kt @@ -245,22 +245,25 @@ fun VideoPlayerPortal( modifier = Modifier .padding(horizontal = 12.dp), i, - { - videoPlayerViewModel.isPlaying = false - videoPlayerViewModel.player?.pause() + apiClient = videoPlayerViewModel.apiClient, + imageLoader = videoPlayerViewModel.imageLoader!! + ) { + videoPlayerViewModel.isPlaying = false + videoPlayerViewModel.player?.pause() - val playList = mutableListOf() - val fv = videoPlayerViewModel.videoLibrary.classesMap.map { it.value }.flatten() + val playList = mutableListOf() + val fv = + videoPlayerViewModel.videoLibrary.classesMap.map { it.value }.flatten() - val group = fv.filter { it.klass == i.klass && it.video.group == i.video.group } - for (i in group) { - playList.add("${i.klass}/${i.id}") - } + val group = + fv.filter { it.klass == i.klass && it.video.group == i.video.group } + for (i in group) { + playList.add("${i.klass}/${i.id}") + } - val route = "video_player_route/${playList.joinToString(",").toHex()}" - navController.navigate(route) - }, videoPlayerViewModel.imageLoader!! - ) + val route = "video_player_route/${playList.joinToString(",").toHex()}" + navController.navigate(route) + } HorizontalDivider( Modifier .padding(vertical = 8.dp) diff --git a/app/src/main/java/com/acitelight/aether/view/VideoScreen.kt b/app/src/main/java/com/acitelight/aether/view/VideoScreen.kt index fc30edf..9f682ec 100644 --- a/app/src/main/java/com/acitelight/aether/view/VideoScreen.kt +++ b/app/src/main/java/com/acitelight/aether/view/VideoScreen.kt @@ -368,7 +368,7 @@ fun VideoCard( AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(video.getCover()) + .data(video.getCover(videoScreenViewModel.apiClient)) .memoryCacheKey("${video.klass}/${video.id}/cover") .diskCacheKey("${video.klass}/${video.id}/cover") .build(), diff --git a/app/src/main/java/com/acitelight/aether/viewModel/ComicGridViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/ComicGridViewModel.kt index 184ddf4..37ca7d3 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/ComicGridViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/ComicGridViewModel.kt @@ -1,11 +1,8 @@ package com.acitelight.aether.viewModel import android.content.Context -import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import coil3.ImageLoader @@ -14,7 +11,7 @@ import com.acitelight.aether.model.BookMark import com.acitelight.aether.model.Comic import com.acitelight.aether.model.ComicRecord 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.RecentManager import dagger.hilt.android.lifecycle.HiltViewModel @@ -26,7 +23,8 @@ import javax.inject.Inject class ComicGridViewModel @Inject constructor( @ApplicationContext val context: Context, val mediaManager: MediaManager, - val recentManager: RecentManager + val recentManager: RecentManager, + val apiClient: ApiClient ) : ViewModel() { var imageLoader: ImageLoader? = null @@ -38,7 +36,7 @@ class ComicGridViewModel @Inject constructor( init { imageLoader = ImageLoader.Builder(context) .components { - add(OkHttpNetworkFetcherFactory(createOkHttp())) + add(OkHttpNetworkFetcherFactory(apiClient.getClient())) } .build() db = try{ diff --git a/app/src/main/java/com/acitelight/aether/viewModel/ComicPageViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/ComicPageViewModel.kt index fc7dfaf..f3db596 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/ComicPageViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/ComicPageViewModel.kt @@ -17,7 +17,7 @@ import coil3.network.okhttp.OkHttpNetworkFetcherFactory import com.acitelight.aether.model.Comic import com.acitelight.aether.model.ComicRecord 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 dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -28,7 +28,8 @@ import javax.inject.Inject @HiltViewModel class ComicPageViewModel @Inject constructor( val mediaManager: MediaManager, - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + val apiClient: ApiClient ) : ViewModel() { var imageLoader: ImageLoader? = null @@ -43,7 +44,7 @@ class ComicPageViewModel @Inject constructor( init{ imageLoader = ImageLoader.Builder(context) .components { - add(OkHttpNetworkFetcherFactory(createOkHttp())) + add(OkHttpNetworkFetcherFactory(apiClient.getClient())) } .build() listState = LazyListState(0, 0) diff --git a/app/src/main/java/com/acitelight/aether/viewModel/ComicScreenViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/ComicScreenViewModel.kt index 0973389..5d8e292 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/ComicScreenViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/ComicScreenViewModel.kt @@ -1,28 +1,24 @@ package com.acitelight.aether.viewModel import android.content.Context -import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import coil3.ImageLoader import coil3.network.okhttp.OkHttpNetworkFetcherFactory import com.acitelight.aether.model.Comic -import com.acitelight.aether.model.ComicResponse -import com.acitelight.aether.service.ApiClient.createOkHttp +import com.acitelight.aether.service.ApiClient import com.acitelight.aether.service.MediaManager import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ComicScreenViewModel @Inject constructor( @ApplicationContext private val context: Context, - val mediaManager: MediaManager + val mediaManager: MediaManager, + val apiClient: ApiClient ) : ViewModel() { var imageLoader: ImageLoader? = null; @@ -54,7 +50,7 @@ class ComicScreenViewModel @Inject constructor( init { imageLoader = ImageLoader.Builder(context) .components { - add(OkHttpNetworkFetcherFactory(createOkHttp())) + add(OkHttpNetworkFetcherFactory(apiClient.getClient())) } .build() diff --git a/app/src/main/java/com/acitelight/aether/viewModel/HomeScreenViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/HomeScreenViewModel.kt index c89161e..28b24e1 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/HomeScreenViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/HomeScreenViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import coil3.ImageLoader 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.VideoLibrary import kotlinx.coroutines.launch @@ -19,6 +19,7 @@ class HomeScreenViewModel @Inject constructor( val recentManager: RecentManager, @ApplicationContext val context: Context, val videoLibrary: VideoLibrary, + val apiClient: ApiClient ) : ViewModel() { var imageLoader: ImageLoader? = null @@ -26,7 +27,7 @@ class HomeScreenViewModel @Inject constructor( init{ imageLoader = ImageLoader.Builder(context) .components { - add(OkHttpNetworkFetcherFactory(createOkHttp())) + add(OkHttpNetworkFetcherFactory(apiClient.getClient())) } .build() viewModelScope.launch { diff --git a/app/src/main/java/com/acitelight/aether/viewModel/MeScreenViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/MeScreenViewModel.kt index 8008d24..3155e55 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/MeScreenViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/MeScreenViewModel.kt @@ -1,45 +1,37 @@ package com.acitelight.aether.viewModel -import android.app.Application import android.content.Context import android.widget.Toast import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.platform.LocalContext 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.viewModelScope import com.acitelight.aether.AetherApp 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.AuthManager import com.acitelight.aether.service.MediaManager -import kotlinx.coroutines.flow.Flow -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 com.acitelight.aether.service.SettingsDataStoreManager import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject @HiltViewModel class MeScreenViewModel @Inject constructor( private val settingsDataStoreManager: SettingsDataStoreManager, @ApplicationContext private val context: Context, - val mediaManager: MediaManager + val mediaManager: MediaManager, + private val apiClient: ApiClient, + private val authManager: AuthManager ) : ViewModel() { - val username = mutableStateOf(""); + val username = mutableStateOf("") val privateKey = mutableStateOf("") - val url = mutableStateOf(""); + val url = mutableStateOf("") val cert = mutableStateOf("") val uss = settingsDataStoreManager.useSelfSignedFlow @@ -54,10 +46,10 @@ class MeScreenViewModel @Inject constructor( if(username.value=="" || privateKey.value=="" || url.value=="") return@launch 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") - mediaManager.token = AuthManager.fetchToken( + mediaManager.token = authManager.fetchToken( username.value, settingsDataStoreManager.privateKeyFlow.first() )!! @@ -65,7 +57,7 @@ class MeScreenViewModel @Inject constructor( Global.loggedIn = true 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() } }catch(e: Exception) @@ -100,10 +92,10 @@ class MeScreenViewModel @Inject constructor( if (u == "" || p == "" || us == "") return@launch try { - val usedUrl = ApiClient.apply(context, u, if(uss.first()) c else "") - (context as AetherApp).abyssService?.proxy?.config(ApiClient.getBase().toUri().host!!, 4096) + val usedUrl = apiClient.apply(context, u, if(uss.first()) c else "") + (context as AetherApp).abyssService?.proxy?.config(apiClient.getBase().toUri().host!!, 4096) context.abyssService?.downloader?.init() - mediaManager.token = AuthManager.fetchToken( + mediaManager.token = authManager.fetchToken( us, p )!! @@ -133,7 +125,7 @@ class MeScreenViewModel @Inject constructor( if (u == "" || p == "" || ur == "") return@launch try { - mediaManager.token = AuthManager.fetchToken( + mediaManager.token = authManager.fetchToken( u, p )!! @@ -141,7 +133,7 @@ class MeScreenViewModel @Inject constructor( Global.loggedIn = true 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() } Toast.makeText(context, "Account Updated", Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/acitelight/aether/viewModel/TransmissionScreenViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/TransmissionScreenViewModel.kt index 9db6bdb..828972a 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/TransmissionScreenViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/TransmissionScreenViewModel.kt @@ -9,7 +9,7 @@ import coil3.ImageLoader import coil3.network.okhttp.OkHttpNetworkFetcherFactory import com.acitelight.aether.model.Video 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.MediaManager import com.acitelight.aether.service.VideoLibrary @@ -20,7 +20,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -29,6 +28,7 @@ class TransmissionScreenViewModel @Inject constructor( @ApplicationContext val context: Context, val videoLibrary: VideoLibrary, val mediaManager: MediaManager, + val apiClient: ApiClient ) : ViewModel() { var imageLoader: ImageLoader? = null val downloads: SnapshotStateList = mutableStateListOf() @@ -205,7 +205,7 @@ class TransmissionScreenViewModel @Inject constructor( fun pause(id: Int) = fetchManager.pause(id) fun resume(id: Int) = fetchManager.resume(id) fun cancel(id: Int) = fetchManager.cancel(id) - fun delete(id: Int, deleteFile: Boolean = true) { + fun delete(id: Int) { fetchManager.delete(id) { viewModelScope.launch(Dispatchers.Main) { removeOnMain(id) } } @@ -218,7 +218,7 @@ class TransmissionScreenViewModel @Inject constructor( init { imageLoader = ImageLoader.Builder(context).components { - add(OkHttpNetworkFetcherFactory(createOkHttp())) + add(OkHttpNetworkFetcherFactory(apiClient.getClient())) }.build() viewModelScope.launch { diff --git a/app/src/main/java/com/acitelight/aether/viewModel/VideoPlayerViewModel.kt b/app/src/main/java/com/acitelight/aether/viewModel/VideoPlayerViewModel.kt index c48016e..aae3bf6 100644 --- a/app/src/main/java/com/acitelight/aether/viewModel/VideoPlayerViewModel.kt +++ b/app/src/main/java/com/acitelight/aether/viewModel/VideoPlayerViewModel.kt @@ -8,29 +8,34 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableLongStateOf -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.core.net.toUri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.media3.common.MediaItem import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Player.STATE_READY +import androidx.media3.common.Tracks import androidx.media3.common.text.Cue import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultDataSource import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import coil3.ImageLoader import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import com.acitelight.aether.model.KeyImage import com.acitelight.aether.model.Video import com.acitelight.aether.model.VideoQueryIndex import com.acitelight.aether.model.VideoRecord 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.RecentManager +import com.acitelight.aether.service.VideoLibrary import com.acitelight.aether.view.formatTime import com.acitelight.aether.view.hexToString import dagger.hilt.android.lifecycle.HiltViewModel @@ -38,20 +43,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.Request import java.io.File 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 class VideoPlayerViewModel @Inject constructor( @@ -59,6 +56,7 @@ class VideoPlayerViewModel @Inject constructor( val mediaManager: MediaManager, val recentManager: RecentManager, val videoLibrary: VideoLibrary, + val apiClient: ApiClient ) : ViewModel() { var showPlaylist by mutableStateOf(false) var isLandscape by mutableStateOf(false) @@ -79,7 +77,7 @@ class VideoPlayerViewModel @Inject constructor( var renderedFirst = false var videos: List