Add image search

This commit is contained in:
seven332 committed 2016-03-29 20:46:47 +08:00
1 parent 311185825e
commit 231e463605
19 files changed
+854 -65

No files matched your search

@@ -113,4 +113,20 @@ public class AppConfig {
public static File createExternalTempFile() {
return FileUtils.createTempFile(getExternalTempDir(), null);
}
@Nullable
public static File getTempDir() {
File dir = sContext.getCacheDir();
File file;
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, TEMP))) {
return file;
} else {
return null;
}
}
@Nullable
public static File createTempFile() {
return FileUtils.createTempFile(getTempDir(), null);
}
}
@@ -43,6 +43,7 @@ import com.hippo.network.StatusCodeException;
import com.hippo.okhttp.CookieDB;
import com.hippo.scene.SceneApplication;
import com.hippo.text.Html;
import com.hippo.util.BitmapUtils;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.SimpleHandler;
@@ -97,6 +98,7 @@ public class EhApplication extends SceneApplication implements Thread.UncaughtEx
SpiderDen.initialize(this);
EhDB.initialize(this);
EhEngine.initialize();
BitmapUtils.initialize(this);
if (EhDB.needMerge()) {
EhDB.mergeOldDB(this);
@@ -25,6 +25,7 @@ import com.hippo.ehviewer.client.exception.CancelledException;
import com.hippo.yorozuya.PriorityThreadFactory;
import com.hippo.yorozuya.SimpleHandler;
import java.io.File;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -57,6 +58,7 @@ public class EhClient {
public static final int METHOD_GET_WHATS_HOT = 13;
public static final int METHOD_GET_PROFILE = 14;
public static final int METHOD_VOTE_COMMENT = 15;
public static final int METHOD_IMAGE_SEARCH = 16;
private final ThreadPoolExecutor mRequestThreadPool;
private final OkHttpClient mOkHttpClient;
@@ -180,6 +182,8 @@ public class EhClient {
return EhEngine.getProfile(this, mOkHttpClient);
case METHOD_VOTE_COMMENT:
return EhEngine.voteComment(this, mOkHttpClient, (Long) params[0], (String) params[1], (Long) params[2], (String) params[3], (Long) params[4], (Integer) params[5]);
case METHOD_IMAGE_SEARCH:
return EhEngine.imageSearch(this, mOkHttpClient, (File) params[0], (Boolean) params[1], (Boolean) params[2], (Boolean) params[3]);
default:
return new IllegalStateException("Can't detect method " + mMethod);
}
@@ -64,6 +64,7 @@ import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
@@ -77,7 +78,8 @@ public class EhEngine {
private static final String SAD_PANDA_TYPE = "image/gif";
private static final String SAD_PANDA_LENGTH = "9615";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg");
public static EhFilter sEhFilter;
@@ -260,7 +262,7 @@ public class EhEngine {
String url = EhUrl.getApiUrl();
Log.d(TAG, url);
Request request = new EhRequestBuilder(url)
.post(RequestBody.create(JSON, json.toString()))
.post(RequestBody.create(MEDIA_TYPE_JSON, json.toString()))
.build();
Call call = okHttpClient.newCall(request);
@@ -342,7 +344,7 @@ public class EhEngine {
json.put("gid", gid);
json.put("token", token);
json.put("rating", (int) Math.ceil(rating * 2));
final RequestBody requestBody = RequestBody.create(JSON, json.toString());
final RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, json.toString());
String url = EhUrl.getApiUrl();
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig())
@@ -404,7 +406,7 @@ public class EhEngine {
.put("method", "gtoken")
.put("pagelist", new JSONArray().put(
new JSONArray().put(gid).put(gtoken).put(page + 1)));
final RequestBody requestBody = RequestBody.create(JSON, json.toString());
final RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, json.toString());
String url = EhUrl.getApiUrl();
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig())
@@ -682,7 +684,7 @@ public class EhEngine {
json.put("token", token);
json.put("comment_id", commentId);
json.put("comment_vote", commentVote);
final RequestBody requestBody = RequestBody.create(JSON, json.toString());
final RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, json.toString());
String url = EhUrl.getApiUrl();
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig())
@@ -707,4 +709,94 @@ public class EhEngine {
throw e;
}
}
/**
* @param image Must be jpeg
*/
public static GalleryListParser.Result imageSearch(EhClient.Task task, OkHttpClient okHttpClient,
File image, boolean uss, boolean osc, boolean se) throws Exception {
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
builder.addPart(
Headers.of("Content-Disposition", "form-data; name=\"sfile\"; filename=\"a.jpg\""),
RequestBody.create(MEDIA_TYPE_JPEG, image)
);
if (uss) {
builder.addPart(
Headers.of("Content-Disposition", "form-data; name=\"fs_similar\""),
RequestBody.create(null, "on")
);
}
if (osc) {
builder.addPart(
Headers.of("Content-Disposition", "form-data; name=\"fs_covers\""),
RequestBody.create(null, "on")
);
}
if (se) {
builder.addPart(
Headers.of("Content-Disposition", "form-data; name=\"fs_exp\""),
RequestBody.create(null, "on")
);
}
builder.addPart(
Headers.of("Content-Disposition", "form-data; name=\"f_sfile\""),
RequestBody.create(null, "File Search")
);
String url = EhUrl.getImageSearchUrl();
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig())
.post(builder.build())
.build();
Call call = okHttpClient.newCall(request);
// Put call
task.setCall(call);
String body = null;
Headers headers = null;
GalleryListParser.Result result;
int code = -1;
try {
Response response = call.execute();
Log.d(TAG, "" + response.request().url().toString());
code = response.code();
headers = response.headers();
body = response.body().string();
result = GalleryListParser.parse(body);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
// Filter title and uploader
List<GalleryInfo> list = result.galleryInfoList;
for (int i = 0, n = list.size(); i < n; i++) {
GalleryInfo info = list.get(i);
if (!sEhFilter.filterTitle(info) || !sEhFilter.filterUploader(info)) {
list.remove(i);
i--;
n--;
}
}
if (list.size() > 0 && (Settings.getShowJpnTitle() || sEhFilter.needCallApi())) {
// Fill by api
fillGalleryListByApi(task, okHttpClient, list);
// Filter tag
for (int i = 0, n = list.size(); i < n; i++) {
GalleryInfo info = list.get(i);
if (!sEhFilter.filterTag(info) || !sEhFilter.filterTagNamespace(info)) {
list.remove(i);
i--;
n--;
}
}
}
return result;
}
}
@@ -16,6 +16,8 @@
package com.hippo.ehviewer.client;
import android.support.annotation.NonNull;
import com.hippo.ehviewer.Settings;
import com.hippo.network.UrlBuilder;
@@ -99,4 +101,15 @@ public class EhUrl {
public static String getTagDefinitionUrl(String tag) {
return "https://ehwiki.org/wiki/" + tag.replace(' ', '_');
}
@NonNull
public static String getImageSearchUrl() {
switch (Settings.getGallerySite()) {
default:
case SITE_G:
return "http://ul.e-hentai.org/image_lookup.php";
case SITE_EX:
return "http://ul.exhentai.org/image_lookup.php";
}
}
}
@@ -62,6 +62,11 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
private int mAdvanceSearch = -1;
private int mMinRating = -1;
private String mImagePath;
private boolean mUseSimilarityScan;
private boolean mOnlySearchCovers;
private boolean mShowExpunged;
/**
* Make this ListUrlBuilder point to homepage
*/
@@ -72,6 +77,10 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
mKeyword = null;
mAdvanceSearch = -1;
mMinRating = -1;
mImagePath = null;
mUseSimilarityScan = false;
mOnlySearchCovers = false;
mShowExpunged = false;
}
@Override
@@ -132,6 +141,38 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
mMinRating = minRating;
}
public String getImagePath() {
return mImagePath;
}
public void setImagePath(String imagePath) {
mImagePath = imagePath;
}
public boolean isUseSimilarityScan() {
return mUseSimilarityScan;
}
public void setUseSimilarityScan(boolean useSimilarityScan) {
mUseSimilarityScan = useSimilarityScan;
}
public boolean isOnlySearchCovers() {
return mOnlySearchCovers;
}
public void setOnlySearchCovers(boolean onlySearchCovers) {
mOnlySearchCovers = onlySearchCovers;
}
public boolean isShowExpunged() {
return mShowExpunged;
}
public void setShowExpunged(boolean showExpunged) {
mShowExpunged = showExpunged;
}
/**
* Make them the same
* @param lub The template
@@ -143,6 +184,10 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
mKeyword = lub.mKeyword;
mAdvanceSearch = lub.mAdvanceSearch;
mMinRating = lub.mMinRating;
mImagePath = lub.mImagePath;
mUseSimilarityScan = lub.mUseSimilarityScan;
mOnlySearchCovers = lub.mOnlySearchCovers;
mShowExpunged = lub.mShowExpunged;
}
public void set(QuickSearch q) {
@@ -413,6 +458,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
}
return sb.toString();
}
case MODE_IMAGE_SEARCH:
return EhUrl.getImageSearchUrl();
}
}
@@ -429,22 +476,30 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
dest.writeString(this.mKeyword);
dest.writeInt(this.mAdvanceSearch);
dest.writeInt(this.mMinRating);
dest.writeString(this.mImagePath);
dest.writeByte(mUseSimilarityScan ? (byte) 1 : (byte) 0);
dest.writeByte(mOnlySearchCovers ? (byte) 1 : (byte) 0);
dest.writeByte(mShowExpunged ? (byte) 1 : (byte) 0);
}
public ListUrlBuilder() {}
public ListUrlBuilder() {
}
@SuppressWarnings("WrongConstant")
protected ListUrlBuilder(Parcel in) {
mMode = in.readInt();
mPageIndex = in.readInt();
mCategory = in.readInt();
mKeyword = in.readString();
mAdvanceSearch = in.readInt();
mMinRating = in.readInt();
this.mMode = in.readInt();
this.mPageIndex = in.readInt();
this.mCategory = in.readInt();
this.mKeyword = in.readString();
this.mAdvanceSearch = in.readInt();
this.mMinRating = in.readInt();
this.mImagePath = in.readString();
this.mUseSimilarityScan = in.readByte() != 0;
this.mOnlySearchCovers = in.readByte() != 0;
this.mShowExpunged = in.readByte() != 0;
}
public static final Parcelable.Creator<ListUrlBuilder> CREATOR = new Parcelable.Creator<ListUrlBuilder>() {
public static final Creator<ListUrlBuilder> CREATOR = new Creator<ListUrlBuilder>() {
@Override
public ListUrlBuilder createFromParcel(Parcel source) {
return new ListUrlBuilder(source);
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
@@ -68,6 +69,7 @@ import com.hippo.ehviewer.client.EhUrl;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.ehviewer.client.parser.GalleryListParser;
import com.hippo.ehviewer.dao.QuickSearch;
import com.hippo.ehviewer.ui.CommonOperations;
@@ -90,6 +92,7 @@ import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.SimpleAnimatorListener;
import com.hippo.yorozuya.ViewUtils;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -106,6 +109,8 @@ public final class GalleryListScene extends BaseScene
private static final int BACK_PRESSED_INTERVAL = 2000;
public final static int REQUEST_CODE_SELECT_IMAGE = 0;
public final static String KEY_ACTION = "action";
public final static String ACTION_HOMEPAGE = "action_homepage";
public final static String ACTION_WHATS_HOT = "action_whats_hot";
@@ -1111,7 +1116,12 @@ public final class GalleryListScene extends BaseScene
startScene(new Announcer(GalleryDetailScene.class).setArgs(args));
return;
} else {
mSearchLayout.formatListUrlBuilder(mUrlBuilder, query);
try {
mSearchLayout.formatListUrlBuilder(mUrlBuilder, query);
} catch (EhException e) {
showTip(e.getMessage(), LENGTH_LONG);
return;
}
}
} else {
mUrlBuilder.reset();
@@ -1177,6 +1187,15 @@ public final class GalleryListScene extends BaseScene
}
}
@Override
public void onSelectImage() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
getString(R.string.select_image)), REQUEST_CODE_SELECT_IMAGE);
}
// SearchBarMover.Helper
@Override
public boolean isValidView(RecyclerView recyclerView) {
@@ -1207,6 +1226,17 @@ public final class GalleryListScene extends BaseScene
scene.startScene(new Announcer(GalleryListScene.class).setArgs(args));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_CODE_SELECT_IMAGE == requestCode) {
if (Activity.RESULT_OK == resultCode && null != mSearchLayout && null != data) {
mSearchLayout.setImageUri(data.getData());
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private class GalleryListAdapter extends GalleryAdapter {
public GalleryListAdapter(@NonNull LayoutInflater inflater,
@@ -1243,6 +1273,14 @@ public final class GalleryListScene extends BaseScene
activity.getStageId(), getTag(), taskId));
request.setArgs();
mClient.execute(request);
} else if (ListUrlBuilder.MODE_IMAGE_SEARCH == mUrlBuilder.getMode()) {
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_IMAGE_SEARCH);
request.setCallback(new GetGalleryListListener(getContext(),
activity.getStageId(), getTag(), taskId));
request.setArgs(new File(mUrlBuilder.getImagePath()), mUrlBuilder.isUseSimilarityScan(),
mUrlBuilder.isOnlySearchCovers(), mUrlBuilder.isShowExpunged());
mClient.execute(request);
} else {
String url = mUrlBuilder.build();
EhRequest request = new EhRequest();
@@ -0,0 +1,238 @@
/*
* 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.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.AbsSavedState;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
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.io.UniFileInputStreamPipe;
import com.hippo.unifile.UniFile;
import com.hippo.util.BitmapUtils;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.ViewUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public final class ImageSearchLayout extends LinearLayout implements View.OnClickListener {
private static final String TAG = ImageSearchLayout.class.getSimpleName();
private ImageView mPreview;
private View mSelectImage;
private CheckBox mSearchUSS;
private CheckBox mSearchOSC;
private CheckBox mSearchSE;
private Helper mHelper;
@Nullable
private String mImagePath;
public ImageSearchLayout(Context context) {
super(context);
init(context);
}
public ImageSearchLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ImageSearchLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@SuppressWarnings("deprecation")
public void init(Context context) {
setOrientation(VERTICAL);
setDividerDrawable(context.getResources().getDrawable(R.drawable.spacer_keyline));
setShowDividers(SHOW_DIVIDER_MIDDLE);
LayoutInflater.from(context).inflate(R.layout.widget_image_search, this);
mPreview = (ImageView) ViewUtils.$$(this, R.id.preview);
mSelectImage = ViewUtils.$$(this, R.id.select_image);
mSearchUSS = (CheckBox) ViewUtils.$$(this, R.id.search_uss);
mSearchOSC = (CheckBox) ViewUtils.$$(this, R.id.search_osc);
mSearchSE = (CheckBox) ViewUtils.$$(this, R.id.search_se);
mSelectImage.setOnClickListener(this);
}
public void setHelper(Helper helper) {
mHelper = helper;
}
@Override
public void onClick(View v) {
if (v == mSelectImage) {
if (null != mHelper) {
mHelper.onSelectImage();
}
}
}
public void setImageUri(@Nullable Uri imageUri) {
if (null == imageUri) {
return;
}
Context context = getContext();
UniFile file = UniFile.fromUri(context, imageUri);
try {
int maxSize = context.getResources().getDimensionPixelOffset(R.dimen.image_search_max_size);
Bitmap bitmap = BitmapUtils.decodeStream(new UniFileInputStreamPipe(file), maxSize, maxSize);
if (null == bitmap) {
return;
}
File temp = AppConfig.createTempFile();
if (null == temp) {
return;
}
// TODO ehentai image search is bad when I'm writing this line.
// Re-compress image will make image search failed.
OutputStream os = null;
try {
os = new FileOutputStream(temp);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
mImagePath = temp.getPath();
mPreview.setImageBitmap(bitmap);
mPreview.setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(os);
}
} catch (OutOfMemoryError e) {
Log.e(TAG, "Out of memory");
}
}
private void setImagePath(@Nullable String imagePath) {
if (null == imagePath) {
return;
}
InputStream is = null;
try {
is = new FileInputStream(imagePath);
Bitmap bitmap = BitmapFactory.decodeStream(is);
if (null == bitmap) {
return;
}
mImagePath = imagePath;
mPreview.setImageBitmap(bitmap);
mPreview.setVisibility(VISIBLE);
} catch (FileNotFoundException e) {
// Ignore
} finally {
IOUtils.closeQuietly(is);
}
}
public void formatListUrlBuilder(ListUrlBuilder builder) throws EhException {
if (null == mImagePath) {
throw new EhException(getContext().getString(R.string.select_image_first));
}
builder.setImagePath(mImagePath);
builder.setUseSimilarityScan(mSearchUSS.isChecked());
builder.setOnlySearchCovers(mSearchOSC.isChecked());
builder.setShowExpunged(mSearchSE.isChecked());
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.imagePath = mImagePath;
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setImagePath(ss.imagePath);
}
private static class SavedState extends AbsSavedState {
String imagePath;
/**
* Constructor called from {@link ImageSearchLayout#onSaveInstanceState()}
*/
protected SavedState(Parcelable superState) {
super(superState);
}
/**
* Constructor called from {@link #CREATOR}
*/
protected SavedState(Parcel source) {
super(source);
imagePath = source.readString();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(imagePath);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
public interface Helper {
void onSelectImage();
}
}
@@ -16,8 +16,10 @@
package com.hippo.ehviewer.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.IntDef;
@@ -39,6 +41,7 @@ import android.widget.TextView;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.rippleold.RippleSalon;
import com.hippo.widget.RadioGridGroup;
import com.hippo.yorozuya.ViewUtils;
@@ -47,9 +50,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnCheckedChangeListener,
View.OnClickListener {
View.OnClickListener, ImageSearchLayout.Helper {
@IntDef({SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE, SEARCH_MODE_SPECIFY})
@IntDef({SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE})
@Retention(RetentionPolicy.SOURCE)
private @interface SearchMode {}
@@ -59,22 +62,19 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
public static final int SEARCH_MODE_NORMAL = 0;
public static final int SEARCH_MODE_IMAGE = 1;
public static final int SEARCH_MODE_SPECIFY = 2;
private static final int ITEM_TYPE_NORMAL = 0;
private static final int ITEM_TYPE_NORMAL_ADVANCE = 1;
private static final int ITEM_TYPE_IMAGE = 2;
private static final int ITEM_TYPE_SPECIFY = 3;
private static final int ITEM_TYPE_ACTION = 4;
private static final int ITEM_TYPE_ACTION = 3;
private static final int[] SEARCH_ITEM_COUNT_ARRAY = {
3, 2, 2
3, 2
};
private static final int[][] SEARCH_ITEM_TYPE = {
{ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION}, // SEARCH_MODE_NORMAL
{ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION}, // SEARCH_MODE_IMAGE
{ITEM_TYPE_SPECIFY, ITEM_TYPE_ACTION} // SEARCH_MODE_SPECIFY
};
private LayoutInflater mInflater;
@@ -86,13 +86,15 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
private CategoryTable mCategoryTable;
private RadioGridGroup mNormalSearchMode;
private ImageView mNormalSearchModeHelp;
private View mSearchTagHelp;
private SwitchCompat mEnableAdvanceSwitch;
private View mAdvanceView;
private AdvanceSearchTable mTableAdvanceSearch;
private ImageSearchLayout mImageView;
private View mActionView;
private TextView mAction;
private LinearLayoutManager mLayoutManager;
private SearchAdapter mAdapter;
@@ -109,26 +111,23 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
init(context);
}
@SuppressLint("InflateParams")
private void init(Context context) {
Resources resources = context.getResources();
mInflater = LayoutInflater.from(context);
mLayoutManager = new LinearLayoutManager(context);
mAdapter = new SearchAdapter();
//mAnimator = new SearchItemAnimator(this);
setLayoutManager(mLayoutManager);
setAdapter(mAdapter);
setHasFixedSize(true);
setClipToPadding(false);
//setItemAnimator(mAnimator);
int paddingH = resources.getDimensionPixelOffset(R.dimen.list_content_margin_h);
int paddingV = resources.getDimensionPixelOffset(R.dimen.list_content_margin_v);
setPadding(paddingV, paddingH, paddingV, paddingH);
FrameLayout frameLayout = new FrameLayout(context);
// Create normal view
View normalView = mInflater.inflate(R.layout.search_normal, frameLayout, false);
View normalView = mInflater.inflate(R.layout.search_normal, null);
mNormalView = normalView;
mCategoryTable = (CategoryTable) normalView.findViewById(R.id.search_category_table);
mNormalSearchMode = (RadioGridGroup) normalView.findViewById(R.id.normal_search_mode);
@@ -140,11 +139,17 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
mEnableAdvanceSwitch.setSwitchPadding(resources.getDimensionPixelSize(R.dimen.switch_padding));
// Create advance view
mAdvanceView = mInflater.inflate(R.layout.search_advance, frameLayout, false);
mAdvanceView = mInflater.inflate(R.layout.search_advance, null);
mTableAdvanceSearch = (AdvanceSearchTable) mAdvanceView.findViewById(R.id.search_advance_search_table);
// Create image view
mImageView = (ImageSearchLayout) mInflater.inflate(R.layout.search_image, null);
mImageView.setHelper(this);
// Create action view
mActionView = new View(context);// TODO mInflater.inflate(R.layout.search_action, frameLayout, false);
mActionView = mInflater.inflate(R.layout.search_action, null);
mAction = (TextView) mActionView.findViewById(R.id.action);
mAction.setOnClickListener(this);
}
public void setHelper(Helper helper) {
@@ -155,12 +160,24 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
mLayoutManager.scrollToPositionWithOffset(0, 0);
}
public void setImageUri(Uri imageUri) {
mImageView.setImageUri(imageUri);
}
@Override
public void onSelectImage() {
if (mHelper != null) {
mHelper.onSelectImage();
}
}
@Override
protected void dispatchSaveInstanceState(@NonNull SparseArray<Parcelable> container) {
super.dispatchSaveInstanceState(container);
mNormalView.saveHierarchyState(container);
mAdvanceView.saveHierarchyState(container);
mImageView.saveHierarchyState(container);
mActionView.saveHierarchyState(container);
}
@@ -170,6 +187,7 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
mNormalView.restoreHierarchyState(container);
mAdvanceView.restoreHierarchyState(container);
mImageView.restoreHierarchyState(container);
mActionView.restoreHierarchyState(container);
}
@@ -214,29 +232,35 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
return R.id.search_specify_gallery == mNormalSearchMode.getCheckedRadioButtonId();
}
// TODO image search
public void formatListUrlBuilder(ListUrlBuilder urlBuilder, String query) {
public void formatListUrlBuilder(ListUrlBuilder urlBuilder, String query) throws EhException {
urlBuilder.reset();
int nsMode = mNormalSearchMode.getCheckedRadioButtonId();
switch (nsMode) {
default:
case R.id.search_normal_search:
urlBuilder.setMode(ListUrlBuilder.MODE_NORMAL);
switch (mSearchMode) {
case SEARCH_MODE_NORMAL:
int nsMode = mNormalSearchMode.getCheckedRadioButtonId();
switch (nsMode) {
default:
case R.id.search_normal_search:
urlBuilder.setMode(ListUrlBuilder.MODE_NORMAL);
break;
case R.id.search_specify_uploader:
urlBuilder.setMode(ListUrlBuilder.MODE_UPLOADER);
break;
case R.id.search_specify_tag:
urlBuilder.setMode(ListUrlBuilder.MODE_TAG);
break;
}
urlBuilder.setKeyword(query);
urlBuilder.setCategory(mCategoryTable.getCategory());
if (mEnableAdvance) {
urlBuilder.setAdvanceSearch(mTableAdvanceSearch.getAdvanceSearch());
urlBuilder.setMinRating(mTableAdvanceSearch.getMinRating());
}
break;
case R.id.search_specify_uploader:
urlBuilder.setMode(ListUrlBuilder.MODE_UPLOADER);
case SEARCH_MODE_IMAGE:
urlBuilder.setMode(ListUrlBuilder.MODE_IMAGE_SEARCH);
mImageView.formatListUrlBuilder(urlBuilder);
break;
case R.id.search_specify_tag:
urlBuilder.setMode(ListUrlBuilder.MODE_TAG);
break;
}
urlBuilder.setKeyword(query);
urlBuilder.setCategory(mCategoryTable.getCategory());
if (mEnableAdvance) {
urlBuilder.setAdvanceSearch(mTableAdvanceSearch.getAdvanceSearch());
urlBuilder.setMinRating(mTableAdvanceSearch.getMinRating());
}
}
@@ -263,7 +287,7 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
int oldItemCount = mAdapter.getItemCount();
mSearchMode++;
if (mSearchMode > SEARCH_MODE_SPECIFY) {
if (mSearchMode > SEARCH_MODE_IMAGE) {
mSearchMode = SEARCH_MODE_NORMAL;
}
@@ -272,6 +296,19 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
mAdapter.notifyItemRangeRemoved(0, oldItemCount - 1);
mAdapter.notifyItemRangeInserted(0, newItemCount - 1);
// Update action text
int resId;
switch (mSearchMode) {
default:
case SEARCH_MODE_NORMAL:
resId = R.string.image_search;
break;
case SEARCH_MODE_IMAGE:
resId = R.string.keyword_search;
break;
}
mAction.setText(resId);
if (mHelper != null) {
mHelper.onChangeSearchMode();
}
@@ -283,6 +320,8 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
new AlertDialog.Builder(getContext())
.setMessage(R.string.search_tip)
.show();
} else if (mAction == v) {
toggleSearchMode();
}
}
@@ -321,6 +360,17 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
mActionView.setLayoutParams(new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
int resId;
switch (mSearchMode) {
default:
case SEARCH_MODE_NORMAL:
resId = R.string.image_search;
break;
case SEARCH_MODE_IMAGE:
resId = R.string.keyword_search;
break;
}
mAction.setText(resId);
view = mActionView;
} else {
view = mInflater.inflate(R.layout.search_category, parent, false);
@@ -337,6 +387,11 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
ViewUtils.removeFromParent(mAdvanceView);
content.addView(mAdvanceView);
break;
case ITEM_TYPE_IMAGE:
title.setText(R.string.search_image);
ViewUtils.removeFromParent(mImageView);
content.addView(mImageView);
break;
}
}
@@ -351,5 +406,6 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC
public interface Helper {
void onChangeSearchMode();
void onSelectImage();
}
}
@@ -0,0 +1,129 @@
/*
* 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.util;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.io.InputStreamPipe;
import java.io.IOException;
public class BitmapUtils {
public static Context sContext;
public static void initialize(Context context) {
sContext = context.getApplicationContext();
}
public static long availableMemory() {
final Runtime runtime = Runtime.getRuntime();
final long used = runtime.totalMemory() - runtime.freeMemory();
final ActivityManager activityManager = (ActivityManager) sContext.
getSystemService(Context.ACTIVITY_SERVICE);
final long total = activityManager.getMemoryClass() * 1024 * 1024;
return total - used;
}
public static Bitmap decodeStream(@NonNull InputStreamPipe isp, int maxWidth, int maxHeight,
int pixels, boolean checkMemory, boolean justCalc, int[] sampleSize) {
try {
isp.obtain();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(isp.open(), null, options);
isp.close();
int width = options.outWidth;
int height = options.outHeight;
if (width <= 0 || height <= 0) {
if (sampleSize != null && sampleSize.length >= 1) {
sampleSize[0] = -1;
}
return null;
}
int scaleW = 1;
int scaleH = 1;
int scaleP = 1;
int scaleM = 1;
if (maxWidth > 0 && width > maxWidth) {
scaleW = MathUtils.ceilDivide(width, maxWidth);
}
if (maxHeight > 0 && height > maxHeight) {
scaleH = MathUtils.ceilDivide(height, maxHeight);
}
if (pixels > 0 && width * height > pixels) {
scaleP = (int) Math.ceil(Math.sqrt(width * height / (float) pixels));
}
if (checkMemory) {
long m = availableMemory() - 5 * 1024 * 1024; // Leave 5m
if (m < 0) {
if (sampleSize != null && sampleSize.length >= 1) {
sampleSize[0] = -1;
}
return null;
}
if (width * height * 3 > m) {
scaleM = (int) Math.ceil(Math.sqrt(width * height * 3 / (float) m));
}
}
options.inSampleSize = MathUtils.nextPowerOf2(MathUtils.max(scaleW, scaleH, scaleP, scaleM, 1));
if (sampleSize != null && sampleSize.length >= 1) {
sampleSize[0] = options.inSampleSize;
}
options.inJustDecodeBounds = false;
if (justCalc) {
return null;
} else {
try {
return BitmapFactory.decodeStream(isp.open(), null, options);
} catch (OutOfMemoryError e) {
if (sampleSize != null && sampleSize.length >= 1) {
sampleSize[0] = -1;
}
return null;
}
}
} catch (IOException e) {
if (sampleSize != null && sampleSize.length >= 1) {
sampleSize[0] = -1;
}
return null;
} finally {
isp.close();
isp.release();
}
}
/**
* @return null or the bitmap
*/
public static Bitmap decodeStream(@NonNull InputStreamPipe isp, int maxWidth, int maxHeight) {
return decodeStream(isp, maxWidth, maxHeight, -1, false, false, null);
}
}