diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt index fc652ce93..c7d7903e8 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt @@ -994,28 +994,29 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, SearchLayout.H private fun onApplySearch(query: String?) { _binding ?: return - if (null == mHelper) { - return - } - if (mState == STATE_SEARCH || mState == STATE_SEARCH_SHOW_LIST) { - try { - binding.searchLayout.formatListUrlBuilder(mUrlBuilder, query) - } catch (e: EhException) { - showTip(e.message, LENGTH_LONG) - return + mHelper ?: return + lifecycleScope.launchIO { + if (mState == STATE_SEARCH || mState == STATE_SEARCH_SHOW_LIST) { + try { + binding.searchLayout.formatListUrlBuilder(mUrlBuilder, query) + } catch (e: EhException) { + showTip(e.message, LENGTH_LONG) + return@launchIO + } + } else { + val oldMode = mUrlBuilder.mode + // If it's MODE_SUBSCRIPTION, keep it + val newMode = if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL + mUrlBuilder.reset() + mUrlBuilder.mode = newMode + mUrlBuilder.keyword = query + } + withUIContext { + onUpdateUrlBuilder() + mHelper!!.refresh() + setState(STATE_NORMAL) } - } else { - val oldMode = mUrlBuilder.mode - // If it's MODE_SUBSCRIPTION, keep it - val newMode = - if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL - mUrlBuilder.reset() - mUrlBuilder.mode = newMode - mUrlBuilder.keyword = query } - onUpdateUrlBuilder() - mHelper!!.refresh() - setState(STATE_NORMAL) } override fun onStartDragHandler() { diff --git a/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.kt b/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.kt index 433671a5d..be9c3a708 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.kt +++ b/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.kt @@ -1,6 +1,9 @@ package com.hippo.ehviewer.widget 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.foundation.layout.Column @@ -18,7 +21,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,8 +30,12 @@ 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.image.Image +import com.hippo.unifile.UniFile import com.hippo.util.pickVisualMedia import kotlinx.coroutines.launch @@ -38,16 +44,18 @@ class ImageSearchLayout @JvmOverloads constructor( 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() - var uss by rememberSaveable { mutableStateOf(false) } - var osc by rememberSaveable { mutableStateOf(false) } - var path by rememberSaveable { mutableStateOf("") } - fun selectImage() = coroutineScope.launch { - path = pickVisualMedia(ImageOnly).toString() + pickVisualMedia(ImageOnly)?.let { + path = it.toString() + } } Column(modifier = Modifier.fillMaxWidth()) { @@ -84,5 +92,14 @@ class ImageSearchLayout @JvmOverloads constructor( } 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 + 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 } } diff --git a/app/src/main/java/com/hippo/widget/FixedAspectImageView.java b/app/src/main/java/com/hippo/widget/FixedAspectImageView.java deleted file mode 100644 index 11ef888ee..000000000 --- a/app/src/main/java/com/hippo/widget/FixedAspectImageView.java +++ /dev/null @@ -1,309 +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.widget; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.View; - -import com.google.android.material.imageview.ShapeableImageView; -import com.hippo.ehviewer.R; -import com.hippo.yorozuya.MathUtils; - -public class FixedAspectImageView extends ShapeableImageView { - - private static final int[] MIN_ATTRS = { - android.R.attr.minWidth, - android.R.attr.minHeight - }; - - private static final int[] ATTRS = { - android.R.attr.adjustViewBounds, - android.R.attr.maxWidth, - android.R.attr.maxHeight - }; - - private int mMinWidth = 0; - private int mMinHeight = 0; - private int mMaxWidth = Integer.MAX_VALUE; - private int mMaxHeight = Integer.MAX_VALUE; - private boolean mAdjustViewBounds = false; - - // width / height - private float mAspect = -1f; - - public FixedAspectImageView(Context context) { - super(context); - init(context, null, 0); - } - - public FixedAspectImageView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0); - } - - public FixedAspectImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context, attrs, defStyle); - } - - @SuppressWarnings("ResourceType") - private void init(Context context, AttributeSet attrs, int defStyle) { - TypedArray a; - - // Make sure we get value from xml - a = context.obtainStyledAttributes(attrs, MIN_ATTRS, defStyle, 0); - setMinimumWidth(a.getDimensionPixelSize(0, 0)); - setMinimumHeight(a.getDimensionPixelSize(1, 0)); - a.recycle(); - - a = context.obtainStyledAttributes(attrs, ATTRS, defStyle, 0); - setAdjustViewBounds(a.getBoolean(0, false)); - setMaxWidth(a.getDimensionPixelSize(1, Integer.MAX_VALUE)); - setMaxHeight(a.getDimensionPixelSize(2, Integer.MAX_VALUE)); - a.recycle(); - - a = context.obtainStyledAttributes( - attrs, R.styleable.FixedAspectImageView, defStyle, 0); - setAspect(a.getFloat(R.styleable.FixedAspectImageView_aspect, -1f)); - a.recycle(); - } - - @Override - public void setMinimumWidth(int minWidth) { - super.setMinimumWidth(minWidth); - mMinWidth = minWidth; - } - - @Override - public void setMinimumHeight(int minHeight) { - super.setMinimumHeight(minHeight); - mMinHeight = minHeight; - } - - @Override - public void setMaxWidth(int maxWidth) { - super.setMaxWidth(maxWidth); - mMaxWidth = maxWidth; - } - - @Override - public void setMaxHeight(int maxHeight) { - super.setMaxHeight(maxHeight); - mMaxHeight = maxHeight; - } - - @Override - public void setAdjustViewBounds(boolean adjustViewBounds) { - super.setAdjustViewBounds(adjustViewBounds); - mAdjustViewBounds = adjustViewBounds; - } - - public float getAspect() { - return mAspect; - } - - /** - * Enable aspect will set AdjustViewBounds true. - * Any negative float to disable it, - * disable Aspect will not disable AdjustViewBounds. - * - * @param ratio width/height - */ - public void setAspect(float ratio) { - if (ratio > 0) { - mAspect = ratio; - } else { - mAspect = -1f; - } - requestLayout(); - } - - private int resolveAdjustedSize(int desiredSize, int minSize, int maxSize, - int measureSpec) { - int result = desiredSize; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - switch (specMode) { - case MeasureSpec.UNSPECIFIED: - // Parent says we can be as big as we want. Just don't be smaller - // than min size, and don't be larger than max size. - result = MathUtils.clamp(desiredSize, minSize, maxSize); - break; - case MeasureSpec.AT_MOST: - // Parent says we can be as big as we want, up to specSize. - // Don't be larger than specSize, and don't be smaller - // than min size, and don't be larger than max size. - result = Math.min(MathUtils.clamp(desiredSize, minSize, maxSize), specSize); - break; - case MeasureSpec.EXACTLY: - // No choice. Do what we are told. - result = specSize; - break; - } - return result; - } - - private boolean isSizeAcceptable(int size, int minSize, int maxSize, int measureSpec) { - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - switch (specMode) { - case MeasureSpec.UNSPECIFIED: - // Parent says we can be as big as we want. Just don't be smaller - // than min size, and don't be larger than max size. - return size >= minSize && size <= maxSize; - case MeasureSpec.AT_MOST: - // Parent says we can be as big as we want, up to specSize. - // Don't be larger than specSize, and don't be smaller - // than min size, and don't be larger than max size. - return size <= specSize && size >= minSize && size <= maxSize; - case MeasureSpec.EXACTLY: - // No choice. - return size == specSize; - default: - // WTF? Return true to make you happy. (´・ω・`) - return true; - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int w; - int h; - - // Desired aspect ratio of the view's contents (not including padding) - float desiredAspect = 0.0f; - - // We are allowed to change the view's width - boolean resizeWidth = false; - - // We are allowed to change the view's height - boolean resizeHeight = false; - - final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - - Drawable drawable = getDrawable(); - if (drawable == null) { - // If no drawable, its intrinsic size is 0. - w = h = 0; - - // Aspect is forced set. - if (mAspect > 0.0f) { - resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; - resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - desiredAspect = mAspect; - } - } else { - w = drawable.getIntrinsicWidth(); - h = drawable.getIntrinsicHeight(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - - if (mAdjustViewBounds) { - // We are supposed to adjust view bounds to match the aspect - // ratio of our drawable. See if that is possible. - resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; - resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - desiredAspect = (float) w / (float) h; - } else if (mAspect > 0.0f) { - // Aspect is forced set. - resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; - resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - desiredAspect = mAspect; - } - } - - int pLeft = getPaddingLeft(); - int pRight = getPaddingRight(); - int pTop = getPaddingTop(); - int pBottom = getPaddingBottom(); - - int widthSize; - int heightSize; - - if (resizeWidth || resizeHeight) { - // If we get here, it means we want to resize to match the - // drawables aspect ratio, and we have the freedom to change at - // least one dimension. - - // Get the max possible width given our constraints - widthSize = resolveAdjustedSize(w + pLeft + pRight, mMinWidth, mMaxWidth, widthMeasureSpec); - - // Get the max possible height given our constraints - heightSize = resolveAdjustedSize(h + pTop + pBottom, mMinHeight, mMaxHeight, heightMeasureSpec); - - if (desiredAspect != 0.0f) { - // See what our actual aspect ratio is - float actualAspect = (float) (widthSize - pLeft - pRight) / - (heightSize - pTop - pBottom); - - if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { - boolean done = false; - - // Try adjusting width to be proportional to height - if (resizeWidth) { - int newWidth = (int) (desiredAspect * (heightSize - pTop - pBottom)) + - pLeft + pRight; - - // Allow the width to outgrow its original estimate if height is fixed. - //if (!resizeHeight) { - //widthSize = resolveAdjustedSize(newWidth, mMinWidth, mMaxWidth, widthMeasureSpec); - //} - - if (isSizeAcceptable(newWidth, mMinWidth, mMaxWidth, widthMeasureSpec)) { - widthSize = newWidth; - done = true; - } - } - - // Try adjusting height to be proportional to width - if (!done && resizeHeight) { - int newHeight = (int) ((widthSize - pLeft - pRight) / desiredAspect) + - pTop + pBottom; - - // Allow the height to outgrow its original estimate if width is fixed. - if (!resizeWidth) { - heightSize = resolveAdjustedSize(newHeight, mMinHeight, mMaxHeight, - heightMeasureSpec); - } - - if (isSizeAcceptable(newHeight, mMinHeight, mMaxHeight, heightMeasureSpec)) { - heightSize = newHeight; - } - } - } - } - } else { - // We are either don't want to preserve the drawables aspect ratio, - // or we are not allowed to change view dimensions. Just measure in - // the normal way. - w += pLeft + pRight; - h += pTop + pBottom; - - w = Math.max(w, getSuggestedMinimumWidth()); - h = Math.max(h, getSuggestedMinimumHeight()); - - widthSize = View.resolveSizeAndState(w, widthMeasureSpec, 0); - heightSize = View.resolveSizeAndState(h, heightMeasureSpec, 0); - } - - setMeasuredDimension(widthSize, heightSize); - } -}