SourceSelectDialog
This commit is contained in:
12
.idea/deploymentTargetDropDown.xml
generated
12
.idea/deploymentTargetDropDown.xml
generated
@@ -1,17 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="deploymentTargetDropDown">
|
<component name="deploymentTargetDropDown">
|
||||||
<targetSelectedWithDropDown>
|
<runningDeviceTargetSelectedWithDropDown>
|
||||||
<Target>
|
<Target>
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
<type value="RUNNING_DEVICE_TARGET" />
|
||||||
<deviceKey>
|
<deviceKey>
|
||||||
<Key>
|
<Key>
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
<type value="SERIAL_NUMBER" />
|
||||||
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_30_x86.avd" />
|
<value value="ce021712e3b19b2b04" />
|
||||||
</Key>
|
</Key>
|
||||||
</deviceKey>
|
</deviceKey>
|
||||||
</Target>
|
</Target>
|
||||||
</targetSelectedWithDropDown>
|
</runningDeviceTargetSelectedWithDropDown>
|
||||||
<timeTargetWasSelectedWithDropDown value="2021-12-16T03:12:12.593009Z" />
|
<timeTargetWasSelectedWithDropDown value="2021-12-16T03:48:05.512086Z" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -28,6 +28,8 @@
|
|||||||
<entry key="../../../../layout/compose-model-1639478149655.xml" value="0.33" />
|
<entry key="../../../../layout/compose-model-1639478149655.xml" value="0.33" />
|
||||||
<entry key="../../../../layout/compose-model-1639535152524.xml" value="0.3055555555555556" />
|
<entry key="../../../../layout/compose-model-1639535152524.xml" value="0.3055555555555556" />
|
||||||
<entry key="../../../../layout/compose-model-1639538998660.xml" value="0.30277777777777776" />
|
<entry key="../../../../layout/compose-model-1639538998660.xml" value="0.30277777777777776" />
|
||||||
|
<entry key="../../../../layout/compose-model-1639625734547.xml" value="0.1" />
|
||||||
|
<entry key="../../../../layout/compose-model-1639629588722.xml" value="0.3472222222222222" />
|
||||||
<entry key="../../../../layout/custom_preview.xml" value="0.518974358974359" />
|
<entry key="../../../../layout/custom_preview.xml" value="0.518974358974359" />
|
||||||
<entry key="app/src/main/res/drawable/avd_star.xml" value="0.2722222222222222" />
|
<entry key="app/src/main/res/drawable/avd_star.xml" value="0.2722222222222222" />
|
||||||
<entry key="app/src/main/res/drawable/history.xml" value="0.3055555555555556" />
|
<entry key="app/src/main/res/drawable/history.xml" value="0.3055555555555556" />
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
<entry key="app/src/main/res/layout/reader_activity.xml" value="0.3" />
|
<entry key="app/src/main/res/layout/reader_activity.xml" value="0.3" />
|
||||||
<entry key="app/src/main/res/layout/reader_item.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/reader_item.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/search_result_item.xml" value="0.2489868287740628" />
|
<entry key="app/src/main/res/layout/search_result_item.xml" value="0.2489868287740628" />
|
||||||
|
<entry key="app/src/main/res/layout/source_select_dialog_item.xml" value="0.5119791666666667" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ abstract class Source {
|
|||||||
abstract suspend fun info(itemID: String): ItemInfo
|
abstract suspend fun info(itemID: String): ItemInfo
|
||||||
|
|
||||||
@Composable
|
@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 = { }
|
open fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = { }
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class History(override val di: DI) : Source(), DIAware {
|
|||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)?) {
|
override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ data class HitomiItemInfo(
|
|||||||
|
|
||||||
class Hitomi(app: Application) : Source(), DIAware {
|
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)
|
private val logger = newLogger(LoggerFactory.default)
|
||||||
|
|
||||||
@@ -223,10 +223,10 @@ class Hitomi(app: Application) : Source(), DIAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)?) {
|
override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
|
||||||
itemInfo as HitomiItemInfo
|
itemInfo as HitomiItemInfo
|
||||||
|
|
||||||
FullSearchResult(itemInfo = itemInfo)
|
FullSearchResult(itemInfo = itemInfo, onEvent = onEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = {
|
override fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = {
|
||||||
@@ -434,7 +434,7 @@ class Hitomi(app: Application) : Source(), DIAware {
|
|||||||
|
|
||||||
@OptIn(ExperimentalCoilApi::class)
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun FullSearchResult(itemInfo: HitomiItemInfo) {
|
fun FullSearchResult(itemInfo: HitomiItemInfo, onEvent: (SearchResultEvent) -> Unit) {
|
||||||
var group by remember { mutableStateOf(emptyList<String>()) }
|
var group by remember { mutableStateOf(emptyList<String>()) }
|
||||||
var pageCount by remember { mutableStateOf("-") }
|
var pageCount by remember { mutableStateOf("-") }
|
||||||
|
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<Channel<ItemInfo>, Int> = withContext(Dispatchers.IO) {
|
|
||||||
val channel = Channel<ItemInfo>()
|
|
||||||
|
|
||||||
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<SearchSuggestion> {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun images(itemID: String): List<String> {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
@@ -24,59 +24,48 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.updateTransition
|
import androidx.compose.animation.core.updateTransition
|
||||||
import androidx.compose.foundation.MutatePriority
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.ScrollState
|
|
||||||
import androidx.compose.foundation.gestures.*
|
import androidx.compose.foundation.gestures.*
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
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.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
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.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
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.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
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.drawablepainter.rememberDrawablePainter
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
|
import org.kodein.di.compose.withDI
|
||||||
import org.kodein.log.LoggerFactory
|
import org.kodein.log.LoggerFactory
|
||||||
import org.kodein.log.newLogger
|
import org.kodein.log.newLogger
|
||||||
import xyz.quaver.pupil.*
|
import xyz.quaver.pupil.*
|
||||||
import xyz.quaver.pupil.R
|
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.types.*
|
||||||
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
|
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
|
||||||
import xyz.quaver.pupil.ui.composable.FloatingSearchBar
|
import xyz.quaver.pupil.ui.composable.FloatingSearchBar
|
||||||
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
|
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
|
||||||
import xyz.quaver.pupil.ui.composable.SubFabItem
|
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.theme.PupilTheme
|
||||||
import xyz.quaver.pupil.ui.view.ProgressCardView
|
import xyz.quaver.pupil.ui.view.ProgressCardView
|
||||||
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
||||||
@@ -101,7 +90,7 @@ class MainActivity : ComponentActivity(), DIAware {
|
|||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
PupilTheme {
|
PupilTheme {
|
||||||
val source: Source? by model.source.observeAsState(null)
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
||||||
var isFabExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
|
var isFabExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
|
||||||
var isFabVisible by remember { mutableStateOf(true) }
|
var isFabVisible by remember { mutableStateOf(true) }
|
||||||
@@ -121,10 +110,17 @@ class MainActivity : ComponentActivity(), DIAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var openSourceSelectDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(navigationIconProgress) {
|
LaunchedEffect(navigationIconProgress) {
|
||||||
navigationIcon.progress = navigationIconProgress
|
navigationIcon.progress = navigationIconProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (openSourceSelectDialog)
|
||||||
|
SourceSelectDialog {
|
||||||
|
openSourceSelectDialog = false
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
MultipleFloatingActionButton(
|
MultipleFloatingActionButton(
|
||||||
@@ -178,19 +174,23 @@ class MainActivity : ComponentActivity(), DIAware {
|
|||||||
) {
|
) {
|
||||||
items(model.searchResults, key = { it.itemID }) { itemInfo ->
|
items(model.searchResults, key = { it.itemID }) { itemInfo ->
|
||||||
ProgressCardView(
|
ProgressCardView(
|
||||||
progress = 0.5f,
|
progress = 0.5f
|
||||||
onClick = {
|
|
||||||
startActivity(
|
|
||||||
Intent(
|
|
||||||
this@MainActivity,
|
|
||||||
ReaderActivity::class.java
|
|
||||||
).apply {
|
|
||||||
putExtra("source", model.source.value!!.name)
|
|
||||||
putExtra("id", itemInfo.itemID)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
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(
|
Icon(
|
||||||
painter = rememberDrawablePainter(navigationIcon),
|
painter = rememberDrawablePainter(navigationIcon),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(bounded = false)
|
||||||
|
) {
|
||||||
|
focusManager.clearFocus()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
Image(
|
||||||
|
painterResource(model.source.iconResID),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(24.dp).clickable {
|
||||||
|
openSourceSelectDialog = true
|
||||||
|
}
|
||||||
|
)
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Sort,
|
Icons.Default.Sort,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|||||||
@@ -18,37 +18,67 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
package xyz.quaver.pupil.ui.dialog
|
||||||
|
|
||||||
import android.app.Dialog
|
import androidx.compose.foundation.Image
|
||||||
import android.os.Bundle
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import org.kodein.di.DIAware
|
import androidx.compose.material.*
|
||||||
import org.kodein.di.android.x.closestDI
|
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
|
Icon(
|
||||||
var onSourceSettingsSelectedListener: ((String) -> Unit)? = null
|
Icons.Default.Settings,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.5f)
|
||||||
|
)
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {/*
|
Button(onClick = { /*TODO*/ }) {
|
||||||
return Dialog(requireContext()).apply {
|
Text("GO")
|
||||||
window?.requestFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
|
||||||
|
|
||||||
val sourcesWithPreferenceID = direct.instance<SourcePreferenceIDs>().map { it.first }
|
|
||||||
val preferences = direct.instance<SourceEntries>().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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
*/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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -25,11 +25,9 @@ import xyz.quaver.pupil.sources.Source
|
|||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ProgressCardView(progress: Float? = null, onLongClick: (() -> Unit)? = null, onClick: () -> Unit, content: @Composable () -> Unit) {
|
fun ProgressCardView(progress: Float? = null, content: @Composable () -> Unit) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier.padding(8.dp),
|
||||||
.padding(8.dp)
|
|
||||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick),
|
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
elevation = 4.dp
|
elevation = 4.dp
|
||||||
) {
|
) {
|
||||||
@@ -38,64 +36,4 @@ fun ProgressCardView(progress: Float? = null, onLongClick: (() -> Unit)? = null,
|
|||||||
content()
|
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,10 +20,7 @@ package xyz.quaver.pupil.ui.viewmodel
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
@@ -34,6 +31,7 @@ import org.kodein.log.LoggerFactory
|
|||||||
import org.kodein.log.newLogger
|
import org.kodein.log.newLogger
|
||||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
import xyz.quaver.pupil.sources.*
|
import xyz.quaver.pupil.sources.*
|
||||||
|
import xyz.quaver.pupil.sources.hitomi.Hitomi
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
import xyz.quaver.pupil.util.source
|
import xyz.quaver.pupil.util.source
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
@@ -61,21 +59,13 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
direct.source(it)
|
direct.source(it)
|
||||||
}
|
}
|
||||||
private var sourceFactory: (String) -> Source = defaultSourceFactory
|
private var sourceFactory: (String) -> Source = defaultSourceFactory
|
||||||
private val _source = MutableLiveData<Source>()
|
var source by mutableStateOf(sourceFactory("hitomi.la"))
|
||||||
val source: LiveData<Source> = _source
|
private set
|
||||||
|
|
||||||
val availableSortMode = Transformations.map(_source) {
|
var sortModeIndex by mutableStateOf(0)
|
||||||
it.availableSortMode
|
private set
|
||||||
}
|
|
||||||
|
|
||||||
val sortModeIndex = MutableLiveData<Int>()
|
var currentPage by mutableStateOf(1)
|
||||||
|
|
||||||
val sourceIcon = Transformations.map(_source) {
|
|
||||||
it.iconResID
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _currentPage = MutableLiveData<Int>()
|
|
||||||
val currentPage: LiveData<Int> = _currentPage
|
|
||||||
|
|
||||||
private val totalItems = MutableLiveData<Int>()
|
private val totalItems = MutableLiveData<Int>()
|
||||||
|
|
||||||
@@ -88,14 +78,9 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
private val _suggestions = MutableLiveData<List<SearchSuggestion>>()
|
private val _suggestions = MutableLiveData<List<SearchSuggestion>>()
|
||||||
val suggestions: LiveData<List<SearchSuggestion>> = _suggestions
|
val suggestions: LiveData<List<SearchSuggestion>> = _suggestions
|
||||||
|
|
||||||
init {
|
|
||||||
setSourceAndReset("hitomi.la")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSourceAndReset(sourceName: String) {
|
fun setSourceAndReset(sourceName: String) {
|
||||||
_source.value = sourceFactory(sourceName).also {
|
source = sourceFactory(sourceName)
|
||||||
sortModeIndex.value = 0
|
sortModeIndex = 0
|
||||||
}
|
|
||||||
|
|
||||||
query = ""
|
query = ""
|
||||||
resetAndQuery()
|
resetAndQuery()
|
||||||
@@ -103,7 +88,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
|
|
||||||
fun resetAndQuery() {
|
fun resetAndQuery() {
|
||||||
queryStack.add(query)
|
queryStack.add(query)
|
||||||
setPage(1)
|
currentPage = 1
|
||||||
|
|
||||||
query()
|
query()
|
||||||
}
|
}
|
||||||
@@ -119,16 +104,13 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
when {
|
when {
|
||||||
mode == MainMode.DOWNLOADS -> "downloads"
|
mode == MainMode.DOWNLOADS -> "downloads"
|
||||||
//source.value is Downloads -> "hitomi.la"
|
//source.value is Downloads -> "hitomi.la"
|
||||||
else -> source.value!!.name
|
else -> source.name
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query() {
|
fun query() {
|
||||||
val perPage = Preferences["per_page", "25"].toInt()
|
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()
|
suggestionJob?.cancel()
|
||||||
queryJob?.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) {
|
fun random(callback: (ItemInfo) -> Unit) {
|
||||||
if (totalItems.value!! == 0)
|
if (totalItems.value!! == 0)
|
||||||
return
|
return
|
||||||
@@ -168,12 +146,12 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
_source.value?.search(
|
source.search(
|
||||||
query + Preferences["default_query", ""],
|
query + Preferences["default_query", ""],
|
||||||
random .. random,
|
random .. random,
|
||||||
sortModeIndex.value!!
|
sortModeIndex
|
||||||
)?.first?.receive()
|
).first.receive()
|
||||||
}?.let(callback)
|
}.let(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +164,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
@SuppressLint("NullSafeMutableLiveData")
|
@SuppressLint("NullSafeMutableLiveData")
|
||||||
_suggestions.value = withContext(Dispatchers.IO) {
|
_suggestions.value = withContext(Dispatchers.IO) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
_source.value!!.suggestion(query)
|
source.suggestion(query)
|
||||||
}.getOrElse { emptyList() }
|
}.getOrElse { emptyList() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user