diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6a57076b..16390d4e 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -41,6 +41,7 @@
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 2ca85270..c70e60b6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -58,6 +58,14 @@ android {
jvmTarget = "1.8"
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
+ packagingOptions {
+ resources.excludes.addAll(
+ listOf(
+ "META-INF/AL2.0",
+ "META-INF/LGPL2.1"
+ )
+ )
+ }
}
dependencies {
diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
index 26ecdbf8..a43a9cf2 100644
--- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
@@ -23,6 +23,15 @@ package xyz.quaver.pupil
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
+import com.google.api.Http
+import io.ktor.client.*
+import io.ktor.client.call.*
+import io.ktor.client.engine.okhttp.*
+import io.ktor.client.request.*
+import io.ktor.client.statement.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
index 3656de96..f2ef7917 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
@@ -28,7 +28,6 @@ import org.kodein.di.*
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.sources.hitomi.Hitomi
interface ItemInfo : Parcelable {
val source: String
@@ -39,7 +38,7 @@ interface ItemInfo : Parcelable {
@Parcelize
class DefaultSearchSuggestion(override val body: String) : SearchSuggestion
-data class SearchResultEvent(val type: Type, val payload: String) {
+data class SearchResultEvent(val type: Type, val itemID: String, val payload: Parcelable? = null) {
enum class Type {
OPEN_READER,
OPEN_DETAILS,
@@ -75,7 +74,8 @@ val sourceModule = DI.Module(name = "source") {
bindSet()
listOf<(Application) -> (Source)>(
- { Hitomi(it) }
+ { Hitomi(it) },
+ { Hiyobi_io(it) }
).forEach { source ->
inSet { singleton { source.invoke(instance()).let { it.name to it } } }
}
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/History.kt b/app/src/main/java/xyz/quaver/pupil/sources/History.kt
index fa4809b7..600c052a 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/History.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/History.kt
@@ -26,11 +26,8 @@ import kotlinx.coroutines.launch
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.direct
-import org.kodein.di.instance
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
-import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.util.database
-import xyz.quaver.pupil.util.source
class History(override val di: DI) : Source(), DIAware {
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt
index 0f48060d..ef4d75f0 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package xyz.quaver.pupil.sources.hitomi
+package xyz.quaver.pupil.sources
import android.app.Application
import android.view.LayoutInflater
@@ -40,12 +40,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.LiveData
-import androidx.room.*
import coil.annotation.ExperimentalCoilApi
import coil.compose.rememberImagePainter
import com.google.accompanist.flowlayout.FlowRow
@@ -57,7 +54,6 @@ import kotlinx.coroutines.sync.withLock
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
-import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.di.instance
@@ -69,9 +65,9 @@ import xyz.quaver.hitomi.*
import xyz.quaver.pupil.R
import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.db.Bookmark
-import xyz.quaver.pupil.sources.ItemInfo
-import xyz.quaver.pupil.sources.SearchResultEvent
-import xyz.quaver.pupil.sources.Source
+import xyz.quaver.pupil.ui.theme.Blue700
+import xyz.quaver.pupil.ui.theme.Orange500
+import xyz.quaver.pupil.ui.theme.Pink600
import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.wordCapitalize
import kotlin.math.max
@@ -152,7 +148,7 @@ class Hitomi(app: Application) : Source(), DIAware {
var cachedSortMode: Int = -1
private val cache = mutableListOf()
- override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> = coroutineScope { withContext(Dispatchers.IO) {
+ override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> = withContext(Dispatchers.IO) {
if (cachedQuery != query || cachedSortMode != sortMode || cache.isEmpty()) {
cachedQuery = null
cache.clear()
@@ -179,8 +175,8 @@ class Hitomi(app: Application) : Source(), DIAware {
channel.close()
}
- Pair(channel, cache.size)
- } }
+ channel to cache.size
+ }
override suspend fun suggestion(query: String) : List {
return getSuggestionsForQuery(query.takeLastWhile { !it.isWhitespace() }).map {
@@ -336,10 +332,10 @@ class Hitomi(app: Application) : Source(), DIAware {
}
val (surfaceColor, textTint) = when {
- isFavorite -> Pair(colorResource(id = R.color.material_orange_500), Color.White)
+ isFavorite -> Pair(Orange500, Color.White)
else -> when (tagParts[0]) {
- "male" -> Pair(colorResource(id = R.color.material_blue_700), Color.White)
- "female" -> Pair(colorResource(id = R.color.material_pink_600), Color.White)
+ "male" -> Pair(Blue700, Color.White)
+ "female" -> Pair(Pink600, Color.White)
else -> Pair(MaterialTheme.colors.background, MaterialTheme.colors.onBackground)
}
}
@@ -394,7 +390,7 @@ class Hitomi(app: Application) : Source(), DIAware {
var isFolded by remember { mutableStateOf(true) }
val bookmarkedTags by bookmarkDao.getAll(name).observeAsState(emptyList())
- val bookmarkedTagsInList = bookmarkedTags.toSet() intersect tags
+ val bookmarkedTagsInList = bookmarkedTags.toSet() intersect tags.toSet()
FlowRow(Modifier.padding(0.dp, 16.dp)) {
tags.sortedBy { if (bookmarkedTagsInList.contains(it)) 0 else 1 }.let { (if (isFolded) it.take(10) else it) }.forEach { tag ->
@@ -454,7 +450,9 @@ class Hitomi(app: Application) : Source(), DIAware {
val painter = rememberImagePainter(itemInfo.thumbnail)
- Column {
+ Column(
+ modifier = Modifier.clickable { onEvent(SearchResultEvent(SearchResultEvent.Type.OPEN_READER, itemInfo.itemID, itemInfo)) }
+ ) {
Row {
Image(
painter = painter,
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi_io.kt b/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi_io.kt
new file mode 100644
index 00000000..83b59797
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Hiyobi_io.kt
@@ -0,0 +1,441 @@
+/*
+ * 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
+
+import android.app.Application
+import android.os.Parcelable
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Female
+import androidx.compose.material.icons.filled.Male
+import androidx.compose.material.icons.filled.Star
+import androidx.compose.material.icons.outlined.StarOutline
+import androidx.compose.runtime.*
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import coil.annotation.ExperimentalCoilApi
+import coil.compose.rememberImagePainter
+import com.google.accompanist.flowlayout.FlowRow
+import io.ktor.client.*
+import io.ktor.client.request.*
+import io.ktor.http.*
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.Channel
+import kotlinx.parcelize.Parcelize
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.int
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.jsonPrimitive
+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.floatingsearchview.suggestions.model.SearchSuggestion
+import xyz.quaver.pupil.R
+import xyz.quaver.pupil.db.AppDatabase
+import xyz.quaver.pupil.db.Bookmark
+import xyz.quaver.pupil.ui.theme.Blue700
+import xyz.quaver.pupil.ui.theme.Orange500
+import xyz.quaver.pupil.ui.theme.Pink600
+import xyz.quaver.pupil.util.content
+import xyz.quaver.pupil.util.get
+import xyz.quaver.pupil.util.wordCapitalize
+
+@Serializable
+@Parcelize
+data class Tag(
+ val male: Int?,
+ val female: Int?,
+ val tag: String
+) : Parcelable {
+ override fun toString(): String {
+ val stringBuilder = StringBuilder()
+
+ stringBuilder.append(when {
+ male != null -> "male"
+ female != null -> "female"
+ else -> "tag"
+ })
+ stringBuilder.append(':')
+ stringBuilder.append(tag)
+
+ return stringBuilder.toString()
+ }
+}
+
+@Serializable
+@Parcelize
+data class HiyobiItemInfo(
+ override val itemID: String,
+ override val title: String,
+ val thumbnail: String,
+ val artists: List,
+ val series: List,
+ val type: String,
+ val date: String,
+ val bookmark: Unit?,
+ val tags: List,
+ val commentCount: Int,
+ val pageCount: Int
+): ItemInfo {
+ override val source: String
+ get() = "hiyobi.io"
+}
+
+@Serializable
+data class Manga(
+ val mangaId: Int,
+ val title: String,
+ val artist: List,
+ val thumbnail: String,
+ val series: List,
+ val type: String,
+ val date: String,
+ val bookmark: Unit?,
+ val tags: List,
+ val commentCount: Int,
+ val pageCount: Int
+)
+
+@Serializable
+data class QueryManga(
+ val nowPage: Int,
+ val maxPage: Int,
+ val manga: List
+)
+
+@Serializable
+data class SearchResultData(
+ val queryManga: QueryManga
+)
+
+@Serializable
+data class SearchResult(
+ val data: SearchResultData
+)
+
+class Hiyobi_io(app: Application): Source(), DIAware {
+ override val di by closestDI(app)
+
+ private val logger = newLogger(LoggerFactory.default)
+
+ private val database: AppDatabase by instance()
+ private val bookmarkDao = database.bookmarkDao()
+
+ override val name = "hiyobi.io"
+ override val iconResID = R.drawable.hitomi
+ override val preferenceID = 0
+ override val availableSortMode = emptyList()
+
+ private val client: HttpClient by instance()
+
+ private suspend fun query(page: Int, tags: String): SearchResult {
+ val query = "{queryManga(page:$page,tags:$tags){nowPage maxPage manga{mangaId title artist thumbnail series type date bookmark tags{male female tag} commentCount pageCount}}}"
+
+ return client.get("https://api.hiyobi.io/api?query=$query")
+ }
+
+ private suspend fun totalCount(tags: String): Int {
+ val firstPageQuery = "{queryManga(page:1,tags:$tags){maxPage}}"
+ val maxPage = client.get(
+ "https://api.hiyobi.io/api?query=$firstPageQuery"
+ )["data"]!!["queryManga"]!!["maxPage"]!!.jsonPrimitive.int
+
+ val lastPageQuery = "{queryManga(page:$maxPage,tags:$tags){manga{mangaId}}}"
+ val lastPageCount = client.get(
+ "https://api.hiyobi.io/api?query=$lastPageQuery"
+ )["data"]!!["queryManga"]!!["manga"]!!.jsonArray.size
+
+ return (maxPage-1)*25+lastPageCount
+ }
+
+ override suspend fun search(
+ query: String,
+ range: IntRange,
+ sortMode: Int
+ ): Pair, Int> = withContext(Dispatchers.IO) {
+ val channel = Channel()
+
+ val tags = parseQuery(query)
+
+ logger.info {
+ tags
+ }
+
+ CoroutineScope(Dispatchers.IO).launch {
+ (range.first/25+1 .. range.last/25+1).map { page ->
+ page to async { query(page, tags) }
+ }.forEach { (page, result) ->
+ result.await().data.queryManga.manga.forEachIndexed { index, manga ->
+ if ((page-1)*25+index in range) channel.send(transform(manga))
+ }
+ }
+
+ channel.close()
+ }
+
+ channel to totalCount(tags)
+ }
+
+ override suspend fun suggestion(query: String): List {
+ return emptyList()
+ }
+
+ override suspend fun images(itemID: String): List = withContext(Dispatchers.IO) {
+ val query = "{getManga(mangaId:$itemID){urls}}"
+
+ client.post("https://api.hiyobi.io/api") {
+ contentType(ContentType.Application.Json)
+ body = mapOf("query" to query)
+ }["data"]!!["getManga"]!!["urls"]!!.jsonArray.map { "https://api.hiyobi.io/${it.content!!}" }
+ }
+
+ override suspend fun info(itemID: String): ItemInfo {
+ TODO("Not yet implemented")
+ }
+
+ @OptIn(ExperimentalMaterialApi::class)
+ @Composable
+ fun TagChip(tag: Tag, isFavorite: Boolean, onClick: ((Tag) -> Unit)? = null, onFavoriteClick: ((Tag) -> Unit)? = null) {
+ val icon = when {
+ tag.male != null -> Icons.Filled.Male
+ tag.female != null -> Icons.Filled.Female
+ else -> null
+ }
+
+ val (surfaceColor, textTint) = when {
+ isFavorite -> Pair(Orange500, Color.White)
+ else -> when {
+ tag.male != null -> Pair(Blue700, Color.White)
+ tag.female != null -> Pair(Pink600, Color.White)
+ else -> Pair(MaterialTheme.colors.background, MaterialTheme.colors.onBackground)
+ }
+ }
+
+ val starIcon = if (isFavorite) Icons.Filled.Star else Icons.Outlined.StarOutline
+
+ Surface(
+ modifier = Modifier.padding(2.dp),
+ onClick = { onClick?.invoke(tag) },
+ shape = RoundedCornerShape(16.dp),
+ color = surfaceColor,
+ elevation = 2.dp
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ if (icon != null)
+ Icon(
+ icon,
+ contentDescription = "Icon",
+ modifier = Modifier
+ .padding(4.dp)
+ .size(24.dp),
+ tint = Color.White
+ )
+ else
+ Box(Modifier.size(16.dp))
+
+ Text(
+ tag.tag,
+ color = textTint,
+ style = MaterialTheme.typography.body2
+ )
+
+ Icon(
+ starIcon,
+ contentDescription = "Favorites",
+ modifier = Modifier
+ .padding(8.dp)
+ .size(16.dp)
+ .clip(CircleShape)
+ .clickable { onFavoriteClick?.invoke(tag) },
+ tint = textTint
+ )
+ }
+ }
+ }
+
+ @OptIn(ExperimentalMaterialApi::class)
+ @Composable
+ fun TagGroup(tags: List) {
+ var isFolded by remember { mutableStateOf(true) }
+ val bookmarkedTags by bookmarkDao.getAll(name).observeAsState(emptyList())
+
+ val bookmarkedTagsInList = tags.filter { it.toString() in bookmarkedTags }
+
+ FlowRow(Modifier.padding(0.dp, 16.dp)) {
+ tags.sortedBy { if (bookmarkedTagsInList.contains(it)) 0 else 1 }.let { (if (isFolded) it.take(10) else it) }.forEach { tag ->
+ TagChip(
+ tag = tag,
+ isFavorite = bookmarkedTagsInList.contains(tag),
+ onFavoriteClick = {
+ val bookmarkTag = Bookmark(name, it.toString())
+
+ CoroutineScope(Dispatchers.IO).launch {
+ if (bookmarkedTagsInList.contains(it))
+ bookmarkDao.delete(bookmarkTag)
+ else
+ bookmarkDao.insert(bookmarkTag)
+ }
+ }
+ )
+ }
+
+ if (isFolded && tags.size > 10)
+ Surface(
+ modifier = Modifier.padding(2.dp),
+ color = MaterialTheme.colors.background,
+ shape = RoundedCornerShape(16.dp),
+ elevation = 2.dp,
+ onClick = { isFolded = false }
+ ) {
+ Text(
+ "…",
+ modifier = Modifier.padding(16.dp, 8.dp),
+ color = MaterialTheme.colors.onBackground,
+ style = MaterialTheme.typography.body2
+ )
+ }
+ }
+ }
+
+ @OptIn(ExperimentalCoilApi::class)
+ @Composable
+ override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
+ itemInfo as HiyobiItemInfo
+
+ val painter = rememberImagePainter(itemInfo.thumbnail)
+
+ Row(
+ modifier = Modifier.clickable {
+ onEvent(SearchResultEvent(SearchResultEvent.Type.OPEN_READER, itemInfo.itemID, itemInfo))
+ }
+ ) {
+ Image(
+ painter = painter,
+ contentDescription = null,
+ modifier = Modifier
+ .requiredWidth(150.dp)
+ .aspectRatio(
+ with(painter.intrinsicSize) { if (this == Size.Unspecified) 1f else width / height },
+ true
+ )
+ .padding(0.dp, 0.dp, 8.dp, 0.dp)
+ .align(Alignment.CenterVertically),
+ contentScale = ContentScale.FillWidth
+ )
+
+ Column {
+ Text(
+ itemInfo.title,
+ style = MaterialTheme.typography.h6,
+ color = MaterialTheme.colors.onSurface
+ )
+
+ val artistStringBuilder = StringBuilder()
+
+ with (itemInfo.artists) {
+ if (this.isNotEmpty())
+ artistStringBuilder.append(this.joinToString(", ") { it.wordCapitalize() })
+ }
+
+ if (artistStringBuilder.isNotEmpty())
+ Text(
+ artistStringBuilder.toString(),
+ style = MaterialTheme.typography.subtitle1,
+ color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
+ )
+
+ if (itemInfo.series.isNotEmpty())
+ Text(
+ stringResource(
+ id = R.string.galleryblock_series,
+ itemInfo.series.joinToString { it.wordCapitalize() }
+ ),
+ style = MaterialTheme.typography.body2,
+ color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
+ )
+
+ Text(
+ stringResource(id = R.string.galleryblock_type, itemInfo.type),
+ style = MaterialTheme.typography.body2,
+ color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
+ )
+
+ key(itemInfo.tags) {
+ TagGroup(tags = itemInfo.tags)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private fun transform(manga: Manga) = HiyobiItemInfo(
+ manga.mangaId.toString(),
+ manga.title,
+ "https://api.hiyobi.io/${manga.thumbnail}",
+ manga.artist,
+ manga.series,
+ manga.type,
+ manga.date,
+ manga.bookmark,
+ manga.tags,
+ manga.commentCount,
+ manga.pageCount
+ )
+
+ fun parseQuery(query: String): String {
+ val queryBuilder = StringBuilder("[")
+
+ if (query.isNotBlank())
+ query.split(' ').filter { it.isNotBlank() }.forEach {
+ val tags = it.replace('_', ' ').split(':', limit = 2)
+
+ if (queryBuilder.length != 1) queryBuilder.append(',')
+
+ queryBuilder.append(
+ when {
+ tags.size == 1 -> "{tag:\"${tags[0]}\"}"
+ tags[0] == "male" -> "{male:1,tag:\"${tags[1]}\"}"
+ tags[0] == "female" -> "{female:1,tag:\"${tags[1]}\"}"
+ else -> "{tag:\"${tags[1]}\"}"
+ }
+ )
+ }
+
+ return queryBuilder.append(']').toString()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt
deleted file mode 100644
index 96d8b3b3..00000000
--- a/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Pupil, Hitomi.la viewer for Android
- * Copyright (C) 2020 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.ui
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import android.os.PersistableBundle
-import android.view.WindowManager
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.annotation.CallSuper
-import androidx.appcompat.app.AppCompatActivity
-import xyz.quaver.pupil.R
-import xyz.quaver.pupil.util.LockManager
-import xyz.quaver.pupil.util.Preferences
-import xyz.quaver.pupil.util.normalizeID
-
-open class BaseActivity : AppCompatActivity() {
-
- private var locked: Boolean = true
-
- private val lockLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- if (it.resultCode == Activity.RESULT_OK)
- locked = false
- else
- finish()
- }
-
- @CallSuper
- override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
- super.onCreate(savedInstanceState, persistentState)
-
- locked = !LockManager(this).locks.isNullOrEmpty()
- }
-
- @CallSuper
- override fun onResume() {
- super.onResume()
-
- if (Preferences["security_mode"])
- window.setFlags(
- WindowManager.LayoutParams.FLAG_SECURE,
- WindowManager.LayoutParams.FLAG_SECURE)
- else
- window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
-
- if (locked)
- lockLauncher.launch(Intent(this, LockActivity::class.java))
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt
deleted file mode 100644
index 33e678f6..00000000
--- a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Pupil, Hitomi.la viewer for Android
- * Copyright (C) 2019 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.ui
-
-import android.app.Activity
-import android.app.AlertDialog
-import android.os.Bundle
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import androidx.appcompat.app.AppCompatActivity
-import androidx.biometric.BiometricManager
-import androidx.biometric.BiometricPrompt
-import androidx.core.content.ContextCompat
-import com.andrognito.patternlockview.PatternLockView
-import com.google.android.material.snackbar.Snackbar
-import xyz.quaver.pupil.R
-import xyz.quaver.pupil.databinding.LockActivityBinding
-import xyz.quaver.pupil.ui.fragment.PINLockFragment
-import xyz.quaver.pupil.ui.fragment.PatternLockFragment
-import xyz.quaver.pupil.util.Lock
-import xyz.quaver.pupil.util.LockManager
-import xyz.quaver.pupil.util.Preferences
-
-private var lastUnlocked = 0L
-class LockActivity : AppCompatActivity() {
-
- private lateinit var lockManager: LockManager
- private var mode: String? = null
-
- private lateinit var binding: LockActivityBinding
-
- private val patternLockFragment = PatternLockFragment().apply {
- var lastPass = ""
- onPatternDrawn = {
- when(mode) {
- null -> {
- val result = lockManager.check(it)
-
- if (result == true) {
- lastUnlocked = System.currentTimeMillis()
- setResult(Activity.RESULT_OK)
- finish()
- } else
- binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
- }
- "add_lock" -> {
- if (lastPass.isEmpty()) {
- lastPass = it
-
- Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show()
- } else {
- if (lastPass == it) {
- LockManager(context!!).add(Lock.generate(Lock.Type.PATTERN, it))
- finish()
- } else {
- binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
- lastPass = ""
-
- Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
- }
- }
- }
- }
- }
- }
-
- private val pinLockFragment = PINLockFragment().apply {
- var lastPass = ""
- onPINEntered = {
- when(mode) {
- null -> {
- val result = lockManager.check(it)
-
- if (result == true) {
- lastUnlocked = System.currentTimeMillis()
- setResult(Activity.RESULT_OK)
- finish()
- } else {
- binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
- setAnimationListener(object: Animation.AnimationListener {
- override fun onAnimationEnd(animation: Animation?) {
- binding.pinLockView.resetPinLockView()
- binding.pinLockView.isEnabled = true
- }
-
- override fun onAnimationStart(animation: Animation?) {
- binding.pinLockView.isEnabled = false
- }
-
- override fun onAnimationRepeat(animation: Animation?) {
- // Do Nothing
- }
- })
- })
- }
- }
- "add_lock" -> {
- if (lastPass.isEmpty()) {
- lastPass = it
-
- binding.pinLockView.resetPinLockView()
- Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show()
- } else {
- if (lastPass == it) {
- LockManager(context!!).add(Lock.generate(Lock.Type.PIN, it))
- finish()
- } else {
- binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
- setAnimationListener(object: Animation.AnimationListener {
- override fun onAnimationEnd(animation: Animation?) {
- binding.pinLockView.resetPinLockView()
- binding.pinLockView.isEnabled = true
- }
-
- override fun onAnimationStart(animation: Animation?) {
- binding.pinLockView.isEnabled = false
- }
-
- override fun onAnimationRepeat(animation: Animation?) {
- // Do Nothing
- }
- })
- })
- lastPass = ""
-
- Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
- }
- }
- }
- }
- }
- }
-
- private fun showBiometricPrompt() {
- val promptInfo = BiometricPrompt.PromptInfo.Builder()
- .setTitle(getText(R.string.settings_lock_fingerprint_prompt))
- .setSubtitle(getText(R.string.settings_lock_fingerprint_prompt_subtitle))
- .setNegativeButtonText(getText(android.R.string.cancel))
- .setConfirmationRequired(false)
- .build()
-
- val biometricPrompt = BiometricPrompt(this, ContextCompat.getMainExecutor(this),
- object : BiometricPrompt.AuthenticationCallback() {
- override fun onAuthenticationSucceeded(
- result: BiometricPrompt.AuthenticationResult) {
- super.onAuthenticationSucceeded(result)
- lastUnlocked = System.currentTimeMillis()
- setResult(RESULT_OK)
- finish()
- return
- }
- })
-
- // Displays the "log in" prompt.
- biometricPrompt.authenticate(promptInfo)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = LockActivityBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
- lockManager = try {
- LockManager(this)
- } catch (e: Exception) {
- AlertDialog.Builder(this).apply {
- setTitle(R.string.warning)
- setMessage(R.string.lock_corrupted)
- setPositiveButton(android.R.string.ok) { _, _ ->
- finish()
- }
- }.show()
- return
- }
-
- mode = intent.getStringExtra("mode")
- val force = intent.getBooleanExtra("force", false)
-
- when(mode) {
- null -> {
- if (lockManager.isEmpty()) {
- setResult(RESULT_OK)
- finish()
- return
- }
-
- if (System.currentTimeMillis() - lastUnlocked < 5*60*1000 && !force) {
- lastUnlocked = System.currentTimeMillis()
- setResult(RESULT_OK)
- finish()
- return
- }
-
- if (
- Preferences["lock_fingerprint"]
- && BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
- ) {
- binding.fingerprintBtn.apply {
- isEnabled = true
- setOnClickListener {
- showBiometricPrompt()
- }
- }
- showBiometricPrompt()
- }
-
- binding.patternBtn.apply {
- isEnabled = lockManager.contains(Lock.Type.PATTERN)
- setOnClickListener {
- supportFragmentManager.beginTransaction().replace(
- R.id.lock_content, patternLockFragment
- ).commit()
- }
- }
- binding.pinBtn.apply {
- isEnabled = lockManager.contains(Lock.Type.PIN)
- setOnClickListener {
- supportFragmentManager.beginTransaction().replace(
- R.id.lock_content, pinLockFragment
- ).commit()
- }
- }
- binding.passwordBtn.isEnabled = false
-
- when (lockManager.locks!!.first().type) {
- Lock.Type.PIN -> {
-
- supportFragmentManager.beginTransaction().add(
- R.id.lock_content, pinLockFragment
- ).commit()
- }
- Lock.Type.PATTERN -> {
- supportFragmentManager.beginTransaction().add(
- R.id.lock_content, patternLockFragment
- ).commit()
- }
- else -> return
- }
- }
- "add_lock" -> {
- binding.patternBtn.isEnabled = false
- binding.pinBtn.isEnabled = false
- binding.fingerprintBtn.isEnabled = false
- binding.passwordBtn.isEnabled = false
-
- when(intent.getStringExtra("type")!!) {
- "pattern" -> {
- binding.patternBtn.isEnabled = true
- supportFragmentManager.beginTransaction().add(
- R.id.lock_content, patternLockFragment
- ).commit()
- }
- "pin" -> {
- binding.pinBtn.isEnabled = true
- supportFragmentManager.beginTransaction().add(
- R.id.lock_content, pinLockFragment
- ).commit()
- }
- }
- }
- }
- }
-
-}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
index 86201752..b2b09f77 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -53,7 +53,6 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.*
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
-import org.kodein.di.compose.withDI
import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.pupil.*
@@ -65,7 +64,6 @@ import xyz.quaver.pupil.ui.composable.FloatingSearchBar
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
import xyz.quaver.pupil.ui.composable.SubFabItem
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
-import xyz.quaver.pupil.ui.dialog.SourceSelectDialogItem
import xyz.quaver.pupil.ui.theme.PupilTheme
import xyz.quaver.pupil.ui.view.ProgressCardView
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
@@ -117,8 +115,12 @@ class MainActivity : ComponentActivity(), DIAware {
}
if (openSourceSelectDialog)
- SourceSelectDialog {
+ SourceSelectDialog(
+ currentSource = model.source.name,
+ onDismissRequest = { openSourceSelectDialog = false }
+ ) { source ->
openSourceSelectDialog = false
+ model.setSourceAndReset(source.name)
}
Scaffold(
@@ -185,7 +187,8 @@ class MainActivity : ComponentActivity(), DIAware {
ReaderActivity::class.java
).apply {
putExtra("source", model.source.name)
- putExtra("id", itemInfo.itemID)
+ putExtra("id", event.itemID)
+ putExtra("payload", event.payload)
})
}
else -> TODO("")
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
index 89c5ea65..56ce631d 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt
@@ -81,8 +81,6 @@ class ReaderActivity : ComponentActivity(), DIAware {
setContent {
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
val isFullscreen by model.isFullscreen.observeAsState(false)
- val title by model.title.observeAsState(stringResource(R.string.reader_loading))
- val sourceIcon by model.sourceIcon.observeAsState()
val imageSources = remember { mutableStateListOf() }
val imageHeights = remember { mutableStateListOf() }
val states = remember { mutableStateListOf() }
@@ -131,12 +129,12 @@ class ReaderActivity : ComponentActivity(), DIAware {
TopAppBar(
title = {
Text(
- title,
+ model.title ?: stringResource(R.string.reader_loading),
color = MaterialTheme.colors.onSecondary
)
},
actions = {
- sourceIcon?.let { sourceIcon ->
+ model.sourceIcon?.let { sourceIcon ->
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = sourceIcon),
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
index 2bf54e78..90b0f0e5 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
@@ -20,11 +20,12 @@ package xyz.quaver.pupil.ui
import android.os.Bundle
import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
import xyz.quaver.pupil.R
import xyz.quaver.pupil.ui.fragment.SettingsFragment
import xyz.quaver.pupil.ui.fragment.SourceSettingsFragment
-class SettingsActivity : BaseActivity() {
+class SettingsActivity : AppCompatActivity() {
companion object {
const val SETTINGS_EXTRA = "xyz.quaver.pupil.ui.SettingsActivity.SETTINGS_EXTRA"
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
index 0fc1e23f..bc389958 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt
@@ -27,7 +27,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding
-import xyz.quaver.pupil.sources.hitomi.Hitomi
+import xyz.quaver.pupil.sources.Hitomi
import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.util.Preferences
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
index c51296d5..4e68bf32 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/SourceSelectDialog.kt
@@ -19,6 +19,7 @@
package xyz.quaver.pupil.ui.dialog
import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
@@ -36,7 +37,7 @@ import xyz.quaver.pupil.sources.Source
import xyz.quaver.pupil.sources.SourceEntries
@Composable
-fun SourceSelectDialogItem(source: Source) {
+fun SourceSelectDialogItem(source: Source, isSelected: Boolean, onSelected: (Source) -> Unit = { }) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -59,16 +60,20 @@ fun SourceSelectDialogItem(source: Source) {
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.5f)
)
- Button(onClick = { /*TODO*/ }) {
+ Button(
+ enabled = !isSelected,
+ onClick = {
+ onSelected(source)
+ }
+ ) {
Text("GO")
}
}
}
-@Preview
@Composable
-fun SourceSelectDialog(onDismissRequest: () -> Unit = { }) {
+fun SourceSelectDialog(currentSource: String, onDismissRequest: () -> Unit = { }, onSelected: (Source) -> Unit = { }) {
val sourceEntries: SourceEntries by rememberInstance()
Dialog(onDismissRequest = onDismissRequest) {
@@ -77,7 +82,7 @@ fun SourceSelectDialog(onDismissRequest: () -> Unit = { }) {
shape = RoundedCornerShape(12.dp)
) {
Column() {
- sourceEntries.forEach { SourceSelectDialogItem(it.second) }
+ sourceEntries.forEach { SourceSelectDialogItem(it.second, it.first == currentSource, onSelected) }
}
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt
deleted file mode 100644
index 06a319cb..00000000
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Pupil, Hitomi.la viewer for Android
- * Copyright (C) 2020 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.ui.fragment
-
-import android.content.Intent
-import android.os.Bundle
-import android.widget.Toast
-import androidx.appcompat.app.AlertDialog
-import androidx.preference.Preference
-import androidx.preference.PreferenceFragmentCompat
-import androidx.preference.SwitchPreferenceCompat
-import xyz.quaver.pupil.R
-import xyz.quaver.pupil.ui.LockActivity
-import xyz.quaver.pupil.util.Lock
-import xyz.quaver.pupil.util.LockManager
-import xyz.quaver.pupil.util.Preferences
-
-class LockSettingsFragment : PreferenceFragmentCompat() {
-
- override fun onResume() {
- super.onResume()
-
- val lockManager = LockManager(requireContext())
-
- findPreference("lock_pattern")?.summary =
- if (lockManager.contains(Lock.Type.PATTERN))
- getString(R.string.settings_lock_enabled)
- else
- ""
-
- findPreference("lock_pin")?.summary =
- if (lockManager.contains(Lock.Type.PIN))
- getString(R.string.settings_lock_enabled)
- else
- ""
-
- if (lockManager.isEmpty()) {
- (findPreference("lock_fingerprint") as SwitchPreferenceCompat).isChecked = false
-
- Preferences["lock_fingerprint"] = false
- }
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- setPreferencesFromResource(R.xml.lock_preferences, rootKey)
-
- with (findPreference("lock_pattern")) {
- this!!
-
- if (LockManager(requireContext()).contains(Lock.Type.PATTERN))
- summary = getString(R.string.settings_lock_enabled)
-
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- val lockManager = LockManager(requireContext())
-
- if (lockManager.contains(Lock.Type.PATTERN)) {
- AlertDialog.Builder(requireContext()).apply {
- setTitle(R.string.warning)
- setMessage(R.string.settings_lock_remove_message)
-
- setPositiveButton(android.R.string.ok) { _, _ ->
- lockManager.remove(Lock.Type.PATTERN)
- onResume()
- }
- setNegativeButton(android.R.string.cancel) { _, _ -> }
- }.show()
- } else {
- val intent = Intent(requireContext(), LockActivity::class.java).apply {
- putExtra("mode", "add_lock")
- putExtra("type", "pattern")
- }
-
- startActivity(intent)
- }
-
- true
- }
- }
-
- with (findPreference("lock_pin")) {
- this!!
-
- if (LockManager(requireContext()).contains(Lock.Type.PIN))
- summary = getString(R.string.settings_lock_enabled)
-
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- val lockManager = LockManager(requireContext())
-
- if (lockManager.contains(Lock.Type.PIN)) {
- AlertDialog.Builder(requireContext()).apply {
- setTitle(R.string.warning)
- setMessage(R.string.settings_lock_remove_message)
-
- setPositiveButton(android.R.string.ok) { _, _ ->
- lockManager.remove(Lock.Type.PIN)
- onResume()
- }
- setNegativeButton(android.R.string.cancel) { _, _ -> }
- }.show()
- } else {
- val intent = Intent(requireContext(), LockActivity::class.java).apply {
- putExtra("mode", "add_lock")
- putExtra("type", "pin")
- }
-
- startActivity(intent)
- }
-
- true
- }
- }
-
- with (findPreference("lock_fingerprint")) {
- this!!
-
- setOnPreferenceChangeListener { _, newValue ->
- this as SwitchPreferenceCompat
-
- if (newValue == true && LockManager(requireContext()).isEmpty()) {
- isChecked = false
-
- Toast.makeText(requireContext(), R.string.settings_lock_fingerprint_without_lock, Toast.LENGTH_SHORT).show()
- } else
- isChecked = newValue as Boolean
-
- false
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
index c3f0a408..78c9ed77 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
@@ -32,7 +32,6 @@ import org.kodein.di.instance
import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.ui.SettingsActivity
import xyz.quaver.pupil.ui.dialog.*
import xyz.quaver.pupil.util.*
@@ -49,16 +48,6 @@ class SettingsFragment :
private val downloadManager: DownloadManager by instance()
- private val lockLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- if (it.resultCode == Activity.RESULT_OK) {
- parentFragmentManager
- .beginTransaction()
- .replace(R.id.settings, LockSettingsFragment())
- .addToBackStack("Lock")
- .commitAllowingStateLoss()
- }
- }
-
override fun onResume() {
super.onResume()
@@ -88,12 +77,6 @@ class SettingsFragment :
"download_folder" -> {
DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
}
- "app_lock" -> {
- val intent = Intent(requireContext(), LockActivity::class.java).apply {
- putExtra("force", true)
- }
- lockLauncher.launch(intent)
- }
"proxy" -> {
ProxyDialogFragment().show(parentFragmentManager, "Proxy Dialog")
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
index 62df1506..2125545d 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/view/FloatingSearchView.kt
@@ -33,7 +33,7 @@ import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.floatingsearchview.util.view.SearchInputView
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.sources.hitomi.Hitomi
+import xyz.quaver.pupil.sources.Hitomi
import xyz.quaver.pupil.types.FavoriteHistorySwitch
import xyz.quaver.pupil.types.HistorySuggestion
import xyz.quaver.pupil.types.LoadingSuggestion
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCardView.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCardView.kt
index a5ff55b9..4c3527a6 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCardView.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCardView.kt
@@ -1,27 +1,12 @@
package xyz.quaver.pupil.ui.view
-import android.content.Context
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.cardview.widget.CardView
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import androidx.core.content.ContextCompat
-import androidx.core.graphics.drawable.DrawableCompat
-import xyz.quaver.pupil.R
-import xyz.quaver.pupil.databinding.ProgressCardViewBinding
-import xyz.quaver.pupil.sources.ItemInfo
-import xyz.quaver.pupil.sources.Source
@OptIn(ExperimentalFoundationApi::class)
@Composable
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt
deleted file mode 100644
index 9e179272..00000000
--- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.ui.viewmodel
-
-import android.app.Application
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.*
-import org.kodein.di.DIAware
-import org.kodein.di.android.x.closestDI
-import xyz.quaver.pupil.sources.ItemInfo
-import xyz.quaver.pupil.sources.Source
-import xyz.quaver.pupil.util.source
-
-class GalleryDialogViewModel(app: Application) : AndroidViewModel(app), DIAware {
-
- override val di by closestDI()
-
- private val _info = MutableLiveData()
- val info: LiveData = _info
-
- private val _related = MutableLiveData>()
- val related: LiveData> = _related
-
- fun load(source: String, itemID: String) {/*
- val source: Source by source(source)
-
- viewModelScope.launch {
- _info.value = withContext(Dispatchers.IO) {
- source.info(itemID)
- }.also {
- _related.value = it.extra[ItemInfo.ExtraType.RELATED_ITEM]?.await()?.split(", ")?.map { related ->
- async(Dispatchers.IO) {
- source.info(related)
- }
- }?.awaitAll()
- }
- }*/
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt
index a5546030..c9781298 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt
@@ -31,7 +31,6 @@ import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.pupil.sources.*
-import xyz.quaver.pupil.sources.hitomi.Hitomi
import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.source
import kotlin.math.ceil
@@ -59,7 +58,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
direct.source(it)
}
private var sourceFactory: (String) -> Source = defaultSourceFactory
- var source by mutableStateOf(sourceFactory("hitomi.la"))
+ var source by mutableStateOf(sourceFactory("hiyobi.io"))
private set
var sortModeIndex by mutableStateOf(0)
@@ -125,13 +124,12 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
sortModeIndex
)
- logger.info { count.toString() }
-
totalItems.postValue(count)
for (result in channel) {
yield()
searchResults.add(result)
+ logger.info { result.toString() }
}
loading = false
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
index e80c1135..a3880e37 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/ReaderViewModel.kt
@@ -22,10 +22,7 @@ package xyz.quaver.pupil.ui.viewmodel
import android.app.Application
import android.content.Intent
import android.net.Uri
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.*
import androidx.lifecycle.*
import io.ktor.client.request.*
import io.ktor.http.*
@@ -36,11 +33,13 @@ import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.direct
import org.kodein.di.instance
+import org.kodein.di.instanceOrNull
import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.db.Bookmark
import xyz.quaver.pupil.db.History
+import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.sources.Source
import xyz.quaver.pupil.util.NetworkCache
import xyz.quaver.pupil.util.source
@@ -61,14 +60,12 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
private val historyDao = database.historyDao()
private val bookmarkDao = database.bookmarkDao()
- private val _source = MutableLiveData()
- val source = _source as LiveData
-
- private val _itemID = MutableLiveData()
- val itemID = _itemID as LiveData
-
- private val _title = MutableLiveData()
- val title = _title as LiveData
+ var source by mutableStateOf(null)
+ private set
+ var itemID by mutableStateOf(null)
+ private set
+ var title by mutableStateOf(null)
+ private set
private val totalProgressMutex = Mutex()
var totalProgress by mutableStateOf(0)
@@ -80,19 +77,8 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
val imageList = mutableStateListOf()
val progressList = mutableStateListOf()
- val isBookmarked = Transformations.switchMap(MediatorLiveData>().apply {
- addSource(source) { source -> itemID.value?.let { itemID -> source to itemID } }
- addSource(itemID) { itemID -> source.value?.let { source -> source to itemID } }
- }) { (source, itemID) ->
- bookmarkDao.contains(source.name, itemID)
- }
-
- val sourceInstance = Transformations.map(source) {
- direct.source(it)
- }
-
- val sourceIcon = Transformations.map(sourceInstance) {
- it.iconResID
+ val sourceIcon by derivedStateOf {
+ source?.iconResID
}
/**
@@ -105,8 +91,8 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
val uri = intent.data
val lastPathSegment = uri?.lastPathSegment
if (uri != null && lastPathSegment != null) {
- _source.value = uri.host ?: error("Source cannot be null")
- _itemID.value = when (uri.host) {
+ source = uri.host?.let { direct.source(it) } ?: error("Invalid host")
+ itemID = when (uri.host) {
"hitomi.la" ->
Regex("([0-9]+).html").find(lastPathSegment)?.groupValues?.get(1) ?: error("Invalid itemID")
"hiyobi.me" -> lastPathSegment
@@ -115,15 +101,16 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
}
} else {
- _source.value = intent.getStringExtra("source") ?: error("Invalid source")
- _itemID.value = intent.getStringExtra("id") ?: error("Invalid itemID")
+ source = intent.getStringExtra("source")?.let { direct.source(it) } ?: error("Invalid source")
+ itemID = intent.getStringExtra("id") ?: error("Invalid itemID")
+ title = intent.getParcelableExtra("payload")?.title
}
}
@OptIn(ExperimentalCoroutinesApi::class)
fun load() {
- val source: Source by source(source.value ?: return)
- val itemID = itemID.value ?: return
+ val source = source ?: return
+ val itemID = itemID ?: return
viewModelScope.launch {
launch(Dispatchers.IO) {
@@ -132,9 +119,10 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
viewModelScope.launch {
- _title.value = withContext(Dispatchers.IO) {
- source.info(itemID)
- }.title
+ if (title == null)
+ title = withContext(Dispatchers.IO) {
+ source.info(itemID)
+ }.title
}
viewModelScope.launch {
@@ -152,6 +140,9 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
images.forEachIndexed { index, image ->
+ logger.info {
+ progressList.toList().toString()
+ }
when (val scheme = image.takeWhile { it != ':' }) {
"http", "https" -> {
val (channel, file) = cache.load {
@@ -203,7 +194,7 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
fun toggleBookmark() {
- val bookmark = source.value?.let { source -> itemID.value?.let { itemID -> Bookmark(source, itemID) } } ?: return
+ val bookmark = source?.let { source -> itemID?.let { itemID -> Bookmark(source.name, itemID) } } ?: return
CoroutineScope(Dispatchers.IO).launch {
if (bookmarkDao.contains(bookmark).value ?: return@launch)
diff --git a/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt b/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt
index 333042ed..6ba525d2 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/DownloadManager.kt
@@ -31,7 +31,6 @@ import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import xyz.quaver.io.FileX
import xyz.quaver.io.util.*
-import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.sources.Source
class DownloadManager constructor(context: Context) : ContextWrapper(context), DIAware {
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 72c8dd07..0cb01414 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt
@@ -19,24 +19,16 @@
package xyz.quaver.pupil.util
import android.annotation.SuppressLint
-import android.content.Context
-import android.content.res.Resources
import android.graphics.BitmapFactory
-import android.graphics.BitmapRegionDecoder
import android.view.MenuItem
import android.view.View
import androidx.compose.runtime.*
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.toAndroidRect
-import androidx.compose.ui.platform.LocalFocusManager
import androidx.lifecycle.MutableLiveData
-import com.google.accompanist.insets.LocalWindowInsets
import kotlinx.serialization.json.*
import org.kodein.di.DIAware
import org.kodein.di.DirectDIAware
@@ -49,7 +41,6 @@ import xyz.quaver.io.util.inputStream
import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.sources.SourceEntries
-import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.*
diff --git a/app/src/main/res/layout/search_result_item.xml b/app/src/main/res/layout/search_result_item.xml
deleted file mode 100644
index 79efbb85..00000000
--- a/app/src/main/res/layout/search_result_item.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt
index 652d776a..9e0c0674 100644
--- a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt
+++ b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt
@@ -33,6 +33,7 @@ import kotlinx.serialization.json.Json
import org.junit.Test
import xyz.quaver.hitomi.getGalleryInfo
import xyz.quaver.hitomi.imageUrlFromImage
+import xyz.quaver.pupil.sources.Hiyobi_io
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
import kotlin.reflect.KType
@@ -50,4 +51,9 @@ class ExampleUnitTest {
}
}
+ @Test
+ fun test2() {
+ print(Hiyobi_io.parseQuery("female:loli female:big_breast tag:group"))
+ }
+
}