Add FavoritesScene

This commit is contained in:
seven332 committed 2016-03-02 22:41:35 +08:00
1 parent f402dd92b5
commit 35e48cb79b
32 files changed
+1730 -47

No files matched your search

+1
View File
@@ -77,4 +77,5 @@ dependencies {
compile 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.8.6'
compile 'de.greenrobot:greendao:2.0.0'
compile 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
compile 'org.jsoup:jsoup:1.8.3'
}
@@ -0,0 +1,28 @@
/*
* 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.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
public @interface Implemented {
Class value();
}
@@ -24,9 +24,11 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.gallery.gl.GalleryView;
import com.hippo.ehviewer.gallery.gl.ImageView;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.NumberUtils;
public class Settings {
@@ -117,17 +119,6 @@ public class Settings {
putBoolean(KEY_SHOW_WARNING, value);
}
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
public static String getRecentDownloadLabel() {
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
}
public static void putRecentDownloadLabel(String value) {
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
}
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
@@ -236,6 +227,9 @@ public class Settings {
putBoolean(KEY_VOLUME_PAGE, value);
}
/********************
****** Download
********************/
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
@@ -267,4 +261,81 @@ public class Settings {
putString(KEY_DOWNLOAD_SAVE_QUERY, uri.getEncodedQuery());
putString(KEY_DOWNLOAD_SAVE_FRAGMENT, uri.getEncodedFragment());
}
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
public static String getRecentDownloadLabel() {
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
}
public static void putRecentDownloadLabel(String value) {
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
}
/********************
****** Favorites
********************/
public static final String KEY_FAV_CAT_0 = "fav_cat_0";
public static final String KEY_FAV_CAT_1 = "fav_cat_1";
public static final String KEY_FAV_CAT_2 = "fav_cat_2";
public static final String KEY_FAV_CAT_3 = "fav_cat_3";
public static final String KEY_FAV_CAT_4 = "fav_cat_4";
public static final String KEY_FAV_CAT_5 = "fav_cat_5";
public static final String KEY_FAV_CAT_6 = "fav_cat_6";
public static final String KEY_FAV_CAT_7 = "fav_cat_7";
public static final String KEY_FAV_CAT_8 = "fav_cat_8";
public static final String KEY_FAV_CAT_9 = "fav_cat_9";
public static final String DEFAULT_FAV_CAT_0 = "Favorites 0";
public static final String DEFAULT_FAV_CAT_1 = "Favorites 1";
public static final String DEFAULT_FAV_CAT_2 = "Favorites 2";
public static final String DEFAULT_FAV_CAT_3 = "Favorites 3";
public static final String DEFAULT_FAV_CAT_4 = "Favorites 4";
public static final String DEFAULT_FAV_CAT_5 = "Favorites 5";
public static final String DEFAULT_FAV_CAT_6 = "Favorites 6";
public static final String DEFAULT_FAV_CAT_7 = "Favorites 7";
public static final String DEFAULT_FAV_CAT_8 = "Favorites 8";
public static final String DEFAULT_FAV_CAT_9 = "Favorites 9";
public static String[] getFavCat() {
String[] favCat = new String[10];
favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0);
favCat[1] = sSettingsPre.getString(KEY_FAV_CAT_1, DEFAULT_FAV_CAT_1);
favCat[2] = sSettingsPre.getString(KEY_FAV_CAT_2, DEFAULT_FAV_CAT_2);
favCat[3] = sSettingsPre.getString(KEY_FAV_CAT_3, DEFAULT_FAV_CAT_3);
favCat[4] = sSettingsPre.getString(KEY_FAV_CAT_4, DEFAULT_FAV_CAT_4);
favCat[5] = sSettingsPre.getString(KEY_FAV_CAT_5, DEFAULT_FAV_CAT_5);
favCat[6] = sSettingsPre.getString(KEY_FAV_CAT_6, DEFAULT_FAV_CAT_6);
favCat[7] = sSettingsPre.getString(KEY_FAV_CAT_7, DEFAULT_FAV_CAT_7);
favCat[8] = sSettingsPre.getString(KEY_FAV_CAT_8, DEFAULT_FAV_CAT_8);
favCat[9] = sSettingsPre.getString(KEY_FAV_CAT_9, DEFAULT_FAV_CAT_9);
return favCat;
}
public static void putFavCat(String[] value) {
AssertUtils.assertEquals(10, value.length);
sSettingsPre.edit()
.putString(KEY_FAV_CAT_0, value[0])
.putString(KEY_FAV_CAT_1, value[1])
.putString(KEY_FAV_CAT_2, value[2])
.putString(KEY_FAV_CAT_3, value[3])
.putString(KEY_FAV_CAT_4, value[4])
.putString(KEY_FAV_CAT_5, value[5])
.putString(KEY_FAV_CAT_6, value[6])
.putString(KEY_FAV_CAT_7, value[7])
.putString(KEY_FAV_CAT_8, value[8])
.putString(KEY_FAV_CAT_9, value[9])
.apply();
}
private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat";
private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL;
public static int getRecentFavCat() {
return getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT);
}
public static void putRecentFavCat(int value) {
putInt(KEY_RECENT_FAV_CAT, value);
}
}
@@ -22,6 +22,7 @@ import android.os.AsyncTask;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.exception.CancelledException;
import com.hippo.yorozuya.IntList;
import com.hippo.yorozuya.PriorityThreadFactory;
import com.hippo.yorozuya.SimpleHandler;
@@ -49,6 +50,8 @@ public class EhClient {
public static final int METHOD_GET_RATE_GALLERY = 5;
public static final int METHOD_GET_COMMENT_GALLERY = 6;
public static final int METHOD_GET_GALLERY_TOKEN = 7;
public static final int METHOD_GET_FAVORITES = 8;
public static final int METHOD_MODIFY_FAVORITES = 9;
private final ThreadPoolExecutor mRequestThreadPool;
private final OkHttpClient mOkHttpClient;
@@ -156,6 +159,10 @@ public class EhClient {
return EhEngine.commentGallery(this, mOkHttpClient, (String) params[0], (String) params[1]);
case METHOD_GET_GALLERY_TOKEN:
return EhEngine.getGalleryToken(this, mOkHttpClient, (Integer) params[0], (String) params[1], (Integer) params[2]);
case METHOD_GET_FAVORITES:
return EhEngine.getFavorites(this, mOkHttpClient, (String) params[0], (Boolean) params[1]);
case METHOD_MODIFY_FAVORITES:
return EhEngine.modifyFavorites(this, mOkHttpClient, (String) params[0], (IntList) params[1], (Integer) params[2], (Boolean) params[3]);
default:
return new IllegalStateException("Can't detect method " + mMethod);
}
@@ -20,6 +20,8 @@ import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.GalleryComment;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.data.GalleryInfo;
@@ -27,6 +29,7 @@ import com.hippo.ehviewer.client.data.LargePreviewSet;
import com.hippo.ehviewer.client.exception.CancelledException;
import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.ehviewer.client.parser.FavoritesParser;
import com.hippo.ehviewer.client.parser.GalleryApiParser;
import com.hippo.ehviewer.client.parser.GalleryDetailParser;
import com.hippo.ehviewer.client.parser.GalleryListParser;
@@ -34,6 +37,7 @@ import com.hippo.ehviewer.client.parser.GalleryTokenApiParser;
import com.hippo.ehviewer.client.parser.RateGalleryParser;
import com.hippo.ehviewer.client.parser.SignInParser;
import com.hippo.network.StatusCodeException;
import com.hippo.yorozuya.IntList;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -76,8 +80,12 @@ public class EhEngine {
throw new EhException("Sad Panda");
}
if (e instanceof ParseException && body != null && !body.contains("<")) {
throw new EhException(body);
if (e instanceof ParseException) {
if (body != null && !body.contains("<")){
throw new EhException(body);
} else {
throw new EhException(GetText.getString(R.string.error_parse_error));
}
}
if (code >= 400) {
@@ -349,4 +357,83 @@ public class EhEngine {
throw e;
}
}
public static FavoritesParser.Result getFavorites(EhClient.Task task, OkHttpClient okHttpClient,
String url, boolean callApi) throws Exception {
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, task.getEhConfig()).build();
Call call = okHttpClient.newCall(request);
// Put call
task.setCall(call);
String body = null;
Headers headers = null;
FavoritesParser.Result result;
int code = -1;
try {
Response response = call.execute();
code = response.code();
headers = response.headers();
body = response.body().string();
result = FavoritesParser.parse(body);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
if (callApi && result.galleryInfoList.size() > 0) {
fillGalleryListByApi(task, okHttpClient, result.galleryInfoList);
}
return result;
}
public static FavoritesParser.Result modifyFavorites(EhClient.Task task, OkHttpClient okHttpClient,
String url, IntList gidList, int dstCat, boolean callApi) throws Exception {
String catStr;
if (dstCat == -1) {
catStr = "delete";
} else if (dstCat >= 0 && dstCat <= 9) {
catStr = "fav" + dstCat;
} else {
Log.w(TAG, "Invalid dstCat: " + dstCat);
return null;
}
FormBody.Builder builder = new FormBody.Builder();
builder.add("ddact", catStr);
for (int i = 0, n = gidList.size(); i < n; i++) {
builder.add("modifygids[]", Integer.toString(gidList.get(i)));
}
builder.add("apply", "Apply");
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;
FavoritesParser.Result result;
int code = -1;
try {
Response response = call.execute();
code = response.code();
headers = response.headers();
body = response.body().string();
result = FavoritesParser.parse(body);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
if (callApi && result.galleryInfoList.size() > 0) {
fillGalleryListByApi(task, okHttpClient, result.galleryInfoList);
}
return result;
}
}
@@ -32,6 +32,8 @@ public class EhUrl {
public static final String API_EX = HOST_EX + "api.php";
public static final String URL_FAVORITES = HOST_EX + "favorites.php";
public static String getGalleryDetailUrl(int gid, String token) {
return getGalleryDetailUrl(gid, token, 0, false);
}
@@ -0,0 +1,115 @@
/*
* 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.client.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import com.hippo.ehviewer.client.EhUrl;
import com.hippo.network.UrlBuilder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class FavListUrlBuilder implements Parcelable {
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
public static final int FAV_CAT_ALL = -1;
private int mIndex;
private String mKeyword;
private int mFavCat = FAV_CAT_ALL;
public void setIndex(int index) {
mIndex = index;
}
public void setKeyword(String keyword) {
mKeyword = keyword;
}
public void setFavCat(int favCat) {
mFavCat = favCat;
}
public String getKeyword() {
return mKeyword;
}
public int getFavCat() {
return mFavCat;
}
public static boolean isValidFavCat(int favCat) {
return favCat >= 0 && favCat <= 9;
}
public String build() {
UrlBuilder ub = new UrlBuilder(EhUrl.URL_FAVORITES);
if (isValidFavCat(mFavCat)) {
ub.addQuery("favcat", Integer.toString(mFavCat));
}
if (!TextUtils.isEmpty(mKeyword)) {
try {
ub.addQuery("f_search", URLEncoder.encode(mKeyword, "UTF-8"));
ub.addQuery("f_apply", "Search+Favorites");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Can't URLEncoder.encode " + mKeyword);
}
}
if (mIndex > 0) {
ub.addQuery("page", Integer.toString(mIndex));
}
return ub.build();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.mIndex);
dest.writeString(this.mKeyword);
dest.writeInt(this.mFavCat);
}
public FavListUrlBuilder() {
}
protected FavListUrlBuilder(Parcel in) {
this.mIndex = in.readInt();
this.mKeyword = in.readString();
this.mFavCat = in.readInt();
}
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<FavListUrlBuilder>() {
@Override
public FavListUrlBuilder createFromParcel(Parcel source) {
return new FavListUrlBuilder(source);
}
@Override
public FavListUrlBuilder[] newArray(int size) {
return new FavListUrlBuilder[size];
}
};
}
@@ -0,0 +1,91 @@
/*
* 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.client.parser;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.exception.ParseException;
import com.hippo.util.JsoupUtils;
import com.hippo.yorozuya.AssertUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FavoritesParser {
private static final Pattern LIMIT_PATTERN = Pattern.compile("Currently Used Favorite Slots: ([\\d,]+) / ([\\d,]+)");
public static class Result {
public String[] catArray; // Size 10
public int[] countArray; // Size 10
public int current; // -1 for error
public int limit; // -1 for error
public int pages;
public List<GalleryInfo> galleryInfoList;
}
@SuppressWarnings("ConstantConditions")
public static Result parse(String body) throws Exception {
String[] catArray = new String[10];
int[] countArray = new int[10];
int current;
int limit;
try {
Document d = Jsoup.parse(body);
Element ido = JsoupUtils.getElementByClass(d, "ido");
Elements fps = ido.getElementsByClass("fp");
// Last one is "fp fps"
AssertUtils.assertEqualsEx(11, fps.size());
for (int i = 0; i < 10; i++) {
Element fp = fps.get(i);
countArray[i] = ParserUtils.parseInt(fp.child(0).text());
catArray[i] = ParserUtils.trim(fp.child(2).text());
}
Matcher m = LIMIT_PATTERN.matcher(body);
if (m.find()) {
current = ParserUtils.parseInt(m.group(1));
limit = ParserUtils.parseInt(m.group(2));
} else {
current = -1;
limit = -1;
}
} catch (Exception e) {
e.printStackTrace();
throw new ParseException("Parse favorites error", body);
}
GalleryListParser.Result result = GalleryListParser.parse(body);
Result re = new Result();
re.catArray = catArray;
re.countArray = countArray;
re.current = current;
re.limit = limit;
re.pages = result.pages;
re.galleryInfoList = result.galleryInfos;
return re;
}
}
@@ -38,6 +38,7 @@ import com.hippo.ehviewer.client.EhUrlOpener;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.ehviewer.ui.scene.DownloadScene;
import com.hippo.ehviewer.ui.scene.FavoritesScene;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.ehviewer.ui.scene.LoginScene;
import com.hippo.ehviewer.ui.scene.WarningScene;
@@ -272,7 +273,7 @@ public final class MainActivity extends StageActivity
} else if (id == R.id.nav_whats_hot) {
} else if (id == R.id.nav_favourite) {
startScene(new Announcer(FavoritesScene.class));
} else if (id == R.id.nav_history) {
} else if (id == R.id.nav_download) {
@@ -0,0 +1,36 @@
/*
* 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.annotation;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created in {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)},
* destroyed in {@link Fragment#onDestroyView()}.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface DrawerLifeCircle {
}
@@ -0,0 +1,36 @@
/*
* 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.annotation;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created in {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)},
* destroyed in {@link Fragment#onDestroyView()}.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface ViewLifeCircle {
}
@@ -0,0 +1,35 @@
/*
* 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.annotation;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created in {@link Fragment#onCreate(Bundle)} or {@link Activity#onCreate(Bundle)},
* destroyed in {@link Fragment#onDestroy()} or {@link Activity#onDestroy()}.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface WholeLifeCircle {
}
@@ -51,7 +51,6 @@ import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.dao.DownloadLabelRaw;
import com.hippo.util.ActivityHelper;
import com.hippo.util.DrawableManager;
import com.hippo.view.ViewTransition;
import com.hippo.yorozuya.ViewUtils;
@@ -140,7 +139,7 @@ public class DownloadLabelScene extends ToolbarScene {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.label);
setNavigationIcon(DrawableManager.getDrawable(getContext(), R.drawable.v_arrow_left_dark_x24));
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
// Clear nav checked item
setNavCheckedItem(0);
@@ -69,7 +69,6 @@ import com.hippo.scene.TransitionHelper;
import com.hippo.unifile.UniFile;
import com.hippo.util.ActivityHelper;
import com.hippo.util.ApiHelper;
import com.hippo.util.DrawableManager;
import com.hippo.view.ViewTransition;
import com.hippo.widget.FabLayout;
import com.hippo.widget.LoadImageView;
@@ -226,7 +225,7 @@ public class DownloadScene extends ToolbarScene
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
updateTitle();
setNavigationIcon(DrawableManager.getDrawable(getContext(), R.drawable.v_arrow_left_dark_x24));
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
// Clear nav checked item
setNavCheckedItem(R.id.nav_download);
@@ -286,7 +285,7 @@ public class DownloadScene extends ToolbarScene
@Override
public View onCreateDrawerView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.drawer_download, container, false);
View view = inflater.inflate(R.layout.drawer_list, container, false);
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
toolbar.setTitle(R.string.drawer_download_label_title);
@@ -883,7 +882,7 @@ public class DownloadScene extends ToolbarScene
@Override
public boolean onTransition(Context context, FragmentTransaction transaction,
Fragment exit, Fragment enter) {
if (!(enter instanceof GalleryDetailScene)) {
if (mHolder == null || !(enter instanceof GalleryDetailScene)) {
return false;
}
@@ -25,9 +25,9 @@ import com.hippo.scene.StageActivity;
public abstract class EhCallback<E extends SceneFragment, T> implements EhClient.Callback<T> {
private EhApplication mApplication;
private int mStageId;
private String mSceneTag;
private final EhApplication mApplication;
private final int mStageId;
private final String mSceneTag;
public EhCallback(Context context, int stageId, String sceneTag) {
mApplication = (EhApplication) context.getApplicationContext();
@@ -0,0 +1,916 @@
/*
* 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.scene;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.transition.TransitionInflater;
import android.util.SparseBooleanArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.hippo.annotation.Implemented;
import com.hippo.drawable.DrawerArrowDrawable;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.easyrecyclerview.FastScroller;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhCacheKeyFactory;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.client.EhRequest;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.parser.FavoritesParser;
import com.hippo.ehviewer.ui.annotation.DrawerLifeCircle;
import com.hippo.ehviewer.ui.annotation.ViewLifeCircle;
import com.hippo.ehviewer.ui.annotation.WholeLifeCircle;
import com.hippo.ehviewer.widget.SearchBar;
import com.hippo.ehviewer.widget.SimpleRatingView;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
import com.hippo.scene.SceneFragment;
import com.hippo.scene.StageActivity;
import com.hippo.scene.TransitionHelper;
import com.hippo.util.ApiHelper;
import com.hippo.util.DrawableManager;
import com.hippo.widget.ContentLayout;
import com.hippo.widget.FabLayout;
import com.hippo.widget.LoadImageView;
import com.hippo.widget.SearchBarMover;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.IntList;
import com.hippo.yorozuya.ObjectUtils;
import com.hippo.yorozuya.SimpleHandler;
import java.util.ArrayList;
import java.util.List;
public class FavoritesScene extends BaseScene implements
EasyRecyclerView.OnItemClickListener, EasyRecyclerView.OnItemLongClickListener,
FastScroller.OnDragHandlerListener, SearchBarMover.Helper, SearchBar.Helper,
FabLayout.OnClickFabListener, EasyRecyclerView.CustomChoiceListener, AdapterView.OnItemClickListener {
private static final long ANIMATE_TIME = 300L;
private static final String KEY_URL_BUILDER = "url_builder";
@Nullable
@ViewLifeCircle
private EasyRecyclerView mRecyclerView;
@Nullable
@ViewLifeCircle
private SearchBar mSearchBar;
@Nullable
@ViewLifeCircle
private FabLayout mFabLayout;
@Nullable
@ViewLifeCircle
private FavoriteAdapter mAdapter;
@Nullable
@ViewLifeCircle
private FavoriteHelper mHelper;
@Nullable
@ViewLifeCircle
private SearchBarMover mSearchBarMover;
@Nullable
@ViewLifeCircle
private DrawerArrowDrawable mLeftDrawable;
@Nullable
@DrawerLifeCircle
private ArrayAdapter<String> mDrawerAdapter;
@Nullable
@DrawerLifeCircle
private List<String> mDrawerList;
@Nullable
@WholeLifeCircle
private EhClient mClient;
@Nullable
@WholeLifeCircle
private String[] mFavCat;
@Nullable
@WholeLifeCircle
private FavListUrlBuilder mUrlBuilder;
private boolean mHasFirstRefresh;
private boolean mSearchMode;
// Avoid unnecessary search bar update
private String mOldFavCat;
// Avoid unnecessary search bar update
private String mOldKeyword;
// For modify action
private boolean mEnableModify;
// For modify action
private int mModifyFavCat;
// For modify action
private final IntList mModifyGidList = new IntList();
private final Runnable mReturnSearchBarRunnabel = new Runnable() {
@Override
public void run() {
if (mSearchBarMover != null) {
mSearchBarMover.returnSearchBarPosition();
}
}
};
// TODO Only single instance
@Override
public int getLaunchMode() {
return LAUNCH_MODE_SINGLE_TOP;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mClient = EhApplication.getEhClient(getContext());
mFavCat = Settings.getFavCat();
if (savedInstanceState == null) {
onInit();
} else {
onRestore(savedInstanceState);
}
}
private void onInit() {
mUrlBuilder = new FavListUrlBuilder();
mUrlBuilder.setFavCat(Settings.getRecentFavCat());
}
private void onRestore(Bundle savedInstanceState) {
mUrlBuilder = savedInstanceState.getParcelable(KEY_URL_BUILDER);
if (mUrlBuilder == null) {
mUrlBuilder = new FavListUrlBuilder();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_URL_BUILDER, mUrlBuilder);
}
@Override
public void onDestroy() {
super.onDestroy();
mClient = null;
mFavCat = null;
mUrlBuilder = null;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Resources resources = getResources();
View view = inflater.inflate(R.layout.scene_favorite, container, false);
ContentLayout contentLayout = (ContentLayout) view.findViewById(R.id.content_layout);
mRecyclerView = contentLayout.getRecyclerView();
AssertUtils.assertNotNull(mRecyclerView);
mSearchBar = (SearchBar) view.findViewById(R.id.search_bar);
AssertUtils.assertNotNull(mSearchBar);
mFabLayout = (FabLayout) view.findViewById(R.id.fab_layout);
AssertUtils.assertNotNull(mFabLayout);
mHelper = new FavoriteHelper();
mHelper.setEmptyString(getString(R.string.gallery_list_empty_hit)); // TODO wrong string
contentLayout.setHelper(mHelper);
contentLayout.getFastScroller().setOnDragHandlerListener(this);
mAdapter = new FavoriteAdapter();
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
mRecyclerView.setDrawSelectorOnTop(true);
mRecyclerView.hasFixedSize();
mRecyclerView.setClipToPadding(false);
mRecyclerView.setOnItemClickListener(this);
mRecyclerView.setOnItemLongClickListener(this);
mRecyclerView.setChoiceMode(EasyRecyclerView.CHOICE_MODE_MULTIPLE_CUSTOM);
mRecyclerView.setCustomCheckedListener(this);
int paddingH = resources.getDimensionPixelOffset(R.dimen.list_content_margin_h);
int paddingV = resources.getDimensionPixelOffset(R.dimen.list_content_margin_v);
mRecyclerView.setPadding(paddingV, paddingH, paddingV, paddingH);
mLeftDrawable = new DrawerArrowDrawable(getContext());
mSearchBar.setLeftDrawable(mLeftDrawable);
mSearchBar.setRightDrawable(DrawableManager.getDrawable(getContext(), R.drawable.v_magnify_x24));
mSearchBar.setHelper(this);
mSearchBar.setAllowEmptySearch(false);
updateSearchBar();
mSearchBarMover = new SearchBarMover(this, mSearchBar, mRecyclerView);
mFabLayout.setExpanded(false, false);
mFabLayout.setAutoCancel(false);
mFabLayout.setHidePrimaryFab(true);
mFabLayout.setOnClickFabListener(this);
// Only refresh for the first time
if (!mHasFirstRefresh) {
mHasFirstRefresh = true;
mHelper.firstRefresh();
}
return view;
}
// keyword of mUrlBuilder, fav cat of mUrlBuilder, mFavCat.
// They changed, call it
private void updateSearchBar() {
if (mUrlBuilder == null || mSearchBar == null || mFavCat == null) {
return;
}
// Update title
int favCat = mUrlBuilder.getFavCat();
String favCatName;
if (favCat >= 0 && favCat < 10) {
favCatName = mFavCat[favCat];
} else {
favCatName = getString(R.string.all_favorites);
}
String keyword = mUrlBuilder.getKeyword();
if (TextUtils.isEmpty(keyword)) {
if (!ObjectUtils.equal(favCatName, mOldFavCat)) {
mSearchBar.setTitle(getString(R.string.favorites_title, favCatName));
}
} else {
if (!ObjectUtils.equal(favCatName, mOldFavCat) || !ObjectUtils.equal(keyword, mOldKeyword)) {
mSearchBar.setTitle(getString(R.string.favorites_title_2, favCatName, keyword));
}
}
// Update hint
if (!ObjectUtils.equal(favCatName, mOldFavCat)) {
Drawable searchImage = DrawableManager.getDrawable(getContext(), R.drawable.v_magnify_x24);
SpannableStringBuilder ssb = new SpannableStringBuilder(" ");
ssb.append(getString(R.string.favorites_search_bar_hint, favCatName));
int textSize = (int) (mSearchBar.getEditTextTextSize() * 1.25);
if (searchImage != null) {
searchImage.setBounds(0, 0, textSize, textSize);
ssb.setSpan(new ImageSpan(searchImage), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mSearchBar.setEditTextHint(ssb);
}
mOldFavCat = favCatName;
mOldKeyword = keyword;
// Save recent fav cat
Settings.putRecentFavCat(mUrlBuilder.getFavCat());
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setNavCheckedItem(R.id.nav_whats_hot);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mRecyclerView = null;
mSearchBar = null;
mFabLayout = null;
mAdapter = null;
mHelper = null;
mSearchBarMover = null;
mLeftDrawable = null;
mOldFavCat = null;
mOldKeyword = null;
}
@Override
public View onCreateDrawerView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.drawer_list, container, false);
ListView listView = (ListView) view.findViewById(R.id.list_view);
mDrawerList = new ArrayList<>(11);
mDrawerList.add(getString(R.string.all_favorites));
if (mFavCat != null) {
for (String favCat: mFavCat) {
mDrawerList.add(favCat);
}
}
mDrawerAdapter = new ArrayAdapter<>(getContext(), R.layout.item_simple_list, mDrawerList);
listView.setAdapter(mDrawerAdapter);
listView.setOnItemClickListener(this);
return view;
}
@Override
public void onDestroyDrawerView() {
super.onDestroyDrawerView();
mDrawerAdapter = null;
mDrawerList = null;
}
@Override
public void onBackPressed() {
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
mRecyclerView.outOfCustomChoiceMode();
} else if (mSearchMode) {
exitSearchMode(true);
} else {
finish();
}
}
@Override
@Implemented(FastScroller.OnDragHandlerListener.class)
public void onStartDragHandler() {
}
@Override
@Implemented(FastScroller.OnDragHandlerListener.class)
public void onEndDragHandler() {
if (mSearchBarMover != null) {
mSearchBarMover.returnSearchBarPosition();
}
}
@Override
@Implemented(EasyRecyclerView.OnItemClickListener.class)
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
mRecyclerView.toggleItemChecked(position);
} else if (mHelper != null) {
GalleryInfo gi = mHelper.getDataAt(position);
Bundle args = new Bundle();
args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GALLERY_INFO);
args.putParcelable(GalleryDetailScene.KEY_GALLERY_INFO, gi);
Announcer announcer = new Announcer(GalleryDetailScene.class).setArgs(args);
if (ApiHelper.SUPPORT_TRANSITION) {
FavoriteHolder holder = (FavoriteHolder) mRecyclerView.getChildViewHolder(view);
announcer.setTranHelper(new EnterGalleryDetailTransaction(holder));
}
startScene(announcer);
}
return true;
}
@Override
@Implemented(EasyRecyclerView.OnItemLongClickListener.class)
public boolean onItemLongClick(EasyRecyclerView parent, View view, int position, long id) {
// Can not into
if (mRecyclerView != null && !mSearchMode) {
if (!mRecyclerView.isInCustomChoice()) {
mRecyclerView.intoCustomChoiceMode();
}
mRecyclerView.toggleItemChecked(position);
}
return true;
}
@Override
@Implemented(SearchBarMover.Helper.class)
public boolean isValidView(RecyclerView recyclerView) {
return recyclerView == mRecyclerView;
}
@Override
@Implemented(SearchBarMover.Helper.class)
public RecyclerView getValidRecyclerView() {
return mRecyclerView;
}
@Override
@Implemented(SearchBarMover.Helper.class)
public boolean forceShowSearchBar() {
return false;
}
@Override
@Implemented(SearchBar.Helper.class)
public void onClickTitle() {
// Skip if in search mode
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
return;
}
if (!mSearchMode) {
enterSearchMode(true);
}
}
@Override
@Implemented(SearchBar.Helper.class)
public void onClickLeftIcon() {
// Skip if in search mode
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
return;
}
if (mSearchMode) {
exitSearchMode(true);
} else {
toggleDrawer(Gravity.LEFT);
}
}
@Override
@Implemented(SearchBar.Helper.class)
public void onClickRightIcon() {
// Skip if in search mode
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
return;
}
if (!mSearchMode) {
enterSearchMode(true);
} else {
if (mSearchBar != null) {
mSearchBar.applySearch();
}
}
}
@Override
@Implemented(SearchBar.Helper.class)
public void onSearchEditTextClick() {
}
@Override
@Implemented(SearchBar.Helper.class)
public void onApplySearch(String query) {
// Skip if in search mode
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
return;
}
if (mUrlBuilder == null || mHelper == null) {
return;
}
// Ensure outOfCustomChoiceMode to avoid error
if (mRecyclerView != null) {
mRecyclerView.isInCustomChoice();
}
exitSearchMode(true);
mUrlBuilder.setKeyword(query);
updateSearchBar();
mHelper.refresh();
}
@Override
@Implemented(SearchBar.Helper.class)
public void onSearchEditTextBackPressed() {
onBackPressed();
}
@Override
@Implemented(FabLayout.OnClickFabListener.class)
public void onClickPrimaryFab(FabLayout view, FloatingActionButton fab) {
if (mRecyclerView != null) {
mRecyclerView.outOfCustomChoiceMode();
}
}
@Override
@Implemented(FabLayout.OnClickFabListener.class)
public void onClickSecondaryFab(FabLayout view, FloatingActionButton fab, int position) {
if (mRecyclerView == null || mHelper == null || !mRecyclerView.isInCustomChoice()) {
return;
}
mModifyGidList.clear();
SparseBooleanArray stateArray = mRecyclerView.getCheckedItemPositions();
for (int i = 0, n = stateArray.size(); i < n; i++) {
if (stateArray.valueAt(i)) {
mModifyGidList.add(mHelper.getDataAt(stateArray.keyAt(i)).gid);
}
}
switch (position) {
case 0: { // Delete
new AlertDialog.Builder(getContext())
.setTitle(R.string.delete_favorites_dialog_title)
.setMessage(getString(R.string.delete_favorites_dialog_message, mModifyGidList.size()))
.setPositiveButton(android.R.string.ok, new DeleteDialogHelper())
.show();
break;
}
case 1: { // Move
new AlertDialog.Builder(getContext())
.setTitle(R.string.move_favorites_dialog_title)
.setItems(Settings.getFavCat(), new MoveDialogHelper())
.show();
break;
}
}
}
@Override
@Implemented(EasyRecyclerView.CustomChoiceListener.class)
public void onIntoCustomChoice(EasyRecyclerView view) {
if (mFabLayout != null) {
mFabLayout.setExpanded(true);
}
if (mHelper != null) {
mHelper.setRefreshLayoutEnable(false);
}
// Lock drawer
setDrawerLayoutEnable(false);
}
@Override
@Implemented(EasyRecyclerView.CustomChoiceListener.class)
public void onOutOfCustomChoice(EasyRecyclerView view) {
if (mFabLayout != null) {
mFabLayout.setExpanded(false);
}
if (mHelper != null) {
mHelper.setRefreshLayoutEnable(true);
}
// Unlock drawer
setDrawerLayoutEnable(true);
}
@Override
@Implemented(EasyRecyclerView.CustomChoiceListener.class)
public void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked) {
if (view.getCheckedItemCount() == 0) {
view.outOfCustomChoiceMode();
}
}
@Override
@Implemented(AdapterView.OnItemClickListener.class)
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Skip if in search mode
if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) {
return;
}
if (mUrlBuilder == null || mHelper == null) {
return;
}
// All favorite position is 0, so position - 1 is OK
int newFavCat = position - 1;
// Check is the same
if (mUrlBuilder.getFavCat() == newFavCat ||
(!FavListUrlBuilder.isValidFavCat(mUrlBuilder.getFavCat()) &&
!FavListUrlBuilder.isValidFavCat(newFavCat))) {
return;
}
// Ensure outOfCustomChoiceMode to avoid error
if (mRecyclerView != null) {
mRecyclerView.isInCustomChoice();
}
exitSearchMode(true);
mUrlBuilder.setKeyword(null);
mUrlBuilder.setFavCat(newFavCat);
updateSearchBar();
mHelper.refresh();
closeDrawer(Gravity.RIGHT);
}
private void enterSearchMode(boolean animation) {
if (mSearchMode ||mSearchBar == null || mSearchBarMover == null || mLeftDrawable == null) {
return;
}
mSearchMode = true;
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMover.returnSearchBarPosition(animation);
mLeftDrawable.setArrow(ANIMATE_TIME);
}
private void exitSearchMode(boolean animation) {
if (!mSearchMode || mSearchBar == null || mSearchBarMover == null || mLeftDrawable == null) {
return;
}
mSearchMode = false;
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMover.returnSearchBarPosition();
mLeftDrawable.setMenu(ANIMATE_TIME);
}
private void onGetFavoritesSuccess(FavoritesParser.Result result, int taskId) {
if (mHelper != null && mSearchBarMover != null &&
mHelper.isCurrentTask(taskId)) {
if (mFavCat != null && mDrawerList != null) {
for (int i = 0; i < 10; i++) {
mFavCat[i] = result.catArray[i];
mDrawerList.set(i + 1, result.catArray[i]);
}
if (mDrawerAdapter != null) {
mDrawerAdapter.notifyDataSetChanged();
}
}
updateSearchBar();
mHelper.setPages(taskId, result.pages);
mHelper.onGetPageData(taskId, result.galleryInfoList);
SimpleHandler.getInstance().post(mReturnSearchBarRunnabel);
}
}
private void onGetFavoritesFailure(Exception e, int taskId) {
if (mHelper != null && mSearchBarMover != null &&
mHelper.isCurrentTask(taskId)) {
mHelper.onGetExpection(taskId, e);
}
}
private class DeleteDialogHelper implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) {
return;
}
if (mRecyclerView == null || mHelper == null) {
return;
}
mRecyclerView.outOfCustomChoiceMode();
mEnableModify = true;
mModifyFavCat = -1;
mHelper.justRefresh();
}
}
private class MoveDialogHelper implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mRecyclerView == null || mHelper == null) {
return;
}
mRecyclerView.outOfCustomChoiceMode();
mEnableModify = true;
mModifyFavCat = which;
mHelper.justRefresh();
}
}
private static class EnterGalleryDetailTransaction implements TransitionHelper {
private final FavoriteHolder mHolder;
public EnterGalleryDetailTransaction(FavoriteHolder holder) {
mHolder = holder;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTransition(Context context, FragmentTransaction transaction,
Fragment exit, Fragment enter) {
if (mHolder == null || !(enter instanceof GalleryDetailScene)) {
return false;
}
exit.setSharedElementReturnTransition(
TransitionInflater.from(context).inflateTransition(R.transition.trans_move));
exit.setExitTransition(
TransitionInflater.from(context).inflateTransition(android.R.transition.fade));
enter.setSharedElementEnterTransition(
TransitionInflater.from(context).inflateTransition(R.transition.trans_move));
enter.setEnterTransition(
TransitionInflater.from(context).inflateTransition(android.R.transition.fade));
transaction.addSharedElement(mHolder.thumb, mHolder.thumb.getTransitionName());
transaction.addSharedElement(mHolder.title, mHolder.title.getTransitionName());
transaction.addSharedElement(mHolder.uploader, mHolder.uploader.getTransitionName());
transaction.addSharedElement(mHolder.category, mHolder.category.getTransitionName());
return true;
}
}
private class FavoriteHolder extends RecyclerView.ViewHolder {
private final LoadImageView thumb;
private final TextView title;
private final TextView uploader;
private final SimpleRatingView rating;
private final TextView category;
private final TextView posted;
private final TextView simpleLanguage;
public FavoriteHolder(View itemView) {
super(itemView);
thumb = (LoadImageView) itemView.findViewById(R.id.thumb);
title = (TextView) itemView.findViewById(R.id.title);
uploader = (TextView) itemView.findViewById(R.id.uploader);
rating = (SimpleRatingView) itemView.findViewById(R.id.rating);
category = (TextView) itemView.findViewById(R.id.category);
posted = (TextView) itemView.findViewById(R.id.posted);
simpleLanguage = (TextView) itemView.findViewById(R.id.simple_language);
}
}
private class FavoriteAdapter extends RecyclerView.Adapter<FavoriteHolder> {
@Override
public FavoriteHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new FavoriteHolder(getActivity().getLayoutInflater()
.inflate(R.layout.item_gallery_list, parent, false));
}
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onBindViewHolder(FavoriteHolder holder, int position) {
if (mHelper == null) {
return;
}
GalleryInfo gi = mHelper.getDataAt(position);
holder.thumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb, true);
holder.title.setText(EhUtils.getSuitableTitle(gi));
holder.uploader.setText(gi.uploader);
holder.rating.setRating(gi.rating);
TextView category = holder.category;
String newCategoryText = EhUtils.getCategory(gi.category);
if (!newCategoryText.equals(category.getText())) {
category.setText(newCategoryText);
category.setBackgroundColor(EhUtils.getCategoryColor(gi.category));
}
holder.posted.setText(gi.posted);
holder.simpleLanguage.setText(gi.simpleLanguage);
// Update transition name
if (ApiHelper.SUPPORT_TRANSITION) {
int gid = gi.gid;
holder.thumb.setTransitionName(TransitionNameFactory.getThumbTransitionName(gid));
holder.title.setTransitionName(TransitionNameFactory.getTitleTransitionName(gid));
holder.uploader.setTransitionName(TransitionNameFactory.getUploaderTransitionName(gid));
holder.category.setTransitionName(TransitionNameFactory.getCategoryTransitionName(gid));
}
}
@Override
public int getItemCount() {
return mHelper != null ? mHelper.size() : 0;
}
}
private class FavoriteHelper extends ContentLayout.ContentHelper<GalleryInfo> {
@Override
protected void getPageData(final int taskId, int type, int page) {
if (mUrlBuilder == null || mClient == null) {
return;
}
if (mEnableModify) {
mEnableModify = false;
mUrlBuilder.setIndex(page);
String url = mUrlBuilder.build();
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_MODIFY_FAVORITES);
request.setCallback(new GetFavoritesListener(getContext(),
((StageActivity) getActivity()).getStageId(), getTag(), taskId));
request.setArgs(url, mModifyGidList, mModifyFavCat, Settings.getShowJpnTitle());
mClient.execute(request);
} else {
mUrlBuilder.setIndex(page);
String url = mUrlBuilder.build();
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_GET_FAVORITES);
request.setCallback(new GetFavoritesListener(getContext(),
((StageActivity) getActivity()).getStageId(), getTag(), taskId));
request.setArgs(url, Settings.getShowJpnTitle());
mClient.execute(request);
}
}
@Override
protected void onHideRecyclerView() {
super.onHideRecyclerView();
if (mSearchBarMover != null) {
mSearchBarMover.showSearchBar();
}
}
@Override
protected Context getContext() {
return FavoritesScene.this.getContext();
}
@Override
protected void notifyDataSetChanged() {
// Ensure outOfCustomChoiceMode to avoid error
if (mRecyclerView != null) {
mRecyclerView.outOfCustomChoiceMode();
}
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
@Override
protected void notifyItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter != null) {
mAdapter.notifyItemRangeRemoved(positionStart, itemCount);
}
}
@Override
protected void notifyItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter != null) {
mAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
}
}
private static class GetFavoritesListener extends EhCallback<FavoritesScene, FavoritesParser.Result> {
private final int mTaskId;
public GetFavoritesListener(Context context, int stageId, String sceneTag, int taskId) {
super(context, stageId, sceneTag);
mTaskId = taskId;
}
@Override
public void onSuccess(FavoritesParser.Result result) {
// Put fav cat
Settings.putFavCat(result.catArray);
FavoritesScene scene = getScene();
if (scene != null) {
scene.onGetFavoritesSuccess(result, mTaskId);
}
}
@Override
public void onFailure(Exception e) {
FavoritesScene scene = getScene();
if (scene != null) {
scene.onGetFavoritesFailure(e, mTaskId);
}
}
@Override
public void onCancel() {}
@Override
public boolean isInstance(SceneFragment scene) {
return scene instanceof FavoritesScene;
}
}
}
@@ -54,7 +54,6 @@ import com.hippo.scene.StageActivity;
import com.hippo.text.Html;
import com.hippo.text.URLImageGetter;
import com.hippo.util.ActivityHelper;
import com.hippo.util.DrawableManager;
import com.hippo.util.ReadableTime;
import com.hippo.util.TextUrl;
import com.hippo.view.ViewTransition;
@@ -181,7 +180,7 @@ public final class GalleryCommentsScene extends ToolbarScene
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.gallery_comments);
setNavigationIcon(DrawableManager.getDrawable(getContext(), R.drawable.v_arrow_left_dark_x24));
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
// Clear nav checked item
setNavCheckedItem(0);
@@ -996,7 +996,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onTransition(Context context,
FragmentTransaction transaction, Fragment exit, Fragment enter) {
if (!(enter instanceof GalleryListScene) && !(enter instanceof DownloadScene)) {
if (!(enter instanceof GalleryListScene) && !(enter instanceof DownloadScene) &&
!(enter instanceof FavoritesScene)) {
return false;
}
@@ -39,7 +39,6 @@ import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.rippleold.RippleSalon;
import com.hippo.util.ActivityHelper;
import com.hippo.util.DrawableManager;
import com.hippo.yorozuya.LayoutUtils;
import java.util.ArrayList;
@@ -170,7 +169,7 @@ public final class GalleryInfoScene extends ToolbarScene implements EasyRecycler
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.gallery_info);
setNavigationIcon(DrawableManager.getDrawable(getContext(), R.drawable.v_arrow_left_dark_x24));
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
// Clear nav checked item
setNavCheckedItem(0);
@@ -427,7 +427,7 @@ public final class GalleryListScene extends BaseScene
@Override
public boolean onTransition(Context context, FragmentTransaction transaction,
Fragment exit, Fragment enter) {
if (!(enter instanceof GalleryDetailScene)) {
if (mHolder == null || !(enter instanceof GalleryDetailScene)) {
return false;
}
@@ -48,7 +48,6 @@ import com.hippo.ehviewer.client.exception.EhException;
import com.hippo.scene.SceneFragment;
import com.hippo.scene.StageActivity;
import com.hippo.util.ActivityHelper;
import com.hippo.util.DrawableManager;
import com.hippo.widget.ContentLayout;
import com.hippo.widget.LoadImageView;
import com.hippo.widget.Slider;
@@ -152,7 +151,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.gallery_previews);
setNavigationIcon(DrawableManager.getDrawable(getContext(), R.drawable.v_arrow_left_dark_x24));
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
// Clear nav checked item
setNavCheckedItem(0);
@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -67,7 +68,7 @@ public class SearchBar extends FrameLayout implements View.OnClickListener,
private int mState = STATE_NORMAL;
private Path mPath = new Path();
private final Path mPath = new Path();
private int mWidth;
private int mHeight;
private int mBaseHeight;
@@ -79,7 +80,7 @@ public class SearchBar extends FrameLayout implements View.OnClickListener,
private SearchEditText mEditText;
private ListView mList;
private View mListHeader;
private boolean mHeaderAttached = false;
private boolean mHeaderAttached;
private ViewTransition mViewTransition;
@@ -90,7 +91,9 @@ public class SearchBar extends FrameLayout implements View.OnClickListener,
private Helper mHelper;
private OnStateChangeListener mOnStateChangeListener;
private boolean mInAnimation = false;
private boolean mAllowEmptySearch = true;
private boolean mInAnimation;
public SearchBar(Context context) {
super(context);
@@ -187,6 +190,10 @@ public class SearchBar extends FrameLayout implements View.OnClickListener,
mSuggestionAdapter.notifyDataSetChanged();
}
public void setAllowEmptySearch(boolean allowEmptySearch) {
mAllowEmptySearch = allowEmptySearch;
}
public float getEditTextTextSize() {
return mEditText.getTextSize();
}
@@ -245,6 +252,11 @@ public class SearchBar extends FrameLayout implements View.OnClickListener,
public void applySearch() {
String query = mEditText.getText().toString().trim();
if (!mAllowEmptySearch && TextUtils.isEmpty(query)) {
return;
}
// Put it into db
mSearchDatabase.addQuery(query);
// Callback
@@ -0,0 +1,56 @@
/*
* 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.util;
import android.support.annotation.Nullable;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public final class JsoupUtils {
@Nullable
public static Element getElementByClass(Document doc, String className) {
Elements elements = doc.getElementsByClass(className);
if (elements != null && elements.size() > 0) {
return elements.get(0);
} else {
return null;
}
}
@Nullable
public static Element getElementByClass(Element element, String className) {
Elements elements = element.getElementsByClass(className);
if (elements != null && elements.size() > 0) {
return elements.get(0);
} else {
return null;
}
}
@Nullable
public static Element getElementByTag(Element element, String tagName) {
Elements elements = element.getElementsByTag(tagName);
if (elements != null && elements.size() > 0) {
return elements.get(0);
} else {
return null;
}
}
}
@@ -240,7 +240,7 @@ public class ContentLayout extends FrameLayout {
private String mEmptyString = "No hint";
private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!mRefreshLayout.isRefreshing() && mRefreshLayout.isAlmostBottom() && mEndPage < mPages) {
@@ -251,7 +251,7 @@ public class ContentLayout extends FrameLayout {
}
};
private RefreshLayout.OnRefreshListener mOnRefreshListener = new RefreshLayout.OnRefreshListener() {
private final RefreshLayout.OnRefreshListener mOnRefreshListener = new RefreshLayout.OnRefreshListener() {
@Override
public void onHeaderRefresh() {
if (mStartPage > 0) {
@@ -284,7 +284,7 @@ public class ContentLayout extends FrameLayout {
}
};
private LayoutManagerUtils.OnScrollToPositionListener mOnScrollToPositionListener =
private final LayoutManagerUtils.OnScrollToPositionListener mOnScrollToPositionListener =
new LayoutManagerUtils.OnScrollToPositionListener() {
@Override
public void onScrollToPosition() {
@@ -318,7 +318,6 @@ public class ContentLayout extends FrameLayout {
});
}
/**
* Call {@link #onGetPageData(int, List)} when get data
*
@@ -338,10 +337,11 @@ public class ContentLayout extends FrameLayout {
protected void onScrollToPosition() {
}
protected void onShowProgress() {
protected void onHideRecyclerView() {
}
protected void onShowText() {
public void setRefreshLayoutEnable(boolean enable) {
mRefreshLayout.setEnabled(enable);
}
public void setEnable(boolean enable) {
@@ -565,24 +565,24 @@ public class ContentLayout extends FrameLayout {
mViewTransition.showView(0);
}
private boolean isContentShowning() {
private boolean isContentShowing() {
return mViewTransition.getShownViewIndex() == 0;
}
public void showProgressBar() {
mViewTransition.showView(1, false);
showProgressBar(true);
}
public void showProgressBar(boolean animation) {
if (mViewTransition.showView(1, animation)) {
onShowProgress();
onHideRecyclerView();
}
}
public void showText(CharSequence text) {
mTextView.setText(text);
if (mViewTransition.showView(2)) {
onShowText();
onHideRecyclerView();
}
}
@@ -642,6 +642,14 @@ public class ContentLayout extends FrameLayout {
doRefresh();
}
/**
* Refresh header and refresh
*/
public void justRefresh() {
mRefreshLayout.setHeaderRefreshing(true);
doRefresh();
}
private void cancelCurrentTask() {
mCurrentTaskId = mIdGenerator.nextId();
mRefreshLayout.setHeaderRefreshing(false);
@@ -684,7 +692,7 @@ public class ContentLayout extends FrameLayout {
}
public boolean canGoTo() {
return isContentShowning();
return isContentShowing();
}
/**
@@ -18,6 +18,7 @@ package com.hippo.widget;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
@@ -82,6 +83,9 @@ public class SearchBarMover extends RecyclerView.OnScrollListener {
show = true;
} else {
RecyclerView recyclerView = mHelper.getValidRecyclerView();
if (recyclerView == null) {
return;
}
if (!recyclerView.isShown()) {
show = true;
} else if (recyclerView.computeVerticalScrollOffset() < mSearchBar.getBottom()){
@@ -206,6 +210,7 @@ public class SearchBarMover extends RecyclerView.OnScrollListener {
boolean isValidView(RecyclerView recyclerView);
@Nullable
RecyclerView getValidRecyclerView();
boolean forceShowSearchBar();
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/primary_drawable_dark"
android:pathData="@string/pd_delete"/>
</vector>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:elevation="8dp"
android:saveEnabled="false"/>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
@@ -36,7 +36,7 @@
<View
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_height="8dp"
android:background="@drawable/shadow_bottom"/>
<ListView
@@ -19,7 +19,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/card_white_2dp">
android:background="@drawable/card_background">
<com.hippo.ehviewer.widget.ThumbImageView
android:id="@+id/thumb"
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<com.hippo.ehviewer.widget.FitPaddingLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:content1="@+id/recycler_view"
app:fitTop1="@+id/search_bar"
app:content2="@+id/fast_scroller"
app:fitTop2="@+id/search_bar"
app:content3="@+id/search_layout"
app:fitTop3="@+id/search_bar"
app:once="true">
<com.hippo.widget.ContentLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.hippo.ehviewer.widget.SearchBar
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/list_search_bar_margin"
android:layout_marginRight="@dimen/list_search_bar_margin"
android:layout_marginTop="8dp"/>
<com.hippo.widget.FabLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingRight="@dimen/corner_fab_margin"
android:paddingBottom="@dimen/corner_fab_margin">
<android.support.design.widget.FloatingActionButton
style="@style/Widget.Design.FloatingActionButton.Mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/v_delete_dark_x24"
app:backgroundTint="?attr/colorPrimary"/>
<android.support.design.widget.FloatingActionButton
style="@style/Widget.Design.FloatingActionButton.Mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/v_folder_move_dark_x24"
app:backgroundTint="?attr/colorPrimary"/>
<android.support.design.widget.FloatingActionButton
style="@style/Widget.Design.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/v_close_dark_x24"/>
</com.hippo.widget.FabLayout>
</com.hippo.ehviewer.widget.FitPaddingLayout>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/v_magnify_dark_x24"
android:title="@string/search"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
+9
View File
@@ -303,6 +303,15 @@
<string name="dir_picker">Dir picker</string>
<string name="directory_not_writable">The directory is not writable</string>
<!-- Favorite -->
<string name="all_favorites">All Favorites</string>
<string name="favorites_search_bar_hint">Search %s</string>
<string name="favorites_title">%s</string>
<string name="favorites_title_2">%1$s - %2$s</string>
<string name="delete_favorites_dialog_title">Delete favorites</string>
<string name="delete_favorites_dialog_message">Delete %d items from favorites</string>
<string name="move_favorites_dialog_title">Move favorites</string>
<!-- Progress Scene -->
<string name="error_something_wrong_happened">Something wrong happened</string>