able to request archive download

This commit is contained in:
Lin, Yi-Li committed 2017-01-29 02:00:02 +09:00
1 parent 9065e7457a
commit b5cae76de5
15 files changed
+407

No files matched your search

@@ -59,6 +59,8 @@ public class EhClient {
public static final int METHOD_GET_PROFILE = 14;
public static final int METHOD_VOTE_COMMENT = 15;
public static final int METHOD_IMAGE_SEARCH = 16;
public static final int METHOD_ARCHIVE_LIST = 17;
public static final int METHOD_DOWNLOAD_ARCHIVE = 18;
private final ThreadPoolExecutor mRequestThreadPool;
private final OkHttpClient mOkHttpClient;
@@ -184,6 +186,10 @@ public class EhClient {
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]);
case METHOD_ARCHIVE_LIST:
return EhEngine.getArchiveList(this, mOkHttpClient, (String) params[0]);
case METHOD_DOWNLOAD_ARCHIVE:
return EhEngine.downloadArchive(this, mOkHttpClient, (Long) params[0], (String) params[1], (String) params[2], (String) params[3]);
default:
return new IllegalStateException("Can't detect method " + mMethod);
}
@@ -31,6 +31,7 @@ import com.hippo.ehviewer.client.data.PreviewSet;
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.ArchiveParser;
import com.hippo.ehviewer.client.parser.FavoritesParser;
import com.hippo.ehviewer.client.parser.ForumsParser;
import com.hippo.ehviewer.client.parser.GalleryApiParser;
@@ -599,6 +600,74 @@ public class EhEngine {
return result;
}
public static Pair<String, Pair<String, String>[]> getArchiveList(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
String url) throws Exception {
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, null != task ? task.getEhConfig() : Settings.getEhConfig()).build();
Call call = okHttpClient.newCall(request);
// Put call
if (null != task) {
task.setCall(call);
}
String body = null;
Headers headers = null;
Pair<String, Pair<String, String>[]> result;
int code = -1;
try {
Response response = call.execute();
code = response.code();
headers = response.headers();
body = response.body().string();
result = ArchiveParser.parse(body);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
return result;
}
public static Void downloadArchive(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
long gid, String token, String or, String res) throws Exception {
if (or == null || or.length() == 0) {
throw new EhException("Invalid form param or: " + or);
}
if (res == null || res.length() == 0) {
throw new EhException("Invalid res: " + res);
}
FormBody.Builder builder = new FormBody.Builder();
builder.add("hathdl_xres", res);
String url = EhUrl.getDownloadArchive(gid, token, or);
Log.d(TAG, url);
Request request = new EhRequestBuilder(url, null != task ? task.getEhConfig() : Settings.getEhConfig())
.post(builder.build())
.build();
Call call = okHttpClient.newCall(request);
// Put call
if (null != task) {
task.setCall(call);
}
String body = null;
Headers headers = null;
int code = -1;
try {
Response response = call.execute();
code = response.code();
headers = response.headers();
body = response.body().string();
throwException(call, code, headers, body, null);
} catch (Exception e) {
throwException(call, code, headers, body, e);
throw e;
}
return null;
}
public static List<GalleryInfo> getWhatsHot(@Nullable EhClient.Task task,
OkHttpClient okHttpClient) throws Exception {
String url = EhUrl.HOST_E;
@@ -108,6 +108,10 @@ public class EhUrl {
return getHost() + "gallerypopups.php?gid=" + gid + "&t=" + token + "&act=addfav";
}
public static String getDownloadArchive(long gid, String token, String or) {
return getHost() + "archiver.php?gid=" + gid + "&token=" + token + "&or=" + or;
}
public static String getTagDefinitionUrl(String tag) {
return "https://ehwiki.org/wiki/" + tag.replace(' ', '_');
}
@@ -27,6 +27,7 @@ public class GalleryDetail extends GalleryInfo {
public String apiKey;
public int torrentCount;
public String torrentUrl;
public String archiveUrl;
public String parent;
public String visible;
public String language;
@@ -50,6 +51,7 @@ public class GalleryDetail extends GalleryInfo {
super.writeToParcel(dest, flags);
dest.writeInt(this.torrentCount);
dest.writeString(this.torrentUrl);
dest.writeString(this.archiveUrl);
dest.writeString(this.parent);
dest.writeString(this.visible);
dest.writeString(this.language);
@@ -71,6 +73,7 @@ public class GalleryDetail extends GalleryInfo {
super(in);
this.torrentCount = in.readInt();
this.torrentUrl = in.readString();
this.archiveUrl = in.readString();
this.parent = in.readString();
this.visible = in.readString();
this.language = in.readString();
@@ -0,0 +1,48 @@
/*
* 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 android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ArchiveParser {
private static final Pattern PATTERN_FORM = Pattern.compile("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a>");
@SuppressWarnings("unchecked")
public static Pair<String, Pair<String, String>[]> parse(String body) {
Matcher m = PATTERN_FORM.matcher(body);
if (!m.find()) {
return new Pair<String, Pair<String, String>[]>("", new Pair[0]);
}
String paramOr = m.group(1);
List<Pair<String, String>> archiveList = new ArrayList<>();
m = PATTERN_ARCHIVE.matcher(body);
while (m.find()) {
String res = ParserUtils.trim(m.group(1));
String name = ParserUtils.trim(m.group(2));
Pair<String, String> item = new Pair<>(res, name);
archiveList.add(item);
}
return new Pair<String, Pair<String, String>[]>(paramOr, archiveList.toArray(new Pair[archiveList.size()]));
}
}
@@ -58,6 +58,7 @@ public class GalleryDetailParser {
private static final Pattern PATTERN_ERROR = Pattern.compile("<div class=\"d\">\n<p>([^<]+)</p>");
private static final Pattern PATTERN_DETAIL = Pattern.compile("var gid = (\\d+);.+?var token = \"([a-f0-9]+)\";.+?var apiuid = ([\\-\\d]+);.+?var apikey = \"([a-f0-9]+)\";", Pattern.DOTALL);
private static final Pattern PATTERN_TORRENT = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download \\( (\\d+) \\)</a>");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Archive Download</a>");
private static final Pattern PATTERN_TAG_GROUP = Pattern.compile("<tr><td[^<>]+>([\\w\\s]+):</td><td>(?:<div[^<>]+><a[^<>]+>[\\w\\s]+</a></div>)+</td></tr>");
private static final Pattern PATTERN_TAG = Pattern.compile("<div[^<>]+><a[^<>]+>([\\w\\s]+)</a></div>");
private static final Pattern PATTERN_COMMENT = Pattern.compile("<div class=\"c3\">Posted on ([^<>]+) by: &nbsp; <a[^<>]+>([^<>]+)</a>.+?<div class=\"c6\"[^>]*>(.+?)</div><div class=\"c[78]\"");
@@ -126,6 +127,13 @@ public class GalleryDetailParser {
gd.torrentUrl = "";
}
matcher = PATTERN_ARCHIVE.matcher(body);
if (matcher.find()) {
gd.archiveUrl = StringUtils.unescapeXml(StringUtils.trim(matcher.group(1)));
} else {
gd.archiveUrl = "";
}
try {
Element gm = JsoupUtils.getElementByClass(d, "gm");
@@ -202,6 +202,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
@Nullable
private TextView mTorrent;
@Nullable
private TextView mArchive;
@Nullable
private TextView mShare;
@Nullable
private TextView mRate;
@@ -250,6 +252,9 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
private Pair<String, String>[] mTorrentList;
private String mArchiveFormParamOr;
private Pair<String, String>[] mArchiveList;
@State
private int mState = STATE_INIT;
@@ -464,18 +469,21 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
mHeart = (TextView) ViewUtils.$$(mHeartGroup, R.id.heart);
mHeartOutline = (TextView) ViewUtils.$$(mHeartGroup, R.id.heart_outline);
mTorrent = (TextView) ViewUtils.$$(mActions, R.id.torrent);
mArchive = (TextView) ViewUtils.$$(mActions, R.id.archive);
mShare = (TextView) ViewUtils.$$(mActions, R.id.share);
mRate = (TextView) ViewUtils.$$(mActions, R.id.rate);
mSimilar = (TextView) ViewUtils.$$(mActions, R.id.similar);
mSearchCover = (TextView) ViewUtils.$$(mActions, R.id.search_cover);
Ripple.addRipple(mHeartGroup, false);
Ripple.addRipple(mTorrent, false);
Ripple.addRipple(mArchive, false);
Ripple.addRipple(mShare, false);
Ripple.addRipple(mRate, false);
Ripple.addRipple(mSimilar, false);
Ripple.addRipple(mSearchCover, false);
mHeartGroup.setOnClickListener(this);
mTorrent.setOnClickListener(this);
mArchive.setOnClickListener(this);
mShare.setOnClickListener(this);
mRate.setOnClickListener(this);
mSimilar.setOnClickListener(this);
@@ -564,6 +572,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
mHeart = null;
mHeartOutline = null;
mTorrent = null;
mArchive = null;
mShare = null;
mRate = null;
mSimilar = null;
@@ -647,6 +656,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
setActionDrawable(mHeartOutline, heartOutline);
Drawable torrent = DrawableManager.getDrawable(context, R.drawable.v_utorrent_primary_x48);
setActionDrawable(mTorrent, torrent);
Drawable archive = DrawableManager.getDrawable(context, R.drawable.v_archive_primary_x48);
setActionDrawable(mArchive, archive);
Drawable share = DrawableManager.getDrawable(context, R.drawable.v_share_primary_x48);
setActionDrawable(mShare, share);
Drawable rate = DrawableManager.getDrawable(context, R.drawable.v_thumb_up_primary_x48);
@@ -1166,6 +1177,21 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
.show();
helper.setDialog(dialog, mGalleryDetail.torrentUrl);
}
} else if (mArchive == v) {
if (mGalleryDetail == null) {
return;
}
if (mGalleryDetail.apiUid < 0) {
showTip(R.string.sign_in_first, LENGTH_SHORT);
return;
}
ArchiveListDialogHelper helper = new ArchiveListDialogHelper();
Dialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.archives)
.setView(R.layout.dialog_archive_list)
.setOnDismissListener(helper)
.show();
helper.setDialog(dialog, mGalleryDetail.archiveUrl);
} else if (mRate == v) {
if (mGalleryDetail == null) {
return;
@@ -1546,6 +1572,146 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
}
}
private class DownloadArchiveListener extends EhCallback<GalleryDetailScene, Void> {
public DownloadArchiveListener(Context context, int stageId, String sceneTag) {
super(context, stageId, sceneTag);
}
@Override
public void onSuccess(Void result) {
showTip(R.string.download_archive_started, LENGTH_SHORT);
}
@Override
public void onFailure(Exception e) {
showTip(R.string.download_archive_failure, LENGTH_SHORT);
}
@Override
public void onCancel() {}
@Override
public boolean isInstance(SceneFragment scene) {
return scene instanceof GalleryDetailScene;
}
}
private class ArchiveListDialogHelper implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener, EhClient.Callback<Pair<String, Pair<String, String>[]>> {
@Nullable
private ProgressView mProgressView;
@Nullable
private TextView mErrorText;
@Nullable
private ListView mListView;
@Nullable
private EhRequest mRequest;
@Nullable
private Dialog mDialog;
public void setDialog(@Nullable Dialog dialog, String url) {
mDialog = dialog;
mProgressView = (ProgressView) ViewUtils.$$(dialog, R.id.progress);
mErrorText = (TextView) ViewUtils.$$(dialog, R.id.text);
mListView = (ListView) ViewUtils.$$(dialog, R.id.list_view);
mListView.setOnItemClickListener(this);
Context context = getContext2();
if (context != null) {
if (mArchiveList == null) {
mErrorText.setVisibility(View.GONE);
mListView.setVisibility(View.GONE);
mRequest = new EhRequest().setMethod(EhClient.METHOD_ARCHIVE_LIST)
.setArgs(url)
.setCallback(this);
EhApplication.getEhClient(context).execute(mRequest);
} else {
bind(mArchiveList);
}
}
}
private void bind(Pair<String, String>[] data) {
if (null == mDialog || null == mProgressView || null == mErrorText || null == mListView) {
return;
}
if (0 == data.length) {
mProgressView.setVisibility(View.GONE);
mErrorText.setVisibility(View.VISIBLE);
mListView.setVisibility(View.GONE);
mErrorText.setText(R.string.no_archives);
} else {
String[] nameArray = new String[data.length];
for (int i = 0, n = data.length; i < n; i++) {
nameArray[i] = data[i].second;
}
mProgressView.setVisibility(View.GONE);
mErrorText.setVisibility(View.GONE);
mListView.setVisibility(View.VISIBLE);
mListView.setAdapter(new ArrayAdapter<>(mDialog.getContext(), R.layout.item_select_dialog, nameArray));
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Context context = getContext2();
MainActivity activity = getActivity2();
if (null != context && null != activity && null != mArchiveList && position < mArchiveList.length) {
String res = mArchiveList[position].first;
EhRequest request = new EhRequest();
request.setMethod(EhClient.METHOD_DOWNLOAD_ARCHIVE);
request.setArgs(mGalleryDetail.gid, mGalleryDetail.token, mArchiveFormParamOr, res);
request.setCallback(new DownloadArchiveListener(context, activity.getStageId(), getTag()));
EhApplication.getEhClient(context).execute(request);
}
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
}
@Override
public void onDismiss(DialogInterface dialog) {
if (mRequest != null) {
mRequest.cancel();
mRequest = null;
}
mDialog = null;
mProgressView = null;
mErrorText = null;
mListView = null;
}
@Override
public void onSuccess(Pair<String, Pair<String, String>[]> result) {
if (mRequest != null) {
mRequest = null;
mArchiveFormParamOr = result.first;
mArchiveList = result.second;
bind(result.second);
}
}
@Override
public void onFailure(Exception e) {
mRequest = null;
Context context = getContext2();
if (null != context && null != mProgressView && null != mErrorText && null != mListView) {
mProgressView.setVisibility(View.GONE);
mErrorText.setVisibility(View.VISIBLE);
mListView.setVisibility(View.GONE);
mErrorText.setText(ExceptionUtils.getReadableString(e));
}
}
@Override
public void onCancel() { mRequest = null; }
}
private class TorrentListDialogHelper implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener, EhClient.Callback<Pair<String, String>[]> {
@@ -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="48dp"
android:width="48dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/colorPrimary"
android:pathData="@string/pd_archive"/>
</vector>
@@ -0,0 +1,44 @@
<?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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/abc_dialog_padding_top_material"
android:paddingBottom="@dimen/abc_dialog_padding_top_material">
<com.hippo.widget.ProgressView
android:id="@+id/progress"
style="@style/ProgressView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
@@ -74,6 +74,15 @@
android:drawablePadding="8dp"
android:textColor="@color/primary_text_light"/>
<TextView
android:id="@+id/archive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/archive"
android:gravity="center_horizontal"
android:drawablePadding="8dp"
android:textColor="@color/primary_text_light"/>
<TextView
android:id="@+id/share"
android:layout_width="wrap_content"
@@ -605,5 +605,10 @@
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="archives">归档</string>
<string name="no_archives">没有归档</string>
<string name="download_archive_started">归档下载开始</string>
<string name="download_archive_failure">无法起始归档下载</string>
<string name="archive">下载归档</string>
</resources>
@@ -605,5 +605,10 @@
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="archives">檔案</string>
<string name="no_archives">沒有檔案</string>
<string name="download_archive_started">檔案下載開始</string>
<string name="download_archive_failure">無法開始檔案下載</string>
<string name="archive">下載檔案</string>
</resources>
@@ -605,5 +605,10 @@
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="archives">檔案</string>
<string name="no_archives">沒有檔案</string>
<string name="download_archive_started">檔案下載開始</string>
<string name="download_archive_failure">無法開始檔案下載</string>
<string name="archive">下載檔案</string>
</resources>
+1
View File
@@ -77,5 +77,6 @@
<string name="pd_sad_panda_nose" translatable="false">M12,17C12.8,17,13.5,17.4,13.5,18S12.8,19,12,19S10.5,18.6,10.5,18S11.2,17,12,17Z</string>
<string name="pd_sad_panda" translatable="false">M21.1,7.8C22.2,7.4,23,6.3,23,5C23,3.3,21.7,2,20,2C18.9,2,17.9,2.6,17.4,3.6L21.1,7.8M6.6,3.6C6.1,2.6,5.1,2,4,2C2.3,2,1,3.3,1,5C1,6.3,1.8,7.4,2.9,7.8L6.6,3.6M10.1,13L8,15.1C7.4,15.7,6.5,15.7,5.9,15.1L5.9,15.1C5.3,14.5,5.3,13.6,5.9,13L8,10.9C8.6,10.3,9.5,10.3,10.1,10.9L10.1,10.9C10.7,11.5,10.7,12.4,10.1,13M16,15.1L13.9,13C13.3,12.4,13.3,11.5,13.9,10.9L13.9,10.9C14.5,10.3,15.4,10.3,16,10.9L18.1,13C18.7,13.6,18.7,14.5,18.1,15.1L18.1,15.1C17.5,15.7,16.6,15.7,16,15.1M12,17C12.8,17,13.5,17.4,13.5,18S12.8,19,12,19S10.5,18.6,10.5,18S11.2,17,12,17Z</string>
<string name="pd_sad_panda_face" translatable="false">M17.4,3.6C15.8,2.6,14,2,12,2S8.2,2.6,6.6,3.6L2.9,7.8C2.3,9.1,2,10.5,2,12C2,17.5,6.5,22,12,22S22,17.5,22,12C22,10.5,21.7,9.1,21.1,7.8L17.4,3.6Z</string>
<string name="pd_archive" translatable="false">M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM10,17l-3.5,-3.5 1.41,-1.41L10,14.17 15.18,9l1.41,1.41L10,17z</string>
</resources>
+5
View File
@@ -655,5 +655,10 @@
<item quantity="one">year</item>
<item quantity="other">years</item>
</plurals>
<string name="archives">Archives</string>
<string name="no_archives">No Archives</string>
<string name="download_archive_started">Archive download started</string>
<string name="download_archive_failure">Failed to download archive</string>
<string name="archive">Archive</string>
</resources>