diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 0aab68d2..c6c1e863 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -1,17 +1,17 @@
-
+
-
+
-
-
+
+
-
-
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f01303dd..6a57076b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -28,6 +28,8 @@
+
+
@@ -38,6 +40,7 @@
+
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 0e683216..3656de96 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt
@@ -60,7 +60,7 @@ abstract class Source {
abstract suspend fun info(itemID: String): ItemInfo
@Composable
- open fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)? = null) { }
+ open fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit = { }) { }
open fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = { }
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 b141eb54..fa4809b7 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/History.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/History.kt
@@ -73,7 +73,7 @@ class History(override val di: DI) : Source(), DIAware {
@Composable
- override fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)?) {
+ override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
}
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 1287a144..0f48060d 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt
@@ -120,7 +120,7 @@ data class HitomiItemInfo(
class Hitomi(app: Application) : Source(), DIAware {
- override val di: DI by closestDI(app)
+ override val di by closestDI(app)
private val logger = newLogger(LoggerFactory.default)
@@ -223,10 +223,10 @@ class Hitomi(app: Application) : Source(), DIAware {
}
@Composable
- override fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)?) {
+ override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
itemInfo as HitomiItemInfo
- FullSearchResult(itemInfo = itemInfo)
+ FullSearchResult(itemInfo = itemInfo, onEvent = onEvent)
}
override fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = {
@@ -434,7 +434,7 @@ class Hitomi(app: Application) : Source(), DIAware {
@OptIn(ExperimentalCoilApi::class)
@Composable
- fun FullSearchResult(itemInfo: HitomiItemInfo) {
+ fun FullSearchResult(itemInfo: HitomiItemInfo, onEvent: (SearchResultEvent) -> Unit) {
var group by remember { mutableStateOf(emptyList()) }
var pageCount by remember { mutableStateOf("-") }
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt b/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt
deleted file mode 100644
index b32d860c..00000000
--- a/app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt
+++ /dev/null
@@ -1,83 +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.sources
-
-import android.app.Application
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Element
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.instance
-import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
-import xyz.quaver.pupil.R
-/*
-class ImHentai(override val di: DI) : Source(), DIAware {
-
- private val app: Application by instance()
- private val client: HttpClient by instance()
-
- override val name: String
- get() = ImHentai.name
- override val iconResID: Int
- get() = R.drawable.ic_imhentai
- override val preferenceID: Int
- get() = R.xml.imhentai_preferences
- override val availableSortMode = app.resources.getStringArray(R.array.imhentai_sort_mode).toList()
-
- override suspend fun search(query: String, range: IntRange, sortMode: Int): Pair, Int> = withContext(Dispatchers.IO) {
- val channel = Channel()
-
- val doc = Jsoup.connect("https://imhentai.xxx/search/?key=$query").get()
-
- val count = countRegex.find(doc.getElementsByClass("heading2").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0
-
- launch {
- doc.getElementsByClass("thumb")
- }
-
- return@withContext Pair(channel, count)
- }
-
- override suspend fun suggestion(query: String): List {
- TODO("Not yet implemented")
- }
-
- override suspend fun images(itemID: String): List {
- TODO("Not yet implemented")
- }
-
- override suspend fun info(itemID: String): ItemInfo {
- TODO("Not yet implemented")
- }
-
- companion object {
- private const val name = "imhentai"
- private val countRegex = Regex("""\(\d+\) results found.""")
- private val idRegex = Regex("""/gallery/(\d+)/""")
-
- private fun transform(item: Element) {
- val caption = item.select(".caption a")
- }
- }
-
-}*/
\ No newline at end of file
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 8e5dd421..86201752 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -24,59 +24,48 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
-import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.MutatePriority
-import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.*
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
+import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
-import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.asImageBitmap
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.PointerEventPass
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastAny
-import androidx.core.graphics.drawable.toBitmap
-import androidx.core.view.WindowCompat
import com.google.accompanist.drawablepainter.rememberDrawablePainter
-import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.distinctUntilChanged
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.*
import xyz.quaver.pupil.R
-import xyz.quaver.pupil.sources.Source
+import xyz.quaver.pupil.sources.SearchResultEvent
import xyz.quaver.pupil.types.*
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
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
@@ -101,7 +90,7 @@ class MainActivity : ComponentActivity(), DIAware {
setContent {
PupilTheme {
- val source: Source? by model.source.observeAsState(null)
+ val focusManager = LocalFocusManager.current
var isFabExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
var isFabVisible by remember { mutableStateOf(true) }
@@ -121,10 +110,17 @@ class MainActivity : ComponentActivity(), DIAware {
}
}
+ var openSourceSelectDialog by remember { mutableStateOf(false) }
+
LaunchedEffect(navigationIconProgress) {
navigationIcon.progress = navigationIconProgress
}
+ if (openSourceSelectDialog)
+ SourceSelectDialog {
+ openSourceSelectDialog = false
+ }
+
Scaffold(
floatingActionButton = {
MultipleFloatingActionButton(
@@ -178,19 +174,23 @@ class MainActivity : ComponentActivity(), DIAware {
) {
items(model.searchResults, key = { it.itemID }) { itemInfo ->
ProgressCardView(
- progress = 0.5f,
- onClick = {
- startActivity(
- Intent(
- this@MainActivity,
- ReaderActivity::class.java
- ).apply {
- putExtra("source", model.source.value!!.name)
- putExtra("id", itemInfo.itemID)
- })
- }
+ progress = 0.5f
) {
- source?.SearchResult(itemInfo = itemInfo)
+ model.source.SearchResult(itemInfo = itemInfo) { event ->
+ when (event.type) {
+ SearchResultEvent.Type.OPEN_READER -> {
+ startActivity(
+ Intent(
+ this@MainActivity,
+ ReaderActivity::class.java
+ ).apply {
+ putExtra("source", model.source.name)
+ putExtra("id", itemInfo.itemID)
+ })
+ }
+ else -> TODO("")
+ }
+ }
}
}
}
@@ -206,10 +206,24 @@ class MainActivity : ComponentActivity(), DIAware {
Icon(
painter = rememberDrawablePainter(navigationIcon),
contentDescription = null,
- modifier = Modifier.size(24.dp)
+ modifier = Modifier
+ .size(24.dp)
+ .clickable(
+ interactionSource = remember { MutableInteractionSource() },
+ indication = rememberRipple(bounded = false)
+ ) {
+ focusManager.clearFocus()
+ }
)
},
actions = {
+ Image(
+ painterResource(model.source.iconResID),
+ contentDescription = null,
+ modifier = Modifier.size(24.dp).clickable {
+ openSourceSelectDialog = true
+ }
+ )
Icon(
Icons.Default.Sort,
contentDescription = null,
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 1cdf2747..c51296d5 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
@@ -18,37 +18,67 @@
package xyz.quaver.pupil.ui.dialog
-import android.app.Dialog
-import android.os.Bundle
-import androidx.fragment.app.DialogFragment
-import org.kodein.di.DIAware
-import org.kodein.di.android.x.closestDI
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import org.kodein.di.compose.rememberInstance
+import xyz.quaver.pupil.sources.Source
+import xyz.quaver.pupil.sources.SourceEntries
-class SourceSelectDialog : DialogFragment(), DIAware {
+@Composable
+fun SourceSelectDialogItem(source: Source) {
+ Row(
+ modifier = Modifier.padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Image(
+ painter = painterResource(source.iconResID),
+ contentDescription = null,
+ modifier = Modifier.size(24.dp)
+ )
- override val di by closestDI()
+ Text(
+ source.name,
+ modifier = Modifier.weight(1f)
+ )
- var onSourceSelectedListener: ((String) -> Unit)? = null
- var onSourceSettingsSelectedListener: ((String) -> Unit)? = null
+ Icon(
+ Icons.Default.Settings,
+ contentDescription = null,
+ tint = MaterialTheme.colors.onSurface.copy(alpha = 0.5f)
+ )
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {/*
- return Dialog(requireContext()).apply {
- window?.requestFeature(Window.FEATURE_NO_TITLE)
- window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
-
- val sourcesWithPreferenceID = direct.instance().map { it.first }
- val preferences = direct.instance().filter {
- it.first in sourcesWithPreferenceID
- }.toSet()
-
- setContentView(RecyclerView(context).apply {
- layoutManager = LinearLayoutManager(context)
- adapter = SourceAdapter(preferences).apply {
- onSourceSelectedListener = this@SourceSelectDialog.onSourceSelectedListener
- onSourceSettingsSelectedListener = this@SourceSelectDialog.onSourceSettingsSelectedListener
- }
- })
+ Button(onClick = { /*TODO*/ }) {
+ Text("GO")
}
- */return super.onCreateDialog(savedInstanceState)}
+ }
+}
+
+@Preview
+@Composable
+fun SourceSelectDialog(onDismissRequest: () -> Unit = { }) {
+ val sourceEntries: SourceEntries by rememberInstance()
+
+ Dialog(onDismissRequest = onDismissRequest) {
+ Card(
+ elevation = 8.dp,
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Column() {
+ sourceEntries.forEach { SourceSelectDialogItem(it.second) }
+ }
+ }
+ }
}
\ No newline at end of file
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 881eed3f..a5ff55b9 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
@@ -25,11 +25,9 @@ import xyz.quaver.pupil.sources.Source
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun ProgressCardView(progress: Float? = null, onLongClick: (() -> Unit)? = null, onClick: () -> Unit, content: @Composable () -> Unit) {
+fun ProgressCardView(progress: Float? = null, content: @Composable () -> Unit) {
Card(
- modifier = Modifier
- .padding(8.dp)
- .combinedClickable(onClick = onClick, onLongClick = onLongClick),
+ modifier = Modifier.padding(8.dp),
shape = RoundedCornerShape(4.dp),
elevation = 4.dp
) {
@@ -38,64 +36,4 @@ fun ProgressCardView(progress: Float? = null, onLongClick: (() -> Unit)? = null,
content()
}
}
-}
-
-class ProgressCardView @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, defStyle: Int = R.attr.cardViewStyle) : CardView(context, attr, defStyle) {
-
- enum class Type {
- LOADING,
- CACHE,
- DOWNLOAD
- }
-
- var type: Type = Type.LOADING
- set(value) {
- field = value
-
- when (field) {
- Type.LOADING -> R.color.colorAccent
- Type.CACHE -> R.color.material_blue_700
- Type.DOWNLOAD -> R.color.material_green_a700
- }.let {
- val color = ContextCompat.getColor(context, it)
- DrawableCompat.setTint(binding.progressbar.progressDrawable, color)
- }
- }
-
- var progress: Int
- get() = binding.progressbar.progress
- set(value) {
- binding.progressbar.progress = value
- }
- var max: Int
- get() = binding.progressbar.max
- set(value) {
- binding.progressbar.max = value
-
- binding.progressbar.visibility =
- if (value == 0)
- GONE
- else
- VISIBLE
- }
-
- val binding = ProgressCardViewBinding.inflate(LayoutInflater.from(context), this)
-
- init {
- binding.content.setOnClickListener {
- performClick()
- }
-
- binding.content.setOnLongClickListener {
- performLongClick()
- }
- }
-
- override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
- if (childCount == 0)
- super.addView(child, index, params)
- else
- binding.content.addView(child, index, params)
- }
-
}
\ 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 886b93e1..a5546030 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
@@ -20,10 +20,7 @@ package xyz.quaver.pupil.ui.viewmodel
import android.annotation.SuppressLint
import android.app.Application
-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 kotlinx.coroutines.*
import org.kodein.di.DIAware
@@ -34,6 +31,7 @@ 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
@@ -61,21 +59,13 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
direct.source(it)
}
private var sourceFactory: (String) -> Source = defaultSourceFactory
- private val _source = MutableLiveData()
- val source: LiveData = _source
+ var source by mutableStateOf(sourceFactory("hitomi.la"))
+ private set
- val availableSortMode = Transformations.map(_source) {
- it.availableSortMode
- }
+ var sortModeIndex by mutableStateOf(0)
+ private set
- val sortModeIndex = MutableLiveData()
-
- val sourceIcon = Transformations.map(_source) {
- it.iconResID
- }
-
- private val _currentPage = MutableLiveData()
- val currentPage: LiveData = _currentPage
+ var currentPage by mutableStateOf(1)
private val totalItems = MutableLiveData()
@@ -88,14 +78,9 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
private val _suggestions = MutableLiveData>()
val suggestions: LiveData> = _suggestions
- init {
- setSourceAndReset("hitomi.la")
- }
-
fun setSourceAndReset(sourceName: String) {
- _source.value = sourceFactory(sourceName).also {
- sortModeIndex.value = 0
- }
+ source = sourceFactory(sourceName)
+ sortModeIndex = 0
query = ""
resetAndQuery()
@@ -103,7 +88,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
fun resetAndQuery() {
queryStack.add(query)
- setPage(1)
+ currentPage = 1
query()
}
@@ -119,16 +104,13 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
when {
mode == MainMode.DOWNLOADS -> "downloads"
//source.value is Downloads -> "hitomi.la"
- else -> source.value!!.name
+ else -> source.name
}
)
}
fun query() {
val perPage = Preferences["per_page", "25"].toInt()
- val source = _source.value ?: error("Source is null")
- val sortModeIndex = sortModeIndex.value ?: 0
- val currentPage = currentPage.value ?: 1
suggestionJob?.cancel()
queryJob?.cancel()
@@ -156,10 +138,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
}
- fun prevPage() { _currentPage.value = _currentPage.value!! - 1 }
- fun nextPage() { _currentPage.value = _currentPage.value!! + 1 }
- fun setPage(page: Int) { _currentPage.value = page }
-
fun random(callback: (ItemInfo) -> Unit) {
if (totalItems.value!! == 0)
return
@@ -168,12 +146,12 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
viewModelScope.launch {
withContext(Dispatchers.IO) {
- _source.value?.search(
+ source.search(
query + Preferences["default_query", ""],
random .. random,
- sortModeIndex.value!!
- )?.first?.receive()
- }?.let(callback)
+ sortModeIndex
+ ).first.receive()
+ }.let(callback)
}
}
@@ -186,7 +164,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
@SuppressLint("NullSafeMutableLiveData")
_suggestions.value = withContext(Dispatchers.IO) {
kotlin.runCatching {
- _source.value!!.suggestion(query)
+ source.suggestion(query)
}.getOrElse { emptyList() }
}
}