diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index 80761d6d..ddc1d33a 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -45,7 +45,6 @@ import okhttp3.Protocol import org.kodein.di.* import org.kodein.di.android.x.androidXModule import xyz.quaver.io.FileX -import xyz.quaver.pupil.db.databaseModule import xyz.quaver.pupil.sources.sourceModule import xyz.quaver.pupil.util.* import java.util.* @@ -54,7 +53,6 @@ class Pupil : Application(), DIAware { override val di: DI by DI.lazy { import(androidXModule(this@Pupil)) - import(databaseModule) import(sourceModule) bind { singleton { NetworkCache(applicationContext) } } diff --git a/app/src/main/java/xyz/quaver/pupil/db/Bookmark.kt b/app/src/main/java/xyz/quaver/pupil/db/Bookmark.kt deleted file mode 100644 index 71758226..00000000 --- a/app/src/main/java/xyz/quaver/pupil/db/Bookmark.kt +++ /dev/null @@ -1,34 +0,0 @@ -package xyz.quaver.pupil.db - -import androidx.lifecycle.LiveData -import androidx.room.* - -@Entity(primaryKeys = ["source", "itemID"]) -data class Bookmark( - val source: String, - val itemID: String, - val timestamp: Long = System.currentTimeMillis() -) - -@Dao -interface BookmarkDao { - @Query("SELECT * FROM bookmark") - fun getAll(): LiveData> - - @Query("SELECT itemID FROM bookmark WHERE source = :source") - fun getAll(source: String): LiveData> - - @Query("SELECT EXISTS(SELECT * FROM bookmark WHERE source = :source AND itemID = :itemID)") - fun contains(source: String, itemID: String): LiveData - fun contains(bookmark: Bookmark) = contains(bookmark.source, bookmark.itemID) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insert(bookmark: Bookmark) - - suspend fun insert(source: String, itemID: String) = insert(Bookmark(source, itemID)) - - @Delete - suspend fun delete(bookmark: Bookmark) - - suspend fun delete(source: String, itemID: String) = delete(Bookmark(source, itemID)) -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/db/Database.kt b/app/src/main/java/xyz/quaver/pupil/db/Database.kt deleted file mode 100644 index e9a2c8d9..00000000 --- a/app/src/main/java/xyz/quaver/pupil/db/Database.kt +++ /dev/null @@ -1,17 +0,0 @@ -package xyz.quaver.pupil.db - -import android.app.Application -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import org.kodein.di.* - -@Database(entities = [History::class, Bookmark::class], version = 1, exportSchema = false) -abstract class AppDatabase : RoomDatabase() { - abstract fun historyDao(): HistoryDao - abstract fun bookmarkDao(): BookmarkDao -} - -val databaseModule = DI.Module("database") { - bind() with singleton { Room.databaseBuilder(instance(), AppDatabase::class.java, "pupil").build() } -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/db/History.kt b/app/src/main/java/xyz/quaver/pupil/db/History.kt deleted file mode 100644 index 3ec9fc58..00000000 --- a/app/src/main/java/xyz/quaver/pupil/db/History.kt +++ /dev/null @@ -1,26 +0,0 @@ -package xyz.quaver.pupil.db - -import androidx.lifecycle.LiveData -import androidx.room.* - -@Entity(primaryKeys = ["source", "itemID"]) -data class History( - val source: String, - val itemID: String, - val timestamp: Long = System.currentTimeMillis() -) - -@Dao -interface HistoryDao { - @Query("SELECT * FROM history") - fun getAll(): LiveData> - - @Query("SELECT itemID FROM history WHERE source = :source") - fun getAll(source: String): LiveData> - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insert(history: History) - - @Delete - suspend fun delete(history: History) -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt index fc681c16..a866f593 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt @@ -20,12 +20,8 @@ package xyz.quaver.pupil.sources.composable import android.app.Application import android.net.Uri -import android.os.Parcelable -import android.util.Log -import android.view.MotionEvent import androidx.compose.animation.core.* import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.* @@ -46,13 +42,11 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -79,25 +73,22 @@ import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlinx.parcelize.Parcelize -import kotlinx.serialization.Serializable import org.kodein.di.DIAware import org.kodein.di.android.closestDI import org.kodein.di.instance import org.kodein.log.LoggerFactory import org.kodein.log.newLogger -import xyz.quaver.graphics.subsampledimage.* -import xyz.quaver.graphics.subsampledimage.ScaleTypes.CENTER_INSIDE +import xyz.quaver.graphics.subsampledimage.ImageSource +import xyz.quaver.graphics.subsampledimage.SubSampledImage +import xyz.quaver.graphics.subsampledimage.SubSampledImageState +import xyz.quaver.graphics.subsampledimage.rememberSubSampledImageState import xyz.quaver.io.FileX import xyz.quaver.pupil.R -import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.proto.ReaderOptions import xyz.quaver.pupil.proto.settingsDataStore -import xyz.quaver.pupil.ui.theme.Orange500 import xyz.quaver.pupil.util.FileXImageSource import xyz.quaver.pupil.util.NetworkCache import xyz.quaver.pupil.util.activity -import xyz.quaver.pupil.util.rememberFileXImageSource import java.util.concurrent.ConcurrentHashMap import kotlin.math.abs import kotlin.math.sign @@ -189,8 +180,6 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar var fullscreen by mutableStateOf(false) - private val database: AppDatabase by instance() - var error by mutableStateOf(false) var imageCount by mutableStateOf(0) diff --git a/app/src/main/java/xyz/quaver/pupil/sources/composable/SearchBase.kt b/app/src/main/java/xyz/quaver/pupil/sources/composable/SearchBase.kt index bb149da6..78ac47e0 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/composable/SearchBase.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/composable/SearchBase.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastFirstOrNull import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.insets.LocalWindowInsets @@ -67,7 +68,7 @@ private enum class NavigationIconState { ARROW } -open class SearchBaseViewModel(app: Application) : AndroidViewModel(app) { +open class SearchBaseViewModel : ViewModel() { val searchResults = mutableStateListOf() var sortModeIndex by mutableStateOf(0) diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Database.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Database.kt new file mode 100644 index 00000000..42f006d9 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Database.kt @@ -0,0 +1,50 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2021 tom5079 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package xyz.quaver.pupil.sources.hitomi + +import androidx.room.* +import kotlinx.coroutines.flow.Flow + +@Entity +data class Favorite( + @PrimaryKey val item: String, + val timestamp: Long = System.currentTimeMillis() +) + +@Dao +interface FavoritesDao { + @Query("SELECT * FROM favorite") + fun getAll(): Flow> + + @Query("SELECT EXISTS(SELECT * FROM favorite WHERE item = :item)") + fun contains(item: String): Flow + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(favorite: Favorite) + suspend fun insert(item: String) = insert(Favorite(item)) + + @Delete + suspend fun delete(favorite: Favorite) + suspend fun delete(item: String) = delete(Favorite(item)) +} + +@Database(entities = [Favorite::class], version = 1) +abstract class HitomiDatabase : RoomDatabase() { + abstract fun favoritesDao(): FavoritesDao +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt index 2dfe9856..674d7f6d 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt @@ -33,7 +33,6 @@ import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -47,20 +46,22 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.compose.navigation +import androidx.room.Room import com.google.accompanist.insets.LocalWindowInsets import com.google.accompanist.insets.rememberInsetsPaddingValues import com.google.accompanist.insets.ui.Scaffold import com.google.accompanist.insets.ui.TopAppBar import io.ktor.client.* import kotlinx.coroutines.launch -import org.kodein.di.DIAware +import org.kodein.di.* import org.kodein.di.android.closestDI +import org.kodein.di.android.subDI import org.kodein.di.compose.rememberInstance -import org.kodein.di.instance +import org.kodein.di.compose.rememberViewModel +import org.kodein.di.compose.withDI import org.kodein.log.LoggerFactory import org.kodein.log.newLogger import xyz.quaver.pupil.R -import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.proto.settingsDataStore import xyz.quaver.pupil.sources.Source import xyz.quaver.pupil.sources.composable.* @@ -70,38 +71,42 @@ import xyz.quaver.pupil.sources.hitomi.lib.getGalleryInfo import xyz.quaver.pupil.sources.hitomi.lib.getReferer import xyz.quaver.pupil.sources.hitomi.lib.imageUrlFromImage import xyz.quaver.pupil.ui.theme.Orange500 +import java.util.* @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) class Hitomi(app: Application) : Source(), DIAware { - override val di by closestDI(app) + override val di by subDI(closestDI(app)) { + bindSingleton { + Room.databaseBuilder(app, HitomiDatabase::class.java, name).build() + } + + bindProvider { HitomiSearchResultViewModel(instance()) } + } private val client: HttpClient by instance() private val logger = newLogger(LoggerFactory.default) - private val database: AppDatabase by instance() - private val bookmarkDao = database.bookmarkDao() - override val name: String = "hitomi.la" override val iconResID: Int = R.drawable.hitomi override fun NavGraphBuilder.navGraph(navController: NavController) { navigation(startDestination = "hitomi.la/search", route = name) { - composable("hitomi.la/search") { Search(navController) } - composable("hitomi.la/reader/{itemID}") { Reader(navController) } + composable("hitomi.la/search") { withDI(di) { Search(navController) } } + composable("hitomi.la/reader/{itemID}") { withDI(di) { Reader(navController) } } } } @Composable fun Search(navController: NavController) { - val model: HitomiSearchResultViewModel = viewModel() - val database: AppDatabase by rememberInstance() - val bookmarkDao = remember { database.bookmarkDao() } + val model: HitomiSearchResultViewModel by rememberViewModel() + val database: HitomiDatabase by rememberInstance() + val favoritesDao = remember { database.favoritesDao() } val coroutineScope = rememberCoroutineScope() - val bookmarks by bookmarkDao.getAll(name).observeAsState() - val bookmarkSet by derivedStateOf { - bookmarks?.toSet() ?: emptySet() + val favorites by favoritesDao.getAll().collectAsState(emptyList()) + val favoritesSet by derivedStateOf { + Collections.unmodifiableSet(favorites.mapTo(mutableSetOf()) { it.item }) } val context = LocalContext.current @@ -200,11 +205,11 @@ class Hitomi(app: Application) : Source(), DIAware { items(model.searchResults) { DetailedSearchResult( it, - bookmarks = bookmarkSet, - onBookmarkToggle = { + favorites = favoritesSet, + onFavoriteToggle = { coroutineScope.launch { - if (it in bookmarkSet) bookmarkDao.delete(name, it) - else bookmarkDao.insert(name, it) + if (it in favoritesSet) favoritesDao.delete(it) + else favoritesDao.insert(it) } } ) { result -> @@ -219,8 +224,8 @@ class Hitomi(app: Application) : Source(), DIAware { fun Reader(navController: NavController) { val model: ReaderBaseViewModel = viewModel() - val database: AppDatabase by rememberInstance() - val bookmarkDao = database.bookmarkDao() + val database: HitomiDatabase by rememberInstance() + val favoritesDao = remember { database.favoritesDao() } val coroutineScope = rememberCoroutineScope() @@ -228,7 +233,7 @@ class Hitomi(app: Application) : Source(), DIAware { if (itemID == null) model.error = true - val bookmark by bookmarkDao.contains(name, itemID ?: "").observeAsState(false) + val isFavorite by favoritesDao.contains(itemID ?: "").collectAsState(false) val galleryInfo by produceState(null) { runCatching { val galleryID = itemID!!.toInt() @@ -271,13 +276,13 @@ class Hitomi(app: Application) : Source(), DIAware { IconButton(onClick = { itemID?.let { coroutineScope.launch { - if (bookmark) bookmarkDao.delete(name, it) - else bookmarkDao.insert(name, it) + if (isFavorite) favoritesDao.delete(it) + else favoritesDao.insert(it) } } }) { Icon( - if (bookmark) Icons.Default.Star else Icons.Default.StarOutline, + if (isFavorite) Icons.Default.Star else Icons.Default.StarOutline, contentDescription = null, tint = Orange500 ) diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/HitomiSearchResultViewModel.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/HitomiSearchResultViewModel.kt index bff10ab0..bf63237a 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/HitomiSearchResultViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/HitomiSearchResultViewModel.kt @@ -18,7 +18,6 @@ package xyz.quaver.pupil.sources.hitomi -import android.app.Application import android.util.LruCache import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -29,12 +28,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import kotlinx.coroutines.yield -import org.kodein.di.DIAware -import org.kodein.di.android.closestDI -import org.kodein.di.instance -import org.kodein.log.LoggerFactory -import org.kodein.log.newLogger -import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.sources.composable.SearchBaseViewModel import xyz.quaver.pupil.sources.hitomi.lib.GalleryBlock import xyz.quaver.pupil.sources.hitomi.lib.doSearch @@ -43,16 +36,9 @@ import kotlin.math.ceil import kotlin.math.max import kotlin.math.min -class HitomiSearchResultViewModel(app: Application) : SearchBaseViewModel(app), DIAware { - override val di by closestDI(app) - - private val logger = newLogger(LoggerFactory.default) - - private val client: HttpClient by instance() - - private val database: AppDatabase by instance() - private val bookmarkDao = database.bookmarkDao() - +class HitomiSearchResultViewModel( + private val client: HttpClient +) : SearchBaseViewModel() { private var cachedQuery: String? = null private var cachedSortByPopularity: Boolean? = null private val cache = mutableListOf() diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/composable/SearchResultEntry.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/composable/SearchResultEntry.kt index 3fd2a4f8..99b7667c 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/composable/SearchResultEntry.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/composable/SearchResultEntry.kt @@ -19,7 +19,6 @@ package xyz.quaver.pupil.sources.hitomi.composable import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -101,8 +100,8 @@ private fun String.wordCapitalize() : String { @Composable fun DetailedSearchResult( result: HitomiSearchResult, - bookmarks: Set, - onBookmarkToggle: (String) -> Unit = { }, + favorites: Set, + onFavoriteToggle: (String) -> Unit = { }, onClick: (HitomiSearchResult) -> Unit = { } ) { val painter = rememberImagePainter(result.thumbnail) @@ -169,8 +168,8 @@ fun DetailedSearchResult( key(result.tags) { TagGroup( tags = result.tags, - bookmarks, - onBookmarkToggle = onBookmarkToggle + favorites, + onFavoriteToggle = onFavoriteToggle ) } } @@ -192,13 +191,13 @@ fun DetailedSearchResult( ) Icon( - if (result.itemID in bookmarks) Icons.Default.Star else Icons.Default.StarOutline, + if (result.itemID in favorites) Icons.Default.Star else Icons.Default.StarOutline, contentDescription = null, tint = Orange500, modifier = Modifier .size(24.dp) .clickable { - onBookmarkToggle(result.itemID) + onFavoriteToggle(result.itemID) } ) } @@ -210,20 +209,20 @@ fun DetailedSearchResult( @Composable fun TagGroup( tags: List, - bookmarks: Set, - onBookmarkToggle: (String) -> Unit = { } + favorites: Set, + onFavoriteToggle: (String) -> Unit = { } ) { var isFolded by remember { mutableStateOf(true) } - val bookmarkedTagsInList = bookmarks intersect tags.toSet() + val favoriteTagsInList = favorites intersect tags.toSet() FlowRow(Modifier.padding(0.dp, 16.dp)) { - tags.sortedBy { if (bookmarkedTagsInList.contains(it)) 0 else 1 } + tags.sortedBy { if (favoriteTagsInList.contains(it)) 0 else 1 } .let { (if (isFolded) it.take(10) else it) }.forEach { tag -> TagChip( tag = tag, - isFavorite = bookmarkedTagsInList.contains(tag), - onFavoriteClick = onBookmarkToggle + isFavorite = favoriteTagsInList.contains(tag), + onFavoriteClick = onFavoriteToggle ) } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Database.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Database.kt new file mode 100644 index 00000000..e150823d --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Database.kt @@ -0,0 +1,70 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2021 tom5079 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package xyz.quaver.pupil.sources.manatoki + +import androidx.room.* +import kotlinx.coroutines.flow.Flow + +@Entity +data class Favorite( + @PrimaryKey val itemID: String +) + +@Entity +data class Bookmark( + @PrimaryKey val itemID: String, + val page: Int +) + +@Entity +data class History( + @PrimaryKey val itemID: String, + val page: Int +) + +@Dao +interface FavoriteDao { + @Query("SELECT EXISTS(SELECT * FROM favorite WHERE itemID = :itemID)") + fun contains(itemID: String): Flow + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(favorite: Favorite) + suspend fun insert(itemID: String) = insert(Favorite(itemID)) + + @Delete + suspend fun delete(favorite: Favorite) + suspend fun delete(itemID: String) = delete(Favorite(itemID)) +} + +@Dao +interface BookmarkDao { + +} + +@Dao +interface HistoryDao { + +} + +@Database(entities = [Favorite::class, Bookmark::class, History::class], version = 1) +abstract class ManatokiDatabase: RoomDatabase() { + abstract fun favoriteDao(): FavoriteDao + abstract fun bookmarkDao(): BookmarkDao + abstract fun historyDao(): HistoryDao +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt index d2a5e0c9..d0871c17 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/Manatoki.kt @@ -32,16 +32,27 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navigation +import androidx.room.Room import org.kodein.di.DIAware import org.kodein.di.android.closestDI +import org.kodein.di.android.subDI +import org.kodein.di.bindProvider +import org.kodein.di.bindSingleton +import org.kodein.di.compose.withDI +import org.kodein.di.instance import org.kodein.log.LoggerFactory +import org.kodein.log.frontend.defaultLogFrontend import org.kodein.log.newLogger +import org.kodein.log.withShortPackageKeepLast import xyz.quaver.pupil.R import xyz.quaver.pupil.sources.Source import xyz.quaver.pupil.sources.manatoki.composable.Main import xyz.quaver.pupil.sources.manatoki.composable.Reader import xyz.quaver.pupil.sources.manatoki.composable.Recent import xyz.quaver.pupil.sources.manatoki.composable.Search +import xyz.quaver.pupil.sources.manatoki.viewmodel.MainViewModel +import xyz.quaver.pupil.sources.manatoki.viewmodel.RecentViewModel +import xyz.quaver.pupil.sources.manatoki.viewmodel.SearchViewModel @OptIn( ExperimentalMaterialApi::class, @@ -50,19 +61,27 @@ import xyz.quaver.pupil.sources.manatoki.composable.Search ExperimentalComposeUiApi::class ) class Manatoki(app: Application) : Source(), DIAware { - override val di by closestDI(app) + override val di by subDI(closestDI(app)) { + bindSingleton { + Room.databaseBuilder( + app, ManatokiDatabase::class.java, name + ).build() + } - private val logger = newLogger(LoggerFactory.default) + bindProvider { MainViewModel(instance()) } + bindProvider { RecentViewModel(instance()) } + bindProvider { SearchViewModel(instance()) } + } override val name = "manatoki.net" override val iconResID = R.drawable.manatoki override fun NavGraphBuilder.navGraph(navController: NavController) { navigation(route = name, startDestination = "manatoki.net/") { - composable("manatoki.net/") { Main(navController) } - composable("manatoki.net/reader/{itemID}") { Reader(navController) } - composable("manatoki.net/search") { Search(navController) } - composable("manatoki.net/recent") { Recent(navController) } + composable("manatoki.net/") { withDI(di) { Main(navController) } } + composable("manatoki.net/reader/{itemID}") { withDI(di) { Reader(navController) } } + composable("manatoki.net/search") { withDI(di) { Search(navController) } } + composable("manatoki.net/recent") { withDI(di) { Recent(navController) } } } } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Main.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Main.kt index c6b90ec2..60e5a645 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Main.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Main.kt @@ -18,7 +18,6 @@ package xyz.quaver.pupil.sources.manatoki.composable -import android.util.Log import androidx.activity.compose.BackHandler import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -40,7 +39,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.google.accompanist.insets.LocalWindowInsets import com.google.accompanist.insets.navigationBarsPadding @@ -48,9 +46,9 @@ import com.google.accompanist.insets.rememberInsetsPaddingValues import com.google.accompanist.insets.ui.Scaffold import com.google.accompanist.insets.ui.TopAppBar import io.ktor.client.* -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.kodein.di.compose.rememberInstance +import org.kodein.di.compose.rememberViewModel import xyz.quaver.pupil.R import xyz.quaver.pupil.proto.settingsDataStore import xyz.quaver.pupil.sources.composable.SourceSelectDialog @@ -62,7 +60,7 @@ import xyz.quaver.pupil.sources.manatoki.viewmodel.MainViewModel @ExperimentalMaterialApi @Composable fun Main(navController: NavController) { - val model: MainViewModel = viewModel() + val model: MainViewModel by rememberViewModel() val client: HttpClient by rememberInstance() diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Reader.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Reader.kt index e24640f6..dbf5ffd3 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Reader.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Reader.kt @@ -34,7 +34,6 @@ import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -57,10 +56,11 @@ import io.ktor.client.* import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.kodein.di.compose.rememberInstance +import org.kodein.di.compose.rememberViewModel import xyz.quaver.pupil.R -import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.sources.composable.ReaderBase import xyz.quaver.pupil.sources.composable.ReaderBaseViewModel +import xyz.quaver.pupil.sources.manatoki.ManatokiDatabase import xyz.quaver.pupil.sources.manatoki.MangaListing import xyz.quaver.pupil.sources.manatoki.ReaderInfo import xyz.quaver.pupil.sources.manatoki.getItem @@ -79,8 +79,9 @@ fun Reader(navController: NavController) { val client: HttpClient by rememberInstance() - val database: AppDatabase by rememberInstance() - val bookmarkDao = database.bookmarkDao() + val database: ManatokiDatabase by rememberInstance() + val favoriteDao = remember { database.favoriteDao() } + val bookmarkDao = remember { database.bookmarkDao() } val coroutineScope = rememberCoroutineScope() @@ -98,7 +99,7 @@ fun Reader(navController: NavController) { else model.error = true } - val bookmark by bookmarkDao.contains("manatoki.net", itemID ?: "").observeAsState(false) + val isFavorite by favoriteDao.contains(itemID ?: "").collectAsState(false) val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) } @@ -174,13 +175,13 @@ fun Reader(navController: NavController) { IconButton(onClick = { itemID?.let { coroutineScope.launch { - if (bookmark) bookmarkDao.delete("manatoki.net", it) - else bookmarkDao.insert("manatoki.net", it) + if (isFavorite) favoriteDao.delete(it) + else favoriteDao.insert(it) } } }) { Icon( - if (bookmark) Icons.Default.Star else Icons.Default.StarOutline, + if (isFavorite) Icons.Default.Star else Icons.Default.StarOutline, contentDescription = null, tint = Orange500 ) diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Recent.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Recent.kt index 58350a82..92eb5c19 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Recent.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Recent.kt @@ -42,6 +42,7 @@ import com.google.accompanist.insets.ui.TopAppBar import io.ktor.client.* import kotlinx.coroutines.launch import org.kodein.di.compose.rememberInstance +import org.kodein.di.compose.rememberViewModel import xyz.quaver.pupil.sources.composable.OverscrollPager import xyz.quaver.pupil.sources.manatoki.MangaListing import xyz.quaver.pupil.sources.manatoki.getItem @@ -51,7 +52,7 @@ import xyz.quaver.pupil.sources.manatoki.viewmodel.RecentViewModel @ExperimentalMaterialApi @Composable fun Recent(navController: NavController) { - val model: RecentViewModel = viewModel() + val model: RecentViewModel by rememberViewModel() val client: HttpClient by rememberInstance() diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Search.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Search.kt index 28f13c6e..27599553 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Search.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/composable/Search.kt @@ -58,6 +58,7 @@ import com.google.accompanist.insets.ui.TopAppBar import io.ktor.client.* import kotlinx.coroutines.launch import org.kodein.di.compose.rememberInstance +import org.kodein.di.compose.rememberViewModel import xyz.quaver.pupil.sources.composable.ModalTopSheetLayout import xyz.quaver.pupil.sources.composable.ModalTopSheetState import xyz.quaver.pupil.sources.composable.OverscrollPager @@ -70,7 +71,7 @@ import xyz.quaver.pupil.sources.manatoki.viewmodel.* @ExperimentalMaterialApi @Composable fun Search(navController: NavController) { - val model: SearchViewModel = viewModel() + val model: SearchViewModel by rememberViewModel() val client: HttpClient by rememberInstance() diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/util.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/util.kt index 2079907f..38eb39cf 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/util.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/util.kt @@ -144,7 +144,7 @@ suspend fun HttpClient.getItem( }.toString() val urls = Jsoup.parse(htmlData) - .select("img[^data-]:not([style]):not([src*=loading])").also { Log.d("PUPILD", it.size.toString()) } + .select("img[^data-]:not([style])") .map { it.attributes() .first { it.key.startsWith("data-") } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/MainViewModel.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/MainViewModel.kt index aeff3d34..3325868b 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/MainViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/MainViewModel.kt @@ -21,6 +21,7 @@ package xyz.quaver.pupil.sources.manatoki.viewmodel import android.app.Application import androidx.compose.runtime.mutableStateListOf import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.ktor.client.* import io.ktor.client.request.* @@ -46,13 +47,9 @@ data class TopWeekly( val count: String ) -class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { - override val di by closestDI(app) - - private val logger = newLogger(LoggerFactory.default) - - private val client: HttpClient by instance() - +class MainViewModel( + private val client: HttpClient +) : ViewModel() { val recentUpload = mutableStateListOf() val mangaList = mutableStateListOf() val topWeekly = mutableStateListOf() @@ -109,7 +106,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { topWeekly.add(TopWeekly(itemID, title, count)) } }.onFailure { - logger.warning(it) + TODO() } } } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/RecentViewModel.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/RecentViewModel.kt index b7b2833f..5dc42d49 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/RecentViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/RecentViewModel.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.ktor.client.* import io.ktor.client.request.* @@ -37,11 +38,9 @@ import org.kodein.di.instance import xyz.quaver.pupil.sources.manatoki.composable.Thumbnail import xyz.quaver.pupil.sources.manatoki.manatokiUrl -class RecentViewModel(app: Application): AndroidViewModel(app), DIAware { - override val di by closestDI(app) - - private val client: HttpClient by instance() - +class RecentViewModel( + private val client: HttpClient +): ViewModel() { var page by mutableStateOf(1) var loading by mutableStateOf(false) diff --git a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/SearchViewModel.kt b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/SearchViewModel.kt index ddecc9f8..d7d40246 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/SearchViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/manatoki/viewmodel/SearchViewModel.kt @@ -23,6 +23,7 @@ import android.os.Parcelable import android.util.Log import androidx.compose.runtime.* import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel import io.ktor.client.* import io.ktor.client.request.* import kotlinx.coroutines.Job @@ -115,13 +116,11 @@ val availableSst = mapOf( "as_bookmark" to "북마크순" ) -class SearchViewModel(app: Application) : AndroidViewModel(app), DIAware { - override val di by closestDI(app) - +class SearchViewModel( + private val client: HttpClient +) : ViewModel() { private val logger = newLogger(LoggerFactory.default) - private val client: HttpClient by instance() - // 발행 var publish by mutableStateOf("") // 초성 diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt index f23e8407..c7759a49 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt @@ -39,7 +39,6 @@ import xyz.quaver.graphics.subsampledimage.ImageSource import xyz.quaver.graphics.subsampledimage.newBitmapRegionDecoder import xyz.quaver.io.FileX import xyz.quaver.io.util.inputStream -import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.sources.SourceEntries import java.security.MessageDigest @@ -55,17 +54,6 @@ val JsonElement.content fun DIAware.source(source: String) = lazy { direct.source(source) } fun DirectDIAware.source(source: String) = instance().toMap()[source]!! -fun DIAware.database() = lazy { direct.database() } -fun DirectDIAware.database() = instance() - -fun View.hide() { - visibility = View.INVISIBLE -} - -fun View.show() { - visibility = View.VISIBLE -} - class FileXImageSource(val file: FileX): ImageSource { private val decoder by lazy { file.inputStream()!!.use {