[optimize] Refactoring API client injection architecture

This commit is contained in:
acite
2025-09-28 14:31:03 +08:00
parent 88392444a4
commit 393419afd7
27 changed files with 231 additions and 272 deletions

View File

@@ -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{

View File

@@ -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)

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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<VideoDownloadItemState> = 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 {

View File

@@ -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<Video> = listOf()
private val httpDataSourceFactory = OkHttpDataSource.Factory(createOkHttp())
private val httpDataSourceFactory = OkHttpDataSource.Factory(apiClient.getClient())
private val defaultDataSourceFactory by lazy {
DefaultDataSource.Factory(
context,
@@ -117,7 +115,7 @@ class VideoPlayerViewModel @Inject constructor(
imageLoader = ImageLoader.Builder(context)
.components {
add(OkHttpNetworkFetcherFactory(createOkHttp()))
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
}
.build()
@@ -156,7 +154,7 @@ class VideoPlayerViewModel @Inject constructor(
)
) {
try {
val client = createOkHttp()
val client = apiClient.getClient()
val headReq = Request.Builder().url(trimmed).head().build()
val headResp = try {
@@ -240,7 +238,7 @@ class VideoPlayerViewModel @Inject constructor(
currentKlass.value = video.klass
currentName.value = video.video.name
currentDuration.longValue = video.video.duration
currentGallery.value = video.getGallery()
currentGallery.value = video.getGallery(apiClient)
player?.apply {
stop()
@@ -249,7 +247,7 @@ class VideoPlayerViewModel @Inject constructor(
recentManager.pushVideo(context, VideoQueryIndex(video.klass, video.id))
val subtitleCandidate = video.getSubtitle().trim()
val subtitleCandidate = video.getSubtitle(apiClient).trim()
val subtitleUri = tryResolveSubtitleUri(subtitleCandidate)
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 mediaItem: MediaItem = if (subtitleUri != null) {

View File

@@ -1,25 +1,19 @@
package com.acitelight.aether.viewModel
import android.app.Application
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.SnapshotStateList
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.Global
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.MediaManager
import com.acitelight.aether.service.RecentManager
import com.acitelight.aether.service.VideoLibrary
import com.tonyodev.fetch2.Status
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -31,7 +25,6 @@ import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@HiltViewModel
@@ -39,12 +32,12 @@ class VideoScreenViewModel @Inject constructor(
val fetchManager: FetchManager,
@ApplicationContext val context: Context,
val mediaManager: MediaManager,
val recentManager: RecentManager,
val videoLibrary: VideoLibrary
val videoLibrary: VideoLibrary,
val apiClient: ApiClient
) : ViewModel() {
private val _tabIndex = mutableIntStateOf(0)
val tabIndex: State<Int> = _tabIndex
var imageLoader: ImageLoader? = null;
var imageLoader: ImageLoader? = null
var menuVisibility = mutableStateOf(false)
var searchFilter = mutableStateOf("")
var doneInit = mutableStateOf(false)
@@ -79,7 +72,7 @@ class VideoScreenViewModel @Inject constructor(
else {
videoLibrary.classes.add("Offline")
videoLibrary.updatingMap[0] = true
videoLibrary.classesMap["Offline"] = mutableStateListOf<Video>()
videoLibrary.classesMap["Offline"] = mutableStateListOf()
val downloaded = fetchManager.getAllDownloadsAsync().filter {
it.status == Status.COMPLETED &&
@@ -101,7 +94,7 @@ class VideoScreenViewModel @Inject constructor(
fun setTabIndex(index: Int) {
viewModelScope.launch()
{
_tabIndex.intValue = index;
_tabIndex.intValue = index
if (videoLibrary.updatingMap[index] == true) return@launch
videoLibrary.updatingMap[index] = true
@@ -126,7 +119,7 @@ class VideoScreenViewModel @Inject constructor(
init {
imageLoader = ImageLoader.Builder(context)
.components {
add(OkHttpNetworkFetcherFactory(createOkHttp()))
add(OkHttpNetworkFetcherFactory(apiClient.getClient()))
}
.build()