From 077d9b976c89c7ff2f277fa73f9d7e8a7d571749 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 16 Dec 2021 16:38:59 +0900 Subject: [PATCH] SourceSelectDialog --- .idea/deploymentTargetDropDown.xml | 12 +-- .idea/misc.xml | 3 + .../java/xyz/quaver/pupil/sources/Common.kt | 2 +- .../java/xyz/quaver/pupil/sources/History.kt | 2 +- .../java/xyz/quaver/pupil/sources/Hitomi.kt | 8 +- .../java/xyz/quaver/pupil/sources/ImHentai.kt | 83 ------------------ .../java/xyz/quaver/pupil/ui/MainActivity.kt | 80 ++++++++++-------- .../pupil/ui/dialog/SourceSelectDialog.kt | 84 +++++++++++++------ .../quaver/pupil/ui/view/ProgressCardView.kt | 66 +-------------- .../pupil/ui/viewmodel/MainViewModel.kt | 54 ++++-------- 10 files changed, 137 insertions(+), 257 deletions(-) delete mode 100644 app/src/main/java/xyz/quaver/pupil/sources/ImHentai.kt 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() } } }