Initial migrate SearchLayout to Compose

TODO: advanced options
This commit is contained in:
tarsin committed 2023-06-27 18:33:08 +08:00
1 parent 2829d081cc
commit 7df7a783f6
15 files changed
+298 -1366

No files matched your search

@@ -2,6 +2,7 @@
package com.hippo.ehviewer
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.FavListUrlBuilder
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@@ -59,6 +60,7 @@ object Settings : DataStorePreferences(null) {
var securityDelay by intPref("require_unlock_delay", 0)
var clipboardTextHashCode by intPref("clipboard_text_hash_code", 0)
var listThumbSize by intPref("list_tile_size", 40)
var searchCategory by intPref("search_pref", EhUtils.ALL_CATEGORY)
var requestNewsTimerHour by intPref("request_news_timer_hour", -1)
var requestNewsTimerMinute by intPref("request_news_timer_minute", -1)
var dataMapNextId by intPref("data_map_next_id", 0)
@@ -66,7 +68,6 @@ object Settings : DataStorePreferences(null) {
var defaultDownloadLabel by stringOrNullPref("default_download_label", null)
var displayName by stringOrNullPref("display_name", null)
var avatar by stringOrNullPref("avatar", null)
var dohUrl by stringPref("doh_url", "")
var language by stringPref("app_language", "system").observed { updateWhenLocaleChanges() }
var lastDawnDay by longPref("last_dawn_day", 0)
@@ -15,132 +15,7 @@
*/
package com.hippo.ehviewer.ui.legacy
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.AutoCompleteTextView
import android.widget.CheckBox
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import com.google.android.material.textfield.TextInputLayout
import com.hippo.ehviewer.R
import com.hippo.ehviewer.yorozuya.toIntOrDefault
class AdvanceSearchTable @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
) : LinearLayout(context, attrs) {
private var mSh: CheckBox
private var mSto: CheckBox
private var mMinRating: TextInputLayout
private var mSp: CheckBox
private var mSpf: EditText
private var mSpt: EditText
private var mSfl: CheckBox
private var mSfu: CheckBox
private var mSft: CheckBox
private val mArray: Array<String> = context.resources.getStringArray(R.array.search_min_rating)
init {
orientation = VERTICAL
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.widget_advance_search_table, this)
val row0 = getChildAt(0) as ViewGroup
mSh = row0.getChildAt(0) as CheckBox
mSto = row0.getChildAt(1) as CheckBox
val row1 = getChildAt(1) as ViewGroup
mMinRating = row1.getChildAt(0) as TextInputLayout
val row2 = getChildAt(2) as ViewGroup
mSp = row2.getChildAt(0) as CheckBox
mSpf = row2.getChildAt(1) as EditText
mSpt = row2.getChildAt(3) as EditText
val row4 = getChildAt(4) as ViewGroup
mSfl = row4.getChildAt(0) as CheckBox
mSfu = row4.getChildAt(1) as CheckBox
mSft = row4.getChildAt(2) as CheckBox
mSpt.setOnEditorActionListener { v: TextView, _: Int, _: KeyEvent? ->
val nextView = v.focusSearch(
FOCUS_DOWN,
)
nextView?.requestFocus(FOCUS_DOWN)
true
}
mSpt.isEnabled = mSp.isChecked
mSpf.isEnabled = mSp.isChecked
mSp.setOnCheckedChangeListener { _: CompoundButton, isChecked: Boolean ->
mSpt.isEnabled = isChecked
mSpf.isEnabled = isChecked
}
}
var advanceSearch: Int
get() {
var advanceSearch = 0
if (mSh.isChecked) advanceSearch = advanceSearch or SH
if (mSto.isChecked) advanceSearch = advanceSearch or STO
if (mSfl.isChecked) advanceSearch = advanceSearch or SFL
if (mSfu.isChecked) advanceSearch = advanceSearch or SFU
if (mSft.isChecked) advanceSearch = advanceSearch or SFT
return advanceSearch
}
set(advanceSearch) {
mSh.isChecked = advanceSearch and SH != 0
mSto.isChecked = advanceSearch and STO != 0
mSfl.isChecked = advanceSearch and SFL != 0
mSfu.isChecked = advanceSearch and SFU != 0
mSft.isChecked = advanceSearch and SFT != 0
}
var minRating: Int
get() {
val position = mArray.indexOf(mMinRating.editText!!.text.toString())
return if (position > 0) {
position + 1
} else {
-1
}
}
set(minRating) {
(mMinRating.editText!! as AutoCompleteTextView).setText(
mArray[if (minRating in 2..5) minRating - 1 else 0],
false,
)
}
var pageFrom: Int
get() = if (mSp.isChecked) {
mSpf.text.toString().toIntOrDefault(-1)
} else {
-1
}
set(pageFrom) {
if (pageFrom > 0) {
mSpf.setText(pageFrom.toString())
mSp.isChecked = true
} else {
mSp.isChecked = false
mSpf.text = null
}
}
var pageTo: Int
get() = if (mSp.isChecked) {
mSpt.text.toString().toIntOrDefault(-1)
} else {
-1
}
set(pageTo) {
if (pageTo > 0) {
mSpt.setText(pageTo.toString())
mSp.isChecked = true
} else {
mSp.isChecked = false
}
}
class AdvanceSearchTable {
companion object {
const val SH = 0x1
const val STO = 0x2
@@ -1,106 +0,0 @@
package com.hippo.ehviewer.ui.legacy
import android.content.Context
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.util.AttributeSet
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.Card
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.google.accompanist.themeadapter.material3.Mdc3Theme
import com.hippo.ehviewer.AppConfig
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.image.Image
import com.hippo.ehviewer.util.pickVisualMedia
import com.hippo.unifile.UniFile
import kotlinx.coroutines.launch
class ImageSearchLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
) : AbstractComposeView(context, attrs, defStyle) {
private var uss by mutableStateOf(false)
private var osc by mutableStateOf(false)
private var path by mutableStateOf("")
@Composable
override fun Content() {
Mdc3Theme {
val coroutineScope = rememberCoroutineScope()
fun selectImage() = coroutineScope.launch {
context.pickVisualMedia(ImageOnly)?.let {
path = it.toString()
}
}
Column(modifier = Modifier.fillMaxWidth()) {
AnimatedVisibility(visible = path.isNotBlank(), modifier = Modifier.align(Alignment.CenterHorizontally)) {
Card {
val maxSize = dimensionResource(id = R.dimen.image_search_max_size)
AsyncImage(
model = path,
contentDescription = null,
modifier = Modifier.sizeIn(maxWidth = maxSize, maxHeight = maxSize),
)
}
}
FilledTonalButton(
onClick = ::selectImage,
modifier = Modifier.fillMaxWidth().padding(8.dp),
) {
Text(text = stringResource(id = R.string.select_image))
}
ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
Row {
Row(modifier = Modifier.weight(1f)) {
Checkbox(checked = uss, onCheckedChange = { uss = it })
Text(text = stringResource(id = R.string.search_uss), modifier = Modifier.align(Alignment.CenterVertically))
}
Row(modifier = Modifier.weight(1f)) {
Checkbox(checked = osc, onCheckedChange = { osc = it })
Text(text = stringResource(id = R.string.search_osc), modifier = Modifier.align(Alignment.CenterVertically))
}
}
}
}
}
}
fun formatListUrlBuilder(lub: ListUrlBuilder) {
if (path.isBlank()) throw EhException(context.getString(R.string.select_image_first))
val uri = Uri.parse(path)
val src = UniFile.fromUri(context, uri)?.imageSource ?: return
val temp = AppConfig.createTempFile() ?: return
val bitmap = ImageDecoder.decodeBitmap(src, Image.imageSearchDecoderSampleListener)
temp.outputStream().use { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, it) }
lub.imagePath = temp.path
lub.isUseSimilarityScan = uss
lub.isOnlySearchCovers = osc
}
}
@@ -1,243 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.legacy
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.CompoundButton
import android.widget.RadioButton
import androidx.annotation.IdRes
class RadioGridGroup : SimpleGridLayout {
/**
*
* Returns the identifier of the selected radio button in this group.
* Upon empty selection, the returned value is -1.
*
* @return the unique id of the selected radio button in this group
* @see .check
* @see .clearCheck
*/
// holds the checked id; the selection is empty by default
@get:IdRes
var checkedRadioButtonId = -1
private set
// tracks children radio buttons checked state
private var mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
// when true, mOnCheckedChangeListener discards events
private var mProtectFromCheckedChange = false
private var mOnCheckedChangeListener: OnCheckedChangeListener? = null
private var mPassThroughListener: PassThroughHierarchyChangeListener? = null
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle,
) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
val a = context.obtainStyledAttributes(attrs, RADIO_ATTRS)
val value = a.getResourceId(0, NO_ID)
if (value != NO_ID) {
checkedRadioButtonId = value
}
a.recycle()
mChildOnCheckedChangeListener = CheckedStateTracker()
mPassThroughListener = PassThroughHierarchyChangeListener()
super.setOnHierarchyChangeListener(mPassThroughListener)
}
/**
* {@inheritDoc}
*/
override fun setOnHierarchyChangeListener(listener: OnHierarchyChangeListener) {
// the user listener is delegated to our pass-through listener
mPassThroughListener!!.mOnHierarchyChangeListener = listener
}
/**
* {@inheritDoc}
*/
override fun onFinishInflate() {
super.onFinishInflate()
// checks the appropriate radio button as requested in the XML file
if (checkedRadioButtonId != -1) {
mProtectFromCheckedChange = true
setCheckedStateForView(checkedRadioButtonId, true)
mProtectFromCheckedChange = false
setCheckedId(checkedRadioButtonId)
}
}
override fun addView(child: View, index: Int, params: LayoutParams) {
if (child is RadioButton) {
if (child.isChecked) {
mProtectFromCheckedChange = true
if (checkedRadioButtonId != -1) {
setCheckedStateForView(checkedRadioButtonId, false)
}
mProtectFromCheckedChange = false
setCheckedId(child.getId())
}
}
super.addView(child, index, params)
}
/**
*
* Sets the selection to the radio button whose identifier is passed in
* parameter. Using -1 as the selection identifier clears the selection;
* such an operation is equivalent to invoking [.clearCheck].
*
* @param id the unique id of the radio button to select in this group
* @see .getCheckedRadioButtonId
* @see .clearCheck
*/
fun check(@IdRes id: Int) {
// don't even bother
if (id != -1 && id == checkedRadioButtonId) {
return
}
if (checkedRadioButtonId != -1) {
setCheckedStateForView(checkedRadioButtonId, false)
}
if (id != -1) {
setCheckedStateForView(id, true)
}
setCheckedId(id)
}
private fun setCheckedId(@IdRes id: Int) {
checkedRadioButtonId = id
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener!!.onCheckedChanged(this, checkedRadioButtonId)
}
}
private fun setCheckedStateForView(viewId: Int, checked: Boolean) {
val checkedView = findViewById<View>(viewId)
if (checkedView is RadioButton) {
checkedView.isChecked = checked
}
}
/**
*
* Clears the selection. When the selection is cleared, no radio button
* in this group is selected and [.getCheckedRadioButtonId] returns
* null.
*
* @see .check
* @see .getCheckedRadioButtonId
*/
fun clearCheck() {
check(-1)
}
/**
*
* Register a callback to be invoked when the checked radio button
* changes in this group.
*
* @param listener the callback to call on checked state change
*/
fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
mOnCheckedChangeListener = listener
}
/**
*
* Interface definition for a callback to be invoked when the checked
* radio button changed in this group.
*/
interface OnCheckedChangeListener {
/**
*
* Called when the checked radio button has changed. When the
* selection is cleared, checkedId is -1.
*
* @param group the group in which the checked radio button has changed
* @param checkedId the unique identifier of the newly checked radio button
*/
fun onCheckedChanged(group: RadioGridGroup?, @IdRes checkedId: Int)
}
private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return
}
mProtectFromCheckedChange = true
if (checkedRadioButtonId != -1) {
setCheckedStateForView(checkedRadioButtonId, false)
}
mProtectFromCheckedChange = false
val id = buttonView.id
setCheckedId(id)
}
}
/**
*
* A pass-through listener acts upon the events and dispatches them
* to another listener. This allows the table layout to set its own internal
* hierarchy change listener without preventing the user to setup his.
*/
private inner class PassThroughHierarchyChangeListener : OnHierarchyChangeListener {
var mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
/**
* {@inheritDoc}
*/
override fun onChildViewAdded(parent: View, child: View) {
if (parent === this@RadioGridGroup && child is RadioButton) {
var id = child.getId()
// generates an id if it's missing
if (id == NO_ID) {
id = generateViewId()
child.setId(id)
}
child.setOnCheckedChangeListener(mChildOnCheckedChangeListener)
}
mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
}
/**
* {@inheritDoc}
*/
override fun onChildViewRemoved(parent: View, child: View) {
if (parent === this@RadioGridGroup && child is RadioButton) {
child.setOnCheckedChangeListener(null)
}
mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
}
}
companion object {
private val RADIO_ATTRS = intArrayOf(android.R.attr.checkedButton)
}
}
@@ -1,375 +1,167 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer 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.
*
* EhViewer 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 EhViewer.
* If not, see <https://www.gnu.org/licenses/>.
*/
package com.hippo.ehviewer.ui.legacy
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import androidx.annotation.IntDef
import androidx.core.content.edit
import androidx.core.view.forEach
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import com.google.accompanist.themeadapter.material3.Mdc3Theme
import com.hippo.ehviewer.AppConfig
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.ListUrlBuilder
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.databinding.SearchAdvanceBinding
import com.hippo.ehviewer.databinding.SearchCategoryBinding
import com.hippo.ehviewer.databinding.SearchNormalBinding
import com.hippo.ehviewer.yorozuya.removeFromParent
import com.hippo.ehviewer.image.Image
import com.hippo.ehviewer.ui.main.ImageSearch
import com.hippo.ehviewer.ui.main.NormalSearch
import com.hippo.ehviewer.util.pickVisualMedia
import com.hippo.unifile.UniFile
import kotlinx.coroutines.launch
@SuppressLint("InflateParams")
class SearchLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
) : EasyRecyclerView(context, attrs),
CompoundButton.OnCheckedChangeListener,
View.OnClickListener,
OnTabSelectedListener {
defStyle: Int = 0,
) : AbstractComposeView(context, attrs, defStyle) {
private var isNormalMode by mutableStateOf(true) // else ImageSearch mode
private var isAdvancedMode by mutableStateOf(false)
@SearchMode
private var mSearchMode = SEARCH_MODE_NORMAL
private var mEnableAdvance = false
private val binding: SearchNormalBinding
private val advanceBinding: SearchAdvanceBinding
private val mImageView: ImageSearchLayout
private val mActionView: View
private val mAction: TabLayout
private val mAdapter: SearchAdapter
private var mHelper: Helper? = null
private val mSharePref: SharedPreferences = Settings.prefs
private val mInflater: LayoutInflater
private var mCategory by mutableIntStateOf(Settings.searchCategory)
private var mSearchMode by mutableIntStateOf(1)
init {
mInflater = LayoutInflater.from(context)
val resources = context.resources
layoutManager = SearchLayoutManager(context)
mAdapter = SearchAdapter()
mAdapter.setHasStableIds(true)
adapter = mAdapter
setHasFixedSize(true)
clipToPadding = false
(itemAnimator as DefaultItemAnimator?)?.supportsChangeAnimations = false
val interval = resources.getDimensionPixelOffset(R.dimen.search_layout_interval)
val paddingH = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_h)
val paddingV = resources.getDimensionPixelOffset(R.dimen.search_layout_margin_v)
val decoration = MarginItemDecoration(
interval,
paddingH,
paddingV,
paddingH,
paddingV,
)
addItemDecoration(decoration)
decoration.applyPaddings(this)
// Create normal view
binding = SearchNormalBinding.inflate(mInflater)
val mCategoryStored = mSharePref.getInt(SEARCH_CATEGORY_PREF, EhUtils.ALL_CATEGORY)
for (mPair in mCategoryTable) {
val mChip = inflate(context, R.layout.filter_chip, null) as IdentifiedChip
mChip.isCheckable = true
mChip.setText(mPair.second)
mChip.idt = mPair.first
mChip.isChecked = mPair.first and mCategoryStored != 0
mChip.setOnLongClickListener {
if (mChip.isChecked) {
binding.searchCategoryChipgroup.forEach {
(it as IdentifiedChip).isChecked = true
private var uss by mutableStateOf(false)
private var osc by mutableStateOf(false)
private var path by mutableStateOf("")
@Composable
override fun Content() {
val coroutineScope = rememberCoroutineScope()
fun selectImage() = coroutineScope.launch {
context.pickVisualMedia(ActivityResultContracts.PickVisualMedia.ImageOnly)?.let {
path = it.toString()
}
}
Mdc3Theme {
Column(
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.search_layout_margin_h)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.search_layout_margin_v)),
) {
AnimatedVisibility(visible = isNormalMode) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.search_category_padding_h), vertical = dimensionResource(id = R.dimen.search_category_padding_v))) {
Text(text = stringResource(id = R.string.search_normal), modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)), style = MaterialTheme.typography.titleMedium)
NormalSearch(
category = mCategory,
onCategoryChanged = {
Settings.searchCategory = it
mCategory = it
},
searchMode = mSearchMode,
onSearchModeChanged = { mSearchMode = it },
isAdvanced = isAdvancedMode,
onAdvancedChanged = { isAdvancedMode = it },
showInfo = { BaseDialogBuilder(context).setMessage(R.string.search_tip).show() },
)
}
}
mChip.isChecked = false
} else {
binding.searchCategoryChipgroup.clearCheck()
mChip.isChecked = true
}
true
AnimatedVisibility(visible = isNormalMode && isAdvancedMode) {
ElevatedCard {
}
}
AnimatedVisibility(visible = !isNormalMode) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.search_category_padding_h), vertical = dimensionResource(id = R.dimen.search_category_padding_v))) {
Text(text = stringResource(id = R.string.search_image), modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)), style = MaterialTheme.typography.titleMedium)
ImageSearch(
imagePath = path,
onSelectImage = ::selectImage,
uss = uss,
onUssChecked = { uss = it },
osc = osc,
onOscChecked = { osc = it },
)
}
}
}
TabRow(
selectedTabIndex = if (isNormalMode) 0 else 1,
divider = {},
) {
Tab(
selected = isNormalMode,
onClick = { isNormalMode = true },
text = { Text(text = stringResource(id = R.string.keyword_search)) },
)
Tab(
selected = !isNormalMode,
onClick = { isNormalMode = false },
text = { Text(text = stringResource(id = R.string.search_image)) },
)
}
}
binding.searchCategoryChipgroup.addView(mChip)
}
binding.searchCategoryChipgroup.setOnCheckedStateChangeListener { group, checkedIds ->
var mCategory = 0
for (index in checkedIds) {
mCategory = mCategory or group.findViewById<IdentifiedChip>(index).idt
}
mSharePref.edit { putInt(SEARCH_CATEGORY_PREF, mCategory) }
}
binding.normalSearchModeHelp.setOnClickListener(this)
binding.searchEnableAdvance.setOnCheckedChangeListener(this)
// Create advance view
advanceBinding = SearchAdvanceBinding.inflate(mInflater)
// Create image view
mImageView = mInflater.inflate(R.layout.search_image, null) as ImageSearchLayout
// Create action view
mActionView = mInflater.inflate(R.layout.search_action, null)
mActionView.layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
mAction = mActionView.findViewById(R.id.action)
mAction.addOnTabSelectedListener(this)
}
fun setHelper(helper: Helper?) {
mHelper = helper
}
fun scrollSearchContainerToTop() {
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0)
}
fun setNormalSearchMode(id: Int) {
binding.normalSearchMode.check(id)
fun setSearchMyTags(isMyTags: Boolean) {
if (isMyTags) mSearchMode = 2
}
fun setCategory(category: Int) {
binding.searchCategoryChipgroup.forEach {
(it as IdentifiedChip).apply {
isChecked = idt and category != 0
}
}
mCategory = category
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
if (buttonView === binding.searchEnableAdvance) {
post {
mEnableAdvance = isChecked
if (mSearchMode == SEARCH_MODE_NORMAL) {
if (mEnableAdvance) {
mAdapter.notifyItemInserted(1)
} else {
mAdapter.notifyItemRemoved(1)
}
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
}
}
}
}
@SuppressLint("NonConstantResourceId")
fun formatListUrlBuilder(urlBuilder: ListUrlBuilder, query: String?) {
urlBuilder.reset()
when (mSearchMode) {
SEARCH_MODE_NORMAL -> {
when (binding.normalSearchMode.checkedRadioButtonId) {
R.id.search_subscription_search -> {
urlBuilder.mode = ListUrlBuilder.MODE_SUBSCRIPTION
}
R.id.search_specify_uploader -> {
urlBuilder.mode = ListUrlBuilder.MODE_UPLOADER
}
R.id.search_specify_tag -> {
urlBuilder.mode = ListUrlBuilder.MODE_TAG
}
else -> {
urlBuilder.mode = ListUrlBuilder.MODE_NORMAL
}
when (isNormalMode) {
true -> {
when (mSearchMode) {
1 -> urlBuilder.mode = ListUrlBuilder.MODE_NORMAL
2 -> urlBuilder.mode = ListUrlBuilder.MODE_SUBSCRIPTION
3 -> urlBuilder.mode = ListUrlBuilder.MODE_UPLOADER
4 -> urlBuilder.mode = ListUrlBuilder.MODE_TAG
}
urlBuilder.keyword = query
urlBuilder.category = Settings.prefs.getInt(
SEARCH_CATEGORY_PREF,
0,
)
if (mEnableAdvance) {
urlBuilder.advanceSearch = advanceBinding.searchAdvanceSearchTable.advanceSearch
urlBuilder.minRating = advanceBinding.searchAdvanceSearchTable.minRating
val pageFrom = advanceBinding.searchAdvanceSearchTable.pageFrom
val pageTo = advanceBinding.searchAdvanceSearchTable.pageTo
if (pageTo != -1 && pageTo < 10) {
throw EhException(context.getString(R.string.search_sp_err1))
} else if (pageFrom != -1 && pageTo != -1 && pageTo - pageFrom < 20) {
throw EhException(context.getString(R.string.search_sp_err2))
}
urlBuilder.pageFrom = pageFrom
urlBuilder.pageTo = pageTo
}
urlBuilder.category = mCategory
}
SEARCH_MODE_IMAGE -> {
false -> {
urlBuilder.mode = ListUrlBuilder.MODE_IMAGE_SEARCH
mImageView.formatListUrlBuilder(urlBuilder)
if (path.isBlank()) throw EhException(context.getString(R.string.select_image_first))
val uri = Uri.parse(path)
val src = UniFile.fromUri(context, uri)?.imageSource ?: return
val temp = AppConfig.createTempFile() ?: return
val bitmap = ImageDecoder.decodeBitmap(src, Image.imageSearchDecoderSampleListener)
temp.outputStream().use { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, it) }
urlBuilder.imagePath = temp.path
urlBuilder.isUseSimilarityScan = uss
urlBuilder.isOnlySearchCovers = osc
}
}
}
override fun onClick(v: View) {
if (binding.normalSearchModeHelp === v) {
BaseDialogBuilder(context)
.setMessage(R.string.search_tip)
.show()
}
}
override fun onTabSelected(tab: TabLayout.Tab) {
post { setSearchMode(tab.position) }
}
private fun setSearchMode(@SearchMode mode: Int) {
val oldItemCount = mAdapter.itemCount
mSearchMode = mode
val newItemCount = mAdapter.itemCount
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1)
mAdapter.notifyItemRangeInserted(0, newItemCount - 1)
if (mHelper != null) {
mHelper!!.onChangeSearchMode()
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {}
@IntDef(SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE)
@Retention(AnnotationRetention.SOURCE)
private annotation class SearchMode
interface Helper {
fun onChangeSearchMode()
}
internal class SearchLayoutManager(context: Context?) : LinearLayoutManager(context!!) {
override fun onLayoutChildren(recycler: Recycler, state: State) {
try {
super.onLayoutChildren(recycler, state)
} catch (e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
}
private inner class SimpleHolder(itemView: View) : ViewHolder(itemView)
private inner class SearchAdapter : Adapter<SimpleHolder>() {
override fun getItemCount(): Int {
var count = SEARCH_ITEM_COUNT_ARRAY[mSearchMode]
if (mSearchMode == SEARCH_MODE_NORMAL && !mEnableAdvance) {
count--
}
return count
}
override fun getItemViewType(position: Int): Int {
var type = SEARCH_ITEM_TYPE[mSearchMode][position]
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION
}
return type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleHolder {
val view: View
if (viewType == ITEM_TYPE_ACTION) {
mActionView.removeFromParent()
view = mActionView
} else {
view = mInflater.inflate(R.layout.search_category, parent, false)
SearchCategoryBinding.bind(view).run {
when (viewType) {
ITEM_TYPE_NORMAL -> {
categoryTitle.setText(R.string.search_normal)
binding.root.removeFromParent()
categoryContent.addView(binding.root)
}
ITEM_TYPE_NORMAL_ADVANCE -> {
categoryTitle.setText(R.string.search_advance)
advanceBinding.root.removeFromParent()
categoryContent.addView(advanceBinding.root)
}
ITEM_TYPE_IMAGE -> {
categoryTitle.setText(R.string.search_image)
mImageView.removeFromParent()
categoryContent.addView(mImageView)
}
}
}
}
return SimpleHolder(view)
}
override fun onBindViewHolder(holder: SimpleHolder, position: Int) {
if (holder.itemViewType == ITEM_TYPE_ACTION) {
mAction.selectTab(mAction.getTabAt(mSearchMode))
}
}
override fun getItemId(position: Int): Long {
var type = SEARCH_ITEM_TYPE[mSearchMode][position]
if (mSearchMode == SEARCH_MODE_NORMAL && position == 1 && !mEnableAdvance) {
type = ITEM_TYPE_ACTION
}
return type.toLong()
}
}
companion object {
const val SEARCH_MODE_NORMAL = 0
const val SEARCH_MODE_IMAGE = 1
const val SEARCH_CATEGORY_PREF = "search_pref"
private const val ITEM_TYPE_NORMAL = 0
private const val ITEM_TYPE_NORMAL_ADVANCE = 1
private const val ITEM_TYPE_IMAGE = 2
private const val ITEM_TYPE_ACTION = 3
private val SEARCH_ITEM_COUNT_ARRAY = intArrayOf(
3,
2,
)
private val mCategoryTable: ArrayList<Pair<Int, Int>> = arrayListOf(
Pair(EhUtils.DOUJINSHI, R.string.doujinshi),
Pair(EhUtils.MANGA, R.string.manga),
Pair(EhUtils.ARTIST_CG, R.string.artist_cg),
Pair(EhUtils.GAME_CG, R.string.game_cg),
Pair(EhUtils.WESTERN, R.string.western),
Pair(EhUtils.NON_H, R.string.non_h),
Pair(EhUtils.IMAGE_SET, R.string.image_set),
Pair(EhUtils.COSPLAY, R.string.cosplay),
Pair(EhUtils.ASIAN_PORN, R.string.asian_porn),
Pair(EhUtils.MISC, R.string.misc),
)
private val SEARCH_ITEM_TYPE = arrayOf(
intArrayOf(ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION),
intArrayOf(
ITEM_TYPE_IMAGE,
ITEM_TYPE_ACTION,
),
)
fun setHelper(helper: () -> Unit) {
}
}
class IdentifiedChip @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
) : Chip(
context,
attrs,
) {
var idt = 0
}
@@ -1,164 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.legacy
import android.content.Context
import android.util.AttributeSet
import android.view.ViewGroup
import com.hippo.ehviewer.R
/**
* not scrollable
*
* @author Hippo
*/
open class SimpleGridLayout : ViewGroup {
private var mColumnCount = 0
private var mItemMargin = 0
private var mRowHeights: IntArray? = null
private var mItemWidth = 0
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle,
) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
val a = context.obtainStyledAttributes(attrs, R.styleable.SimpleGridLayout)
mColumnCount = a.getInteger(R.styleable.SimpleGridLayout_columnCount, DEFAULT_COLUMN_COUNT)
mItemMargin = a.getDimensionPixelOffset(R.styleable.SimpleGridLayout_itemMargin, 0)
a.recycle()
}
fun setItemMargin(itemMargin: Int) {
if (mItemMargin != itemMargin) {
mItemMargin = itemMargin
requestLayout()
}
}
fun setColumnCount(columnCount: Int) {
check(columnCount > 0) { "Column count can't be $columnCount" }
if (mColumnCount != columnCount) {
mColumnCount = columnCount
requestLayout()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val maxRowCount = (childCount + mColumnCount - 1) / mColumnCount
if (mRowHeights == null || mRowHeights!!.size != maxRowCount) {
mRowHeights = IntArray(maxRowCount)
}
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
var maxWidth = MeasureSpec.getSize(widthMeasureSpec)
var maxHeight = MeasureSpec.getSize(heightMeasureSpec)
if (widthMode == MeasureSpec.UNSPECIFIED) {
maxWidth = 300
}
if (heightMode == MeasureSpec.UNSPECIFIED) {
maxHeight = Int.MAX_VALUE and (0x3 shl 30).inv()
}
// Get item width MeasureSpec
mItemWidth =
((maxWidth - paddingLeft - paddingRight - (mColumnCount - 1) * mItemMargin) / mColumnCount)
.coerceAtLeast(1)
val itemWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY)
val itemHeightMeasureSpec = MeasureSpec.UNSPECIFIED
val measuredWidth = maxWidth
var measuredHeight = 0
var rowHeight = 0
var row = 0
val count = childCount
var index = 0
var indexInRow = 0
while (index < count) {
val child = getChildAt(index)
if (child.visibility == GONE) {
indexInRow--
index++
indexInRow++
continue
}
child.measure(itemWidthMeasureSpec, itemHeightMeasureSpec)
if (indexInRow == mColumnCount) {
// New row
indexInRow = 0
rowHeight = 0
row++
}
rowHeight = rowHeight.coerceAtLeast(child.measuredHeight)
if (indexInRow == mColumnCount - 1 || index == count - 1) {
mRowHeights!![row] = rowHeight
measuredHeight += rowHeight + mItemMargin
}
index++
indexInRow++
}
measuredHeight -= mItemMargin
measuredHeight = (measuredHeight + paddingTop + paddingBottom).coerceIn(0, maxHeight)
setMeasuredDimension(measuredWidth, measuredHeight)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val itemWidth = mItemWidth
val itemMargin = mItemMargin
val paddingLeft = paddingLeft
var left = paddingLeft
var top = paddingTop
var row = 0
val count = childCount
var index = 0
var indexInRow = 0
while (index < count) {
val child = getChildAt(index)
if (child.visibility == GONE) {
indexInRow--
index++
indexInRow++
continue
}
if (indexInRow == mColumnCount) {
// New row
left = paddingLeft
top += mRowHeights!![row] + itemMargin
indexInRow = 0
row++
}
child.layout(left, top, left + child.measuredWidth, top + child.measuredHeight)
left += itemWidth + itemMargin
index++
indexInRow++
}
}
companion object {
private const val DEFAULT_COLUMN_COUNT = 3
}
}
@@ -0,0 +1,61 @@
package com.hippo.ehviewer.ui.main
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.Card
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.hippo.ehviewer.R
@Composable
fun ImageSearch(
imagePath: String,
onSelectImage: () -> Unit,
uss: Boolean,
onUssChecked: (Boolean) -> Unit,
osc: Boolean,
onOscChecked: (Boolean) -> Unit,
) = Column(modifier = Modifier.fillMaxWidth()) {
AnimatedVisibility(visible = imagePath.isNotBlank(), modifier = Modifier.align(Alignment.CenterHorizontally)) {
Card {
val maxSize = dimensionResource(id = R.dimen.image_search_max_size)
AsyncImage(
model = imagePath,
contentDescription = null,
modifier = Modifier.sizeIn(maxWidth = maxSize, maxHeight = maxSize),
)
}
}
FilledTonalButton(
onClick = onSelectImage,
modifier = Modifier.fillMaxWidth().padding(8.dp),
) {
Text(text = stringResource(id = R.string.select_image))
}
ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
Row {
Row(modifier = Modifier.weight(1f)) {
Checkbox(checked = uss, onCheckedChange = onUssChecked)
Text(text = stringResource(id = R.string.search_uss), modifier = Modifier.align(Alignment.CenterVertically))
}
Row(modifier = Modifier.weight(1f)) {
Checkbox(checked = osc, onCheckedChange = onOscChecked)
Text(text = stringResource(id = R.string.search_osc), modifier = Modifier.align(Alignment.CenterVertically))
}
}
}
}
@@ -0,0 +1,100 @@
package com.hippo.ehviewer.ui.main
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Help
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhUtils
private val categoryTable = arrayOf(
EhUtils.DOUJINSHI to R.string.doujinshi,
EhUtils.MANGA to R.string.manga,
EhUtils.ARTIST_CG to R.string.artist_cg,
EhUtils.GAME_CG to R.string.game_cg,
EhUtils.WESTERN to R.string.western,
EhUtils.NON_H to R.string.non_h,
EhUtils.IMAGE_SET to R.string.image_set,
EhUtils.COSPLAY to R.string.cosplay,
EhUtils.ASIAN_PORN to R.string.asian_porn,
EhUtils.MISC to R.string.misc,
)
private val modeTable = arrayOf(
1 to R.string.search_normal_search,
2 to R.string.search_subscription_search,
3 to R.string.search_specify_uploader,
4 to R.string.search_specify_tag,
)
@Composable
fun NormalSearch(
category: Int,
onCategoryChanged: (Int) -> Unit,
searchMode: Int,
onSearchModeChanged: (Int) -> Unit,
isAdvanced: Boolean,
onAdvancedChanged: (Boolean) -> Unit,
showInfo: () -> Unit,
) {
FlowRow(modifier = Modifier.wrapContentHeight(align = Alignment.Top)) {
categoryTable.forEach {
val selected = category and it.first != 0
FilterChip(
selected = selected,
onClick = { onCategoryChanged(if (selected) category xor it.first else category or it.first) },
label = { Text(text = stringResource(id = it.second)) },
leadingIcon = { if (selected) Icon(imageVector = Icons.Default.Check, contentDescription = null) },
modifier = Modifier.padding(horizontal = 4.dp).align(alignment = Alignment.CenterVertically),
)
}
}
ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
FlowRow {
modeTable.forEach {
Row(
modifier = Modifier.weight(1F).width(112.dp),
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(
selected = (it.first == searchMode),
onClick = { onSearchModeChanged(it.first) },
)
Text(
text = stringResource(id = it.second),
)
}
}
}
Row(horizontalArrangement = Arrangement.Start) {
Box(modifier = Modifier.weight(1f)) {
IconButton(onClick = showInfo) {
Icon(imageVector = Icons.Default.Help, contentDescription = null)
}
}
Row(modifier = Modifier.weight(1f)) {
Checkbox(checked = isAdvanced, onCheckedChange = onAdvancedChanged)
Text(text = stringResource(id = R.string.search_enable_advance), modifier = Modifier.align(Alignment.CenterVertically))
}
}
}
}
@@ -89,7 +89,6 @@ import com.hippo.ehviewer.ui.legacy.FabLayout.OnClickFabListener
import com.hippo.ehviewer.ui.legacy.FabLayout.OnExpandListener
import com.hippo.ehviewer.ui.legacy.FastScroller.OnDragHandlerListener
import com.hippo.ehviewer.ui.legacy.GalleryInfoContentHelper
import com.hippo.ehviewer.ui.legacy.SearchLayout
import com.hippo.ehviewer.ui.legacy.ViewTransition
import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper
import com.hippo.ehviewer.ui.setMD3Content
@@ -109,7 +108,7 @@ import java.time.ZoneId
import java.time.ZoneOffset
import com.hippo.ehviewer.download.DownloadManager as downloadManager
class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.Helper, OnClickFabListener, OnExpandListener {
class GalleryListScene : SearchBarScene(), OnDragHandlerListener, OnClickFabListener, OnExpandListener {
private val mCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
when (mState) {
@@ -309,7 +308,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
binding.fabLayout.setSecondaryFabVisibilityAt(2, !isPopular)
// Update normal search mode and category
binding.searchLayout.setNormalSearchMode(if (mode == MODE_SUBSCRIPTION) R.id.search_subscription_search else R.id.search_normal_search)
binding.searchLayout.setSearchMyTags(mode == MODE_SUBSCRIPTION)
if (category != EhUtils.NONE) {
binding.searchLayout.setCategory(category)
}
@@ -378,7 +377,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
}
setSearchBarHint()
setSearchBarSuggestionProvider()
binding.searchLayout.setHelper(this)
binding.searchLayout.setHelper { showSearchBar() }
binding.searchLayout.run {
setPadding(
paddingLeft,
@@ -831,13 +830,11 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
STATE_SEARCH -> {
mViewTransition!!.showView(1, animation)
binding.searchLayout.scrollSearchContainerToTop()
selectSearchFab(animation)
}
STATE_SEARCH_SHOW_LIST -> {
mViewTransition!!.showView(1, animation)
binding.searchLayout.scrollSearchContainerToTop()
selectSearchFab(animation)
}
}
@@ -847,15 +844,9 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
selectActionFab(animation)
}
STATE_SEARCH -> {
mViewTransition!!.showView(1, animation)
binding.searchLayout.scrollSearchContainerToTop()
}
STATE_SEARCH -> mViewTransition!!.showView(1, animation)
STATE_SEARCH_SHOW_LIST -> {
mViewTransition!!.showView(1, animation)
binding.searchLayout.scrollSearchContainerToTop()
}
STATE_SEARCH_SHOW_LIST -> mViewTransition!!.showView(1, animation)
}
STATE_SEARCH, STATE_SEARCH_SHOW_LIST -> if (state == STATE_NORMAL) {
@@ -926,10 +917,6 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H
}
}
override fun onChangeSearchMode() {
showSearchBar()
}
private fun onGetGalleryListSuccess(result: GalleryListParser.Result, taskId: Int) {
if (Settings.preloadThumbAggressively) {
lifecycleScope.launchIO {