Add source
This commit is contained in:
1 parent
6004602ab3
commit
fe8021a512
29 files changed
+3313
-8
No files matched your search
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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;
|
||||
|
||||
import com.hippo.util.AssertUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DetailUrlParser {
|
||||
|
||||
public int gid;
|
||||
public String token;
|
||||
|
||||
public void parser(String url) throws Exception {
|
||||
AssertUtils.assertNotNull("Url is null when parse detail url", url);
|
||||
|
||||
Pattern p = Pattern.compile("/(\\d+)/(\\w+)");
|
||||
Matcher m = p.matcher(url);
|
||||
if (m.find()) {
|
||||
gid = Integer.parseInt(m.group(1));
|
||||
token = m.group(2);
|
||||
} else {
|
||||
throw new EhException("Can not parse detail url");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.network.EhHttpHelper;
|
||||
import com.hippo.util.Utils;
|
||||
|
||||
public class EhClient {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = EhClient.class.getSimpleName();
|
||||
|
||||
public static final int SOURCE_G = 0x0;
|
||||
public static final int SOURCE_EX = 0x1;
|
||||
public static final int SOURCE_LOFI = 0x2;
|
||||
|
||||
public static final String API_G = "http://g.e-hentai.org/api.php";
|
||||
public static final String API_EX = "http://exhentai.org/api.php";
|
||||
public static final long APIUID = 1363542;
|
||||
public static final String APIKEY = "f4b5407ab1727b9d08d7";
|
||||
|
||||
private static final String LOGIN_URL =
|
||||
"http://forums.e-hentai.org/index.php?act=Login&CODE=01";
|
||||
private static final String FORUMS_URL = "http://forums.e-hentai.org/index.php";
|
||||
|
||||
public static final String HEADER_G = "http://g.e-hentai.org/";
|
||||
public static final String HEADER_EX = "http://exhentai.org/";
|
||||
public static final String HEADER_LOFI = "http://lofi.e-hentai.org/";
|
||||
|
||||
public static final String API_EHVIEWER = "http://www.ehviewer.com/API";
|
||||
|
||||
/**
|
||||
* Get gallery list
|
||||
*
|
||||
* @param source the source, one of {@link #SOURCE_G}, {@link #SOURCE_EX} and
|
||||
* {@link #SOURCE_LOFI}
|
||||
* @param url the url to get gallery list
|
||||
* @return a array with {@code GalleryInfo[]} gallery info array and
|
||||
* {@code int} pages number
|
||||
*/
|
||||
public Object[] doGetGalleryList(int source, String url) throws Exception {
|
||||
EhHttpHelper ehh = new EhHttpHelper();
|
||||
String body = ehh.get(url);
|
||||
|
||||
ListParser parser = new ListParser();
|
||||
parser.parse(body, source);
|
||||
return new Object[]{parser.giArray, parser.pageNum};
|
||||
}
|
||||
|
||||
private void doBgJob(BgJobHelper bjh) {
|
||||
Utils.execute(false, new AsyncTask<Object, Void, Object[]>() {
|
||||
@Override
|
||||
protected Object[] doInBackground(Object... params) {
|
||||
BgJobHelper bjh = (BgJobHelper) params[0];
|
||||
return new Object[]{bjh.doInBackground(), bjh};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object[] resultPackage) {
|
||||
Object result = resultPackage[0];
|
||||
BgJobHelper bjh = (BgJobHelper) resultPackage[1];
|
||||
bjh.onPostExecute(result);
|
||||
}
|
||||
}, bjh);
|
||||
}
|
||||
|
||||
private static interface BgJobHelper {
|
||||
public Object doInBackground();
|
||||
|
||||
public void onPostExecute(Object result);
|
||||
}
|
||||
|
||||
public interface OnGetGalleryListListener {
|
||||
public void onSuccess(GalleryInfo[] glArray, int pageNum);
|
||||
|
||||
public void onFailure(Exception e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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;
|
||||
|
||||
public class EhException extends Exception {
|
||||
|
||||
public EhException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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;
|
||||
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.data.LofiGalleryInfo;
|
||||
import com.hippo.ehviewer.util.EhUtils;
|
||||
import com.hippo.util.AssertUtils;
|
||||
import com.hippo.util.NotEqualsException;
|
||||
import com.hippo.util.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ListParser {
|
||||
private static final String PARSE_MESSAGE = "Gallery list parser error";
|
||||
|
||||
private static final int DEFAULT_LIST_SIZE = 25;
|
||||
|
||||
public static final int CURRENT_PAGE_IS_LAST = -1;
|
||||
|
||||
/**
|
||||
* If NOT FOUND, pageNum is 0.<br>
|
||||
* For lofi, we can not get pages number,
|
||||
* so pageNum is {@link java.lang.Integer#MAX_VALUE} when it is not the last page,
|
||||
* pageNum is {@link #CURRENT_PAGE_IS_LAST} when it is the last page
|
||||
*/
|
||||
public int pageNum;
|
||||
|
||||
public GalleryInfo[] giArray;
|
||||
|
||||
private void parse(String body) throws EhException {
|
||||
Pattern p;
|
||||
Matcher m;
|
||||
|
||||
// Get page number
|
||||
p = Pattern.compile("<a[^<>]+>([\\d]+)</a></td><td[^<>]+>(?:<a[^<>]+>)?&");
|
||||
m = p.matcher(body);
|
||||
if (m.find()) {
|
||||
pageNum = Integer.parseInt(m.group(1));
|
||||
} else if (body.contains("No hits found</p>")) {
|
||||
pageNum = 0;
|
||||
} else {
|
||||
// Can not get page number
|
||||
throw new EhException(PARSE_MESSAGE);
|
||||
}
|
||||
|
||||
// Get gallery
|
||||
List<GalleryInfo> giList = new ArrayList<>(DEFAULT_LIST_SIZE);
|
||||
|
||||
p = Pattern.compile("<td class=\"itdc\">(?:<a.+?>)?<img.+?alt=\"(.+?)\".+?/>(?:</a>)?</td>" // category
|
||||
+ "<td.+?>(.+?)</td>" // posted
|
||||
+ "<td.+?><div.+?><div.+?height:(\\d+)px; width:(\\d+)px\">"
|
||||
+ "(?:<img.+?src=\"(.+?)\".+?alt=\"(.+?)\" style.+?/>"
|
||||
+ "|init~([^<>\"~]+~[^<>\"~]+)~([^<>]+))" // thumb and title
|
||||
+ "</div>"
|
||||
+ ".+?"
|
||||
+ "<div class=\"it5\"><a href=\"([^<>\"]+)\"[^<>]+>(.+?)</a></div>" // url and title
|
||||
+ ".+?"
|
||||
+ "<div class=\"ir it4r\" style=\"([^<>\"]+)\">" // rating
|
||||
+ ".+?"
|
||||
+ "<td class=\"itu\"><div><a.+?>(.+?)</a>"); // uploader
|
||||
m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
GalleryInfo gi = new GalleryInfo();
|
||||
|
||||
gi.category = EhUtils.getCategory(m.group(1));
|
||||
gi.posted = m.group(2);
|
||||
gi.thumbHeight = Integer.parseInt(m.group(3));
|
||||
gi.thumbWidth = Integer.parseInt(m.group(4));
|
||||
|
||||
if (m.group(5) == null) {
|
||||
gi.thumb = Utils.unescapeXml("http://"
|
||||
+ m.group(7).replace('~', '/'));
|
||||
gi.title = Utils.unescapeXml(m.group(8));
|
||||
} else {
|
||||
gi.thumb = Utils.unescapeXml(m.group(5));
|
||||
gi.title = Utils.unescapeXml(m.group(6));
|
||||
}
|
||||
|
||||
Pattern pattern = Pattern
|
||||
.compile("/(\\d+)/(\\w+)");
|
||||
Matcher matcher = pattern.matcher(m.group(9));
|
||||
if (matcher.find()) {
|
||||
gi.gid = Integer.parseInt(matcher.group(1));
|
||||
gi.token = matcher.group(2);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
gi.rating = Utils.parseFloatSafely(getRate(m.group(11)), Float.NaN);
|
||||
gi.uploader = m.group(12);
|
||||
gi.generateSLang();
|
||||
|
||||
giList.add(gi);
|
||||
}
|
||||
|
||||
if (giList.size() == 0) {
|
||||
throw new EhException(PARSE_MESSAGE);
|
||||
}
|
||||
|
||||
giArray = giList.toArray(new GalleryInfo[giList.size()]);
|
||||
}
|
||||
|
||||
private void parseLofi(String body) throws Exception {
|
||||
Pattern p;
|
||||
Matcher m;
|
||||
|
||||
// Get gallery
|
||||
List<LofiGalleryInfo> lgiList = new ArrayList<>(DEFAULT_LIST_SIZE);
|
||||
p = Pattern.compile("<td class=\"ii\"><a href=\"(.+?)\">" // detail url
|
||||
+ "<img src=\"(.+?)\".+?/>" // thumb url
|
||||
+ ".+?<a class=\"b\" href=\".+?\">(.+?)</a>" // title
|
||||
+ ".+?<td class=\"ik ip\">Posted:</td><td class=\"ip\">(.+?)</td>" // Posted and uploader
|
||||
+ "</tr><tr><td class=\"ik\">Category:</td><td>(.+?)</td>" // Category
|
||||
+ "</tr><tr><td class=\"ik\">Tags:</td><td>(.+?)</td>" // Tags
|
||||
+ "</tr><tr><td class=\"ik\">Rating:</td><td class=\"ir\">(.+?)</td>"); // rating
|
||||
m = p.matcher(body);
|
||||
DetailUrlParser dup = new DetailUrlParser();
|
||||
String[] pau = new String[2];
|
||||
while (m.find()) {
|
||||
LofiGalleryInfo lgi = new LofiGalleryInfo();
|
||||
|
||||
dup.parser(m.group(1));
|
||||
lgi.gid = dup.gid;
|
||||
lgi.token = dup.token;
|
||||
|
||||
lgi.thumb = Utils.unescapeXml(m.group(2));
|
||||
lgi.title = Utils.unescapeXml(m.group(3));
|
||||
|
||||
getPostedAndUploader(m.group(4), pau);
|
||||
lgi.posted = pau[0];
|
||||
lgi.uploader = pau[1];
|
||||
|
||||
lgi.category = EhUtils.getCategory(m.group(5));
|
||||
String tags = m.group(6);
|
||||
if (tags.equals("-"))
|
||||
lgi.lofiTags = new String[0];
|
||||
else
|
||||
lgi.lofiTags = tags.split(", ");
|
||||
String rating = m.group(7);
|
||||
if (rating.equals("-"))
|
||||
lgi.rating = Float.NaN;
|
||||
else
|
||||
lgi.rating = getStartNum(rating);
|
||||
lgi.generateSLang();
|
||||
|
||||
lgiList.add(lgi);
|
||||
}
|
||||
|
||||
if (lgiList.size() == 0) {
|
||||
if (body.contains("No hits found</div>")) {
|
||||
pageNum = 0;
|
||||
} else if (body.contains("No more hits found</div>")) {
|
||||
throw new EhException("Index is out of range");
|
||||
} else {
|
||||
throw new EhException(PARSE_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
if (!body.contains("Next Page ></a>")) {
|
||||
pageNum = CURRENT_PAGE_IS_LAST;
|
||||
} else {
|
||||
pageNum = Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parse(String body, int source) throws Exception {
|
||||
AssertUtils.assertNotNull("Body is null when parse gallery list", body);
|
||||
|
||||
switch (source) {
|
||||
default:
|
||||
case EhClient.SOURCE_G:
|
||||
case EhClient.SOURCE_EX: {
|
||||
parse(body);
|
||||
break;
|
||||
}
|
||||
case EhClient.SOURCE_LOFI: {
|
||||
parseLofi(body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getRate(String rawRate) {
|
||||
Pattern p = Pattern.compile("\\d+px");
|
||||
Matcher m = p.matcher(rawRate);
|
||||
int num1;
|
||||
int num2;
|
||||
int rate = 5;
|
||||
String re;
|
||||
if (m.find())
|
||||
num1 = Integer.parseInt(m.group().replace("px", ""));
|
||||
else
|
||||
return null;
|
||||
if (m.find())
|
||||
num2 = Integer.parseInt(m.group().replace("px", ""));
|
||||
else
|
||||
return null;
|
||||
rate = rate - num1 / 16;
|
||||
if (num2 == 21) {
|
||||
rate--;
|
||||
re = Integer.toString(rate);
|
||||
re = re + ".5";
|
||||
} else
|
||||
re = Integer.toString(rate);
|
||||
return re;
|
||||
}
|
||||
|
||||
private static final String PAU_SPACER = " by ";
|
||||
|
||||
private void getPostedAndUploader(String raw, String[] pau)
|
||||
throws NotEqualsException {
|
||||
int index = raw.indexOf(PAU_SPACER);
|
||||
AssertUtils.assertEquals("Can not parse posted and uploader", index, -1);
|
||||
pau[0] = raw.substring(0, index);
|
||||
pau[1] = raw.substring(index + PAU_SPACER.length());
|
||||
}
|
||||
|
||||
private int getStartNum(String str) {
|
||||
int startNum = 0;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
if (str.charAt(i) == '*')
|
||||
startNum++;
|
||||
}
|
||||
return startNum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GalleryInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* ISO 639-1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static final String S_LANG_JA = "JA";
|
||||
public static final String S_LANG_EN = "EN";
|
||||
public static final String S_LANG_ZH = "ZH";
|
||||
public static final String S_LANG_NL = "NL";
|
||||
public static final String S_LANG_FR = "FR";
|
||||
public static final String S_LANG_DE = "DE";
|
||||
public static final String S_LANG_HU = "HU";
|
||||
public static final String S_LANG_IT = "IT";
|
||||
public static final String S_LANG_KO = "KO";
|
||||
public static final String S_LANG_PL = "PL";
|
||||
public static final String S_LANG_PT = "PT";
|
||||
public static final String S_LANG_RU = "RU";
|
||||
public static final String S_LANG_ES = "ES";
|
||||
public static final String S_LANG_TH = "TH";
|
||||
public static final String S_LANG_VI = "VI";
|
||||
|
||||
public static final String[] S_LANGS = {
|
||||
S_LANG_EN,
|
||||
S_LANG_ZH,
|
||||
S_LANG_ES,
|
||||
S_LANG_KO,
|
||||
S_LANG_RU,
|
||||
S_LANG_FR,
|
||||
S_LANG_PT,
|
||||
S_LANG_TH,
|
||||
S_LANG_DE,
|
||||
S_LANG_IT,
|
||||
S_LANG_VI,
|
||||
S_LANG_PL,
|
||||
S_LANG_HU,
|
||||
S_LANG_NL,
|
||||
};
|
||||
|
||||
public static final String[] S_LANG_PATTERNS = {
|
||||
"[(\\[]eng(?:lish)?[)\\]]",
|
||||
"[((\\[]chinese[))\\]]|[汉漢]化|中[国國][语語]|中文|[(\\[]CN[)\\]]",
|
||||
"[(\\[]spanish[)\\]]|[(\\[]Español[)\\]]",
|
||||
"[(\\[]korean?[)\\]]",
|
||||
"[(\\[]rus(?:sian)?[)\\]]",
|
||||
"[(\\[]fr(?:ench)?[)\\]]",
|
||||
"[(\\[]portuguese",
|
||||
"[(\\[]thai(?: ภาษาไทย)?[)\\]]",
|
||||
"[(\\[]german[)\\]]",
|
||||
"[(\\[]italiano?[)\\]]",
|
||||
"[(\\[]vietnamese(?: Tiếng Việt)?[)\\]]",
|
||||
"[(\\[]polish[)\\]]",
|
||||
"[(\\[]hun(?:garian)?[)\\]]",
|
||||
"[(\\[]dutch[)\\]]",
|
||||
};
|
||||
|
||||
public int gid;
|
||||
public String token;
|
||||
public String title;
|
||||
public String posted;
|
||||
public int category;
|
||||
public String thumb;
|
||||
public String uploader;
|
||||
public float rating;
|
||||
/**
|
||||
* Will not be recond
|
||||
*/
|
||||
public int thumbWidth;
|
||||
/**
|
||||
* Will not be recond
|
||||
*/
|
||||
public int thumbHeight;
|
||||
/**
|
||||
* language get from title
|
||||
*/
|
||||
public String simpleLanguage;
|
||||
|
||||
public static final Creator<GalleryInfo> CREATOR =
|
||||
new Creator<GalleryInfo>() {
|
||||
@Override
|
||||
public GalleryInfo createFromParcel(Parcel source) {
|
||||
GalleryInfo p = new GalleryInfo();
|
||||
p.gid = source.readInt();
|
||||
p.token = source.readString();
|
||||
p.title = source.readString();
|
||||
p.posted = source.readString();
|
||||
p.category = source.readInt();
|
||||
p.thumb = source.readString();
|
||||
p.uploader = source.readString();
|
||||
p.rating = source.readFloat();
|
||||
p.simpleLanguage = source.readString();
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryInfo[] newArray(int size) {
|
||||
return new GalleryInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(gid);
|
||||
dest.writeString(token);
|
||||
dest.writeString(title);
|
||||
dest.writeString(posted);
|
||||
dest.writeInt(category);
|
||||
dest.writeString(thumb);
|
||||
dest.writeString(uploader);
|
||||
dest.writeFloat(rating);
|
||||
dest.writeString(simpleLanguage);
|
||||
}
|
||||
|
||||
public GalleryInfo() {
|
||||
// Empty
|
||||
}
|
||||
|
||||
public GalleryInfo(GalleryInfo gi) {
|
||||
gid = gi.gid;
|
||||
token = gi.token;
|
||||
title = gi.title;
|
||||
posted = gi.posted;
|
||||
category = gi.category;
|
||||
thumb = gi.thumb;
|
||||
uploader = gi.uploader;
|
||||
rating = gi.rating;
|
||||
simpleLanguage = gi.simpleLanguage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof GalleryInfo && ((GalleryInfo) o).gid == gid)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void generateSLang() {
|
||||
for (int i = 0; i < S_LANGS.length; i++) {
|
||||
if (Pattern.compile(S_LANG_PATTERNS[i], Pattern.CASE_INSENSITIVE).matcher(title).find()) {
|
||||
simpleLanguage = S_LANGS[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
simpleLanguage = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.data;
|
||||
|
||||
public class ListUrlBuilder {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LofiGalleryInfo extends GalleryInfo {
|
||||
|
||||
public @NotNull String[] lofiTags;
|
||||
|
||||
public static final Creator<LofiGalleryInfo> CREATOR =
|
||||
new Creator<LofiGalleryInfo>() {
|
||||
@Override
|
||||
public LofiGalleryInfo createFromParcel(Parcel source) {
|
||||
int length;
|
||||
|
||||
LofiGalleryInfo p = new LofiGalleryInfo();
|
||||
p.gid = source.readInt();
|
||||
p.token = source.readString();
|
||||
p.title = source.readString();
|
||||
p.posted = source.readString();
|
||||
p.category = source.readInt();
|
||||
p.thumb = source.readString();
|
||||
p.uploader = source.readString();
|
||||
p.rating = source.readFloat();
|
||||
p.simpleLanguage = source.readString();
|
||||
|
||||
length = source.readInt();
|
||||
p.lofiTags = new String[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
p.lofiTags[i] = source.readString();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LofiGalleryInfo[] newArray(int size) {
|
||||
return new LofiGalleryInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
public LofiGalleryInfo() {}
|
||||
|
||||
public LofiGalleryInfo(GalleryInfo galleryInfo) {
|
||||
gid = galleryInfo.gid;
|
||||
token = galleryInfo.token;
|
||||
title = galleryInfo.title;
|
||||
posted = galleryInfo.posted;
|
||||
category = galleryInfo.category;
|
||||
thumb = galleryInfo.thumb;
|
||||
uploader = galleryInfo.uploader;
|
||||
rating = galleryInfo.rating;
|
||||
simpleLanguage = galleryInfo.simpleLanguage;
|
||||
lofiTags = new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
|
||||
int length = lofiTags == null ? 0 : lofiTags.length;
|
||||
dest.writeInt(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
dest.writeString(lofiTags[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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.network;
|
||||
|
||||
import com.hippo.network.HttpHelper;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class EhHttpHelper extends HttpHelper {
|
||||
|
||||
@Override
|
||||
protected String getCookie(URL url) {
|
||||
return super.getCookie(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storeCookie(URL url, String value) {
|
||||
super.storeCookie(url, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBeforeConnect(HttpURLConnection conn) {
|
||||
super.onBeforeConnect(conn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.util;
|
||||
|
||||
public final class EhUtils {
|
||||
|
||||
public static final String EH_DOWNLOAD_FILENAME = ".ehviewer";
|
||||
|
||||
public static final int NONE = -1; // Use it for homepage
|
||||
public static final int MISC = 0x1;
|
||||
public static final int DOUJINSHI = 0x2;
|
||||
public static final int MANGA = 0x4;
|
||||
public static final int ARTIST_CG = 0x8;
|
||||
public static final int GAME_CG = 0x10;
|
||||
public static final int IMAGE_SET = 0x20;
|
||||
public static final int COSPLAY = 0x40;
|
||||
public static final int ASIAN_PORN = 0x80;
|
||||
public static final int NON_H = 0x100;
|
||||
public static final int WESTERN = 0x200;
|
||||
public static final int UNKNOWN = 0x400;
|
||||
|
||||
private static final int[] CATEGORY_VALUES = {
|
||||
MISC,
|
||||
DOUJINSHI,
|
||||
MANGA,
|
||||
ARTIST_CG,
|
||||
GAME_CG,
|
||||
IMAGE_SET,
|
||||
COSPLAY,
|
||||
ASIAN_PORN,
|
||||
NON_H,
|
||||
WESTERN,
|
||||
UNKNOWN };
|
||||
|
||||
private static final String[][] CATEGORY_STRINGS = {
|
||||
new String[] { "misc" },
|
||||
new String[] { "doujinshi" },
|
||||
new String[] { "manga" },
|
||||
new String[] { "artistcg", "Artist CG Sets" },
|
||||
new String[] { "gamecg", "Game CG Sets" },
|
||||
new String[] { "imageset", "Image Sets" },
|
||||
new String[] { "cosplay" },
|
||||
new String[] { "asianporn", "Asian Porn" },
|
||||
new String[] { "non-h" },
|
||||
new String[] { "western" },
|
||||
new String[] { "unknown" }
|
||||
};
|
||||
|
||||
public static int getCategory(String type) {
|
||||
int i;
|
||||
for (i = 0; i < CATEGORY_STRINGS.length - 1; i++) {
|
||||
for (String str : CATEGORY_STRINGS[i])
|
||||
if (str.equalsIgnoreCase(type))
|
||||
return CATEGORY_VALUES[i];
|
||||
}
|
||||
|
||||
return CATEGORY_VALUES[i];
|
||||
}
|
||||
|
||||
public static String getCategory(int type) {
|
||||
int i;
|
||||
for (i = 0; i < CATEGORY_VALUES.length - 1; i++) {
|
||||
if (CATEGORY_VALUES[i] == type)
|
||||
break;
|
||||
}
|
||||
return CATEGORY_STRINGS[i][0];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,899 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.network;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.hippo.util.FastByteArrayOutputStream;
|
||||
import com.hippo.util.Utils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* It is not thread safe
|
||||
*/
|
||||
public class HttpHelper {
|
||||
private static final String TAG = HttpHelper.class.getSimpleName();
|
||||
|
||||
private static final int MAX_RETRY = 3;
|
||||
private static final int MAX_REDIRECTS = 3;
|
||||
private static final int CONNECT_TIMEOUT = 5000;
|
||||
private static final int READ_TIMEOUT = 5000;
|
||||
|
||||
public static final String DEFAULT_USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) " +
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) " +
|
||||
"Chrome/39.0.2171.95 Safari/537.36";
|
||||
public static final String USER_AGENT =
|
||||
System.getProperty("http.agent", DEFAULT_USER_AGENT);
|
||||
|
||||
private static final int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
private static final String DEFAULT_CHARSET = "utf-8";
|
||||
private static final String CHARSET_KEY = "charset=";
|
||||
|
||||
private static final CookieManager sCookieManager = CookieManager.getInstance();
|
||||
|
||||
private int mResponseCode = -1;
|
||||
|
||||
public void reset() {
|
||||
mResponseCode = -1;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return mResponseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cookie for the url
|
||||
* @param url the URL
|
||||
* @return the cookie for the URL
|
||||
*/
|
||||
protected String getCookie(URL url) {
|
||||
return sCookieManager.getCookie(url.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store cookie for the url
|
||||
* @param url the URL
|
||||
* @param value the cookie for the URL
|
||||
*/
|
||||
protected void storeCookie(URL url, String value) {
|
||||
sCookieManager.setCookie(url.toString(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare before connecting
|
||||
* @param conn the connection
|
||||
*/
|
||||
protected void onBeforeConnect(HttpURLConnection conn) {
|
||||
conn.setInstanceFollowRedirects(true);
|
||||
conn.setRequestProperty("User-Agent", USER_AGENT);
|
||||
conn.setConnectTimeout(CONNECT_TIMEOUT);
|
||||
conn.setReadTimeout(READ_TIMEOUT);
|
||||
}
|
||||
|
||||
private Object doRequst(RequestHelper rh) throws Exception {
|
||||
URL url;
|
||||
HttpURLConnection conn = null;
|
||||
int redirectionCount = 0;
|
||||
try {
|
||||
url = rh.getUrl();
|
||||
while (redirectionCount++ < MAX_REDIRECTS) {
|
||||
Log.d(TAG, "Request: " + url.toString());
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
// Prepare before connecting
|
||||
onBeforeConnect(conn);
|
||||
// Set cookie
|
||||
String cookie = getCookie(url);
|
||||
if (cookie != null) {
|
||||
conn.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
// Do custom staff
|
||||
rh.onBeforeConnect(conn);
|
||||
|
||||
conn.connect();
|
||||
// Store cookie
|
||||
List<String> cookieList = conn.getHeaderFields().get("Set-Cookie");
|
||||
if (cookieList != null) {
|
||||
for (String cookieTemp : cookieList) {
|
||||
if (cookieTemp != null) {
|
||||
storeCookie(url, cookieTemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
final int responseCode = conn.getResponseCode();
|
||||
mResponseCode = responseCode;
|
||||
Log.d(TAG, "Response code: " + responseCode);
|
||||
switch (responseCode) {
|
||||
case HttpURLConnection.HTTP_MOVED_PERM:
|
||||
case HttpURLConnection.HTTP_MOVED_TEMP:
|
||||
case HttpURLConnection.HTTP_SEE_OTHER:
|
||||
case HTTP_TEMP_REDIRECT:
|
||||
final String location = conn.getHeaderField("Location");
|
||||
Log.d(TAG, "New location: " + location);
|
||||
conn.disconnect();
|
||||
url = new URL(url, location);
|
||||
break;
|
||||
default:
|
||||
return rh.onAfterConnect(conn);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (conn != null)
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
throw new RedirectionException();
|
||||
}
|
||||
|
||||
private Object requst(RequestHelper rh) throws Exception {
|
||||
Exception exception = null;
|
||||
for (int times = 0;
|
||||
times < MAX_RETRY &&(times == 0 || rh.onRetry(exception));
|
||||
times++) {
|
||||
try {
|
||||
return doRequst(rh);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
rh.onRequestFailed(exception);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
|
||||
public static interface RequestHelper {
|
||||
|
||||
/**
|
||||
* Get the URL to connect
|
||||
* @return the URL to connect
|
||||
*/
|
||||
public URL getUrl() throws MalformedURLException;
|
||||
|
||||
/**
|
||||
* Add header or do something else for HttpURLConnection before connect
|
||||
*
|
||||
* @param conn the connection
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public void onBeforeConnect(HttpURLConnection conn) throws Exception;
|
||||
|
||||
/**
|
||||
* Get what do you need from HttpURLConnection after connect
|
||||
* Return null means get error
|
||||
*
|
||||
* @param conn the connection
|
||||
* @return what you want to return
|
||||
* @throws Exception
|
||||
*/
|
||||
public Object onAfterConnect(HttpURLConnection conn) throws Exception;
|
||||
|
||||
/**
|
||||
* Retry http connecting, or stop
|
||||
*
|
||||
* @param previousException previous thrown
|
||||
* @return true for retry, false for stop
|
||||
*/
|
||||
public boolean onRetry(Exception previousException);
|
||||
|
||||
/**
|
||||
* Called when request failed by exception
|
||||
*/
|
||||
public void onRequestFailed(Exception exception);
|
||||
}
|
||||
|
||||
public static abstract class GetStringHelper implements RequestHelper {
|
||||
private final String mUrl;
|
||||
|
||||
public GetStringHelper(String url) {
|
||||
mUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
return new URL(mUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
conn.addRequestProperty("Accept-Encoding", "gzip");
|
||||
}
|
||||
|
||||
private String getCharsetFromContentType(String contentType) {
|
||||
if (contentType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] values = contentType.split(";");
|
||||
for (String value : values) {
|
||||
value = value.trim();
|
||||
|
||||
if (value.toLowerCase().startsWith(CHARSET_KEY)) {
|
||||
return value.substring(CHARSET_KEY.length());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getBody(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
String body = null;
|
||||
InputStream is = null;
|
||||
ByteArrayOutputStream baos = null;
|
||||
try {
|
||||
try {
|
||||
// First try to get input stream
|
||||
is = conn.getInputStream();
|
||||
} catch (Exception t){
|
||||
// If we get error, get error stream
|
||||
is = conn.getErrorStream();
|
||||
}
|
||||
String encoding = conn.getContentEncoding();
|
||||
if (encoding != null && encoding.equalsIgnoreCase("gzip"))
|
||||
is = new GZIPInputStream(is);
|
||||
|
||||
int length = conn.getContentLength();
|
||||
if (length >= 0)
|
||||
baos = new ByteArrayOutputStream(length);
|
||||
else
|
||||
baos = new ByteArrayOutputStream();
|
||||
|
||||
Utils.copy(is, baos);
|
||||
|
||||
// Get charset
|
||||
String charset = getCharsetFromContentType(conn.getContentType());
|
||||
if (charset == null) {
|
||||
charset = DEFAULT_CHARSET;
|
||||
}
|
||||
|
||||
body = baos.toString(charset);
|
||||
|
||||
} finally {
|
||||
Utils.closeQuietly(is);
|
||||
Utils.closeQuietly(baos);
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onAfterConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
return getBody(conn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRetry(Exception previousException) {
|
||||
// Do not care about exception, just retry
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestFailed(Exception exception) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RequstHelper for GET method
|
||||
*/
|
||||
public static class GetHelper extends GetStringHelper {
|
||||
|
||||
public GetHelper(String url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
super.onBeforeConnect(conn);
|
||||
conn.setRequestMethod("GET");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RequstHelper for post form data, use POST method
|
||||
*/
|
||||
public static class PostFormHelper extends GetStringHelper {
|
||||
private final String[][] mArgs;
|
||||
|
||||
public PostFormHelper(String url, String[][] args) {
|
||||
super(url);
|
||||
mArgs = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
super.onBeforeConnect(conn);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type",
|
||||
"application/x-www-form-urlencoded");
|
||||
|
||||
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (String[] arg : mArgs) {
|
||||
if (i != 0)
|
||||
sb.append("&");
|
||||
sb.append(URLEncoder.encode(arg[0], "UTF-8"));
|
||||
sb.append("=");
|
||||
sb.append(URLEncoder.encode(arg[1], "UTF-8"));
|
||||
i++;
|
||||
}
|
||||
out.writeBytes(sb.toString());
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RequstHelper for post json, use POST method
|
||||
*/
|
||||
public static class PostJsonHelper extends GetStringHelper {
|
||||
private final JSONObject mJo;
|
||||
|
||||
public PostJsonHelper(String url, JSONObject jo) {
|
||||
super(url);
|
||||
mJo = jo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn) throws Exception {
|
||||
super.onBeforeConnect(conn);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
|
||||
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
|
||||
String body = mJo.toString();
|
||||
Log.d(TAG, "Post json: " + body);
|
||||
out.writeBytes(body);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class FormData {
|
||||
private final Map<String, String> mProperties;
|
||||
|
||||
public FormData() {
|
||||
mProperties = new LinkedHashMap<String, String>();
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value) {
|
||||
mProperties.put(key, value);
|
||||
}
|
||||
|
||||
public void clearProperty(String key) {
|
||||
mProperties.remove(key);
|
||||
}
|
||||
|
||||
public void clearAllProperties() {
|
||||
mProperties.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put information to target OutputStream.
|
||||
*
|
||||
* @param os target outputStream
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public abstract void output(OutputStream os) throws IOException;
|
||||
|
||||
public void doOutPut(OutputStream os) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : mProperties.keySet())
|
||||
sb.append(key).append(": ").append(mProperties.get(key)).append("\r\n");
|
||||
sb.append("\r\n");
|
||||
os.write(sb.toString().getBytes());
|
||||
output(os);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringData extends FormData {
|
||||
private final String mStr;
|
||||
|
||||
public StringData(String str) {
|
||||
mStr = str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(OutputStream os) throws IOException {
|
||||
os.write(mStr.getBytes());
|
||||
os.write("\r\n".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapData extends FormData {
|
||||
private final Bitmap mBitmap;
|
||||
|
||||
public BitmapData(Bitmap bmp) {
|
||||
mBitmap = bmp;
|
||||
setProperty("Content-Type", "image/jpeg");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(OutputStream os) throws IOException {
|
||||
mBitmap.compress(Bitmap.CompressFormat.JPEG, 98, os);
|
||||
os.write("\r\n".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileData extends FormData {
|
||||
private final File mFile;
|
||||
|
||||
public FileData(File file) {
|
||||
mFile = file;
|
||||
setProperty("Content-Type",
|
||||
URLConnection.guessContentTypeFromName(file.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(OutputStream os) throws IOException {
|
||||
InputStream is = new FileInputStream(mFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while((bytesRead = is.read(buffer)) !=-1)
|
||||
os.write(buffer, 0, bytesRead);
|
||||
is.close();
|
||||
|
||||
os.write("\r\n".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PostFormDataHelper extends GetStringHelper {
|
||||
private static final String BOUNDARY = "------WebKitFormBoundary7eDB0hDQ91s22Tkf";
|
||||
|
||||
private final List<FormData> mDataList;
|
||||
|
||||
|
||||
public PostFormDataHelper(String url, List<FormData> dataList) {
|
||||
super(url);
|
||||
mDataList = dataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn) throws Exception {
|
||||
super.onBeforeConnect(conn);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type",
|
||||
"multipart/form-data; boundary=----WebKitFormBoundary7eDB0hDQ91s22Tkf");
|
||||
|
||||
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
|
||||
|
||||
for (FormData data : mDataList) {
|
||||
out.write(BOUNDARY.getBytes());
|
||||
out.write("\r\n".getBytes());
|
||||
data.doOutPut(out);
|
||||
}
|
||||
out.write(BOUNDARY.getBytes());
|
||||
out.write("--".getBytes());
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static class GetImageHelper implements RequestHelper {
|
||||
private final String mUrl;
|
||||
|
||||
public GetImageHelper(String url) {
|
||||
mUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
return new URL(mUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
conn.setRequestMethod("GET");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onAfterConnect(HttpURLConnection conn)
|
||||
throws Exception {
|
||||
// If just
|
||||
// Bitmap bmp = BitmapFactory.decodeStream(conn.getInputStream(), null, Ui.getBitmapOpt());
|
||||
// bitmap might be incomplete.
|
||||
int size = conn.getContentLength();
|
||||
ByteArrayOutputStream a;
|
||||
FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(size == -1 ? 24 * 1024 : (size + 100));
|
||||
Utils.copy(conn.getInputStream(), fbaos);
|
||||
Bitmap bmp = BitmapFactory.decodeByteArray(fbaos.getBuffer(), 0, fbaos.size(), null);
|
||||
|
||||
if (bmp == null) {
|
||||
throw new IllegalStateException("Can not decode to bitmap.");
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRetry(Exception previousException) {
|
||||
// Do not care about exception, just retry
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestFailed(Exception exception) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
public static interface OnDownloadListener {
|
||||
|
||||
/**
|
||||
* Called before connecting
|
||||
*/
|
||||
public void onStartConnecting();
|
||||
|
||||
/**
|
||||
* Called after connecting and before downloading
|
||||
*
|
||||
* @param totalSize content length, -1 for unknown
|
||||
*/
|
||||
public void onStartDownloading(int totalSize);
|
||||
|
||||
/**
|
||||
* File name is fixed
|
||||
*
|
||||
* @param newName new file name
|
||||
*/
|
||||
public void onNameFix(String newName);
|
||||
|
||||
/**
|
||||
* Called repeatedly during downloading
|
||||
*
|
||||
* @param downloadSize downloaded size
|
||||
* @param totalSize content length, -1 for unknown
|
||||
*/
|
||||
public void onDownload(int downloadSize, int totalSize);
|
||||
|
||||
/**
|
||||
* Called when download ok
|
||||
*/
|
||||
public void onSuccess();
|
||||
|
||||
/**
|
||||
* Called when download stop
|
||||
*/
|
||||
public void onStop();
|
||||
|
||||
/**
|
||||
* Called when download failed
|
||||
*
|
||||
* @param e the exception why fail
|
||||
*/
|
||||
public void onFailure(Exception e);
|
||||
}
|
||||
|
||||
public static class DownloadControlor {
|
||||
private boolean mStop = false;
|
||||
|
||||
public void stop() {
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mStop = false;
|
||||
}
|
||||
|
||||
public boolean isStop() {
|
||||
return mStop;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DownloadHelper implements RequestHelper {
|
||||
|
||||
private static final String DOWNLOAD_EXTENSION = ".download";
|
||||
|
||||
private final String mUrl;
|
||||
private final File mDir;
|
||||
private String mFileName;
|
||||
private File mFile;
|
||||
private File mTempFile;
|
||||
private final DownloadControlor mControlor;
|
||||
private final OnDownloadListener mListener;
|
||||
private int mContentLength;
|
||||
private int mReceivedSize;
|
||||
|
||||
public DownloadHelper(String url, File dir, String fileName,
|
||||
DownloadControlor controlor, OnDownloadListener listener) {
|
||||
mUrl = url;
|
||||
mDir = dir;
|
||||
mFileName = fileName;
|
||||
mControlor = controlor;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
return new URL(mUrl);
|
||||
}
|
||||
|
||||
private void checkStop() throws StopRequestException {
|
||||
if (mControlor != null && mControlor.isStop()) {
|
||||
throw new StopRequestException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn) throws Exception {
|
||||
checkStop();
|
||||
|
||||
conn.setRequestMethod("GET");
|
||||
// Sometimes for application/octet-stream, we need to add this line
|
||||
// to get content length
|
||||
conn.setRequestProperty("Range", "bytes=0-");
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onStartConnecting();
|
||||
}
|
||||
}
|
||||
|
||||
private int getContentLength(HttpURLConnection conn) {
|
||||
int contentLength = conn.getContentLength();
|
||||
String range;
|
||||
if (contentLength == -1 &&
|
||||
(range = conn.getHeaderField("Content-Range")) != null) {
|
||||
// Content-Range looks like bytes 500-999/1234
|
||||
contentLength = 0;
|
||||
int step = 0;
|
||||
boolean isNum = false;
|
||||
char ch;
|
||||
for (int i = 0; i < range.length(); i++) {
|
||||
ch = range.charAt(i);
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (!isNum) {
|
||||
isNum = true;
|
||||
step++;
|
||||
}
|
||||
} else {
|
||||
isNum = false;
|
||||
}
|
||||
if (isNum && step == 3) {
|
||||
contentLength = contentLength * 10 + ch - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new extension
|
||||
*
|
||||
* @param ext new extension
|
||||
* @return null for not changed
|
||||
*/
|
||||
protected String fixNewExtension(String ext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onAfterConnect(HttpURLConnection conn) throws Exception {
|
||||
// Check stop
|
||||
if (mControlor != null && mControlor.isStop())
|
||||
throw new StopRequestException();
|
||||
|
||||
String contentType = conn.getContentType();
|
||||
int index = contentType.indexOf(';');
|
||||
if (index != -1)
|
||||
contentType = contentType.substring(0, index);
|
||||
|
||||
mContentLength = getContentLength(conn);
|
||||
if (mListener != null) {
|
||||
mListener.onStartDownloading(mContentLength);
|
||||
}
|
||||
|
||||
// Fix extension
|
||||
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
||||
String newExtension = mime.getExtensionFromMimeType(contentType);
|
||||
if (newExtension == null) {
|
||||
index = contentType.lastIndexOf('/');
|
||||
if (index != -1) {
|
||||
newExtension = contentType.substring(index + 1);
|
||||
}
|
||||
}
|
||||
newExtension = fixNewExtension(newExtension);
|
||||
if (newExtension != null) {
|
||||
index = mFileName.lastIndexOf('.');
|
||||
if (index == -1) {
|
||||
mFileName = mFileName + '.' + newExtension;
|
||||
} else {
|
||||
mFileName = mFileName.substring(0, index) + '.' + newExtension;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure parent exist
|
||||
if (!mDir.mkdirs() && !mDir.isDirectory()) {
|
||||
throw new FileNotFoundException("Can not create dir " +
|
||||
mDir.getPath());
|
||||
}
|
||||
|
||||
mFile = new File(mDir, mFileName);
|
||||
mTempFile = new File(mDir, mFileName + DOWNLOAD_EXTENSION);
|
||||
|
||||
// Transfer
|
||||
transferData(conn.getInputStream(), new FileOutputStream(mTempFile));
|
||||
|
||||
// Get ok, rename
|
||||
Utils.deleteFile(mFile);
|
||||
if (!mTempFile.renameTo(mFile)) {
|
||||
throw new FileNotFoundException("Rename " + mTempFile.getPath() +
|
||||
" to " + mFile.getPath() + " error");
|
||||
}
|
||||
|
||||
// Callback
|
||||
if (mListener != null) {
|
||||
mListener.onSuccess();
|
||||
}
|
||||
|
||||
// We do not need result
|
||||
return null;
|
||||
}
|
||||
|
||||
private void transferData(InputStream in, OutputStream out)
|
||||
throws Exception {
|
||||
final byte data[] = new byte[512 * 1024];
|
||||
mReceivedSize = 0;
|
||||
|
||||
while (true) {
|
||||
// Check stop first
|
||||
checkStop();
|
||||
|
||||
int bytesRead = in.read(data);
|
||||
if (bytesRead == -1)
|
||||
break;
|
||||
out.write(data, 0, bytesRead);
|
||||
mReceivedSize += bytesRead;
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onDownload(mReceivedSize, mContentLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (mContentLength != -1 && mReceivedSize != mContentLength) {
|
||||
throw new UncompletedException("Received size is " + mReceivedSize
|
||||
+ ", but ContentLength is " + mContentLength);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRetry(Exception previousException) {
|
||||
// Delete unfinished file
|
||||
Utils.deleteFile(mTempFile);
|
||||
Utils.deleteFile(mFile);
|
||||
|
||||
if (previousException instanceof StopRequestException) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestFailed(Exception exception) {
|
||||
if (mListener != null) {
|
||||
if (exception instanceof StopRequestException) {
|
||||
mListener.onStop();
|
||||
} else {
|
||||
mListener.onFailure(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Http GET method
|
||||
* @param url the url to get
|
||||
* @return body
|
||||
*/
|
||||
public String get(String url) throws Exception {
|
||||
return (String) requst(new GetHelper(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post form data
|
||||
* @param url the url to post
|
||||
* @param args the form to post
|
||||
* @return body
|
||||
*/
|
||||
public String postForm(String url, String[][] args) throws Exception {
|
||||
return (String) requst(new PostFormHelper(url, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post json data
|
||||
* @param url the url to post
|
||||
* @param json the json to post
|
||||
* @return body
|
||||
*/
|
||||
public String postJson(String url, JSONObject json) throws Exception {
|
||||
return (String) requst(new PostJsonHelper(url, json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post data, multipart/form-data
|
||||
* @param url the url to post
|
||||
* @param dataList the data list to post
|
||||
* @return body
|
||||
*/
|
||||
public String postFormData(String url, List<FormData> dataList) throws Exception {
|
||||
return (String)requst(new PostFormDataHelper(url, dataList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image
|
||||
* @param url the url to get image
|
||||
* @return bitmap
|
||||
*/
|
||||
public Bitmap getImage(String url) throws Exception {
|
||||
return (Bitmap) requst(new GetImageHelper(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Init DownloadHelper
|
||||
*
|
||||
* @param url the url to download
|
||||
* @param dir the dir to download file
|
||||
* @param file the file name
|
||||
* @param controlor download control
|
||||
* @param listener download listener
|
||||
*/
|
||||
public void download(String url, File dir, String file,
|
||||
DownloadControlor controlor, OnDownloadListener listener) throws Exception {
|
||||
requst(new DownloadHelper(url, dir, file, controlor, listener));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.network;
|
||||
|
||||
public class RedirectionException extends Exception {
|
||||
|
||||
public RedirectionException() {
|
||||
super("To many redirection");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.network;
|
||||
|
||||
public class ResponseCodeException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.network;
|
||||
|
||||
public class StopRequestException extends Exception {
|
||||
|
||||
public StopRequestException() {
|
||||
super("Stoped by request");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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.network;
|
||||
|
||||
public class UncompletedException extends Exception {
|
||||
|
||||
public UncompletedException() {
|
||||
super("Uncompleted error");
|
||||
}
|
||||
|
||||
public UncompletedException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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.network;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class UrlBuilder {
|
||||
|
||||
public String mRootUrl;
|
||||
public Map<String, Object> mQueryMap = new HashMap<String, Object>();
|
||||
|
||||
public UrlBuilder(String rootUrl) {
|
||||
mRootUrl = rootUrl;
|
||||
}
|
||||
|
||||
public void addQuery(String key, Object value) {
|
||||
mQueryMap.put(key, value);
|
||||
}
|
||||
|
||||
public String build() {
|
||||
if (mQueryMap.size() == 0) {
|
||||
return mRootUrl;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(mRootUrl);
|
||||
sb.append("?");
|
||||
|
||||
Iterator<String> iter = mQueryMap.keySet().iterator();
|
||||
if (iter.hasNext()) {
|
||||
String key = iter.next();
|
||||
Object value = mQueryMap.get(key);
|
||||
sb.append(key).append("=").append(value);
|
||||
}
|
||||
while (iter.hasNext()) {
|
||||
String key = iter.next();
|
||||
Object value = mQueryMap.get(key);
|
||||
sb.append("&").append(key).append("=").append(value);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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;
|
||||
|
||||
public class AssertUtils {
|
||||
|
||||
public static void assertNotNull(Object object) throws NullPointerException {
|
||||
assertNotNull(null, object);
|
||||
}
|
||||
|
||||
public static void assertNotNull(String message, Object object)
|
||||
throws NullPointerException {
|
||||
if (object == null) {
|
||||
throw new NullPointerException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEquals(String message, int expected, int actual)
|
||||
throws NotEqualsException {
|
||||
if (expected != actual) {
|
||||
throw new NotEqualsException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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 java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* It is the same as {@link java.io.ByteArrayOutputStream}, but buffer can be got
|
||||
* directly. So it is not safe.
|
||||
*/
|
||||
public class FastByteArrayOutputStream extends OutputStream {
|
||||
/**
|
||||
* The byte array containing the bytes written.
|
||||
*/
|
||||
protected byte[] buf;
|
||||
|
||||
/**
|
||||
* The number of bytes written.
|
||||
*/
|
||||
protected int count;
|
||||
|
||||
/**
|
||||
* Constructs a new ByteArrayOutputStream with a default size of 32 bytes.
|
||||
* If more than 32 bytes are written to this instance, the underlying byte
|
||||
* array will expand.
|
||||
*/
|
||||
public FastByteArrayOutputStream() {
|
||||
buf = new byte[32];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ByteArrayOutputStream} with a default size of
|
||||
* {@code size} bytes. If more than {@code size} bytes are written to this
|
||||
* instance, the underlying byte array will expand.
|
||||
*
|
||||
* @param size
|
||||
* initial size for the underlying byte array, must be
|
||||
* non-negative.
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code size} < 0.
|
||||
*/
|
||||
public FastByteArrayOutputStream(int size) {
|
||||
if (size >= 0) {
|
||||
buf = new byte[size];
|
||||
} else {
|
||||
throw new IllegalArgumentException("size < 0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this stream. This releases system resources used for this stream.
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
* if an error occurs while attempting to close this stream.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
/**
|
||||
* Although the spec claims "A closed stream cannot perform output
|
||||
* operations and cannot be reopened.", this implementation must do
|
||||
* nothing.
|
||||
*/
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void expand(int i) {
|
||||
/* Can the buffer handle @i more bytes, if not expand it */
|
||||
if (count + i <= buf.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] newbuf = new byte[(count + i) * 2];
|
||||
System.arraycopy(buf, 0, newbuf, 0, count);
|
||||
buf = newbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this stream to the beginning of the underlying byte array. All
|
||||
* subsequent writes will overwrite any bytes previously stored in this
|
||||
* stream.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes written to this stream so far.
|
||||
*
|
||||
* @return the number of bytes written to this stream.
|
||||
*/
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this ByteArrayOutputStream as a byte array. Any
|
||||
* changes made to the receiver after returning will not be reflected in the
|
||||
* byte array returned to the caller.
|
||||
*
|
||||
* @return this stream's current contents as a byte array.
|
||||
*/
|
||||
public synchronized byte[] getBuffer() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this ByteArrayOutputStream as a string. Any
|
||||
* changes made to the receiver after returning will not be reflected in the
|
||||
* string returned to the caller.
|
||||
*
|
||||
* @return this stream's current contents as a string.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(buf, 0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this ByteArrayOutputStream as a string. Each byte
|
||||
* {@code b} in this stream is converted to a character {@code c} using the
|
||||
* following function:
|
||||
* {@code c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))}. This method is
|
||||
* deprecated and either {@link #toString()} or {@link #toString(String)}
|
||||
* should be used.
|
||||
*
|
||||
* @param hibyte
|
||||
* the high byte of each resulting Unicode character.
|
||||
* @return this stream's current contents as a string with the high byte set
|
||||
* to {@code hibyte}.
|
||||
* @deprecated Use {@link #toString()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(int hibyte) {
|
||||
char[] newBuf = new char[size()];
|
||||
for (int i = 0; i < newBuf.length; i++) {
|
||||
newBuf[i] = (char) (((hibyte & 0xff) << 8) | (buf[i] & 0xff));
|
||||
}
|
||||
return new String(newBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this ByteArrayOutputStream as a string converted
|
||||
* according to the encoding declared in {@code charsetName}.
|
||||
*
|
||||
* @param charsetName
|
||||
* a string representing the encoding to use when translating
|
||||
* this stream to a string.
|
||||
* @return this stream's current contents as an encoded string.
|
||||
* @throws java.io.UnsupportedEncodingException
|
||||
* if the provided encoding is not supported.
|
||||
*/
|
||||
public String toString(String charsetName) throws UnsupportedEncodingException {
|
||||
return new String(buf, 0, count, charsetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code count} bytes from the byte array {@code buffer} starting at
|
||||
* offset {@code index} to this stream.
|
||||
*
|
||||
* @param buffer
|
||||
* the buffer to be written.
|
||||
* @param offset
|
||||
* the initial position in {@code buffer} to retrieve bytes.
|
||||
* @param len
|
||||
* the number of bytes of {@code buffer} to write.
|
||||
* @throws NullPointerException
|
||||
* if {@code buffer} is {@code null}.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if {@code offset < 0} or {@code len < 0}, or if
|
||||
* {@code offset + len} is greater than the length of
|
||||
* {@code buffer}.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(byte[] buffer, int offset, int len) {
|
||||
if ((offset | count) < 0 || offset > buffer.length || buffer.length - offset < count) {
|
||||
throw new ArrayIndexOutOfBoundsException("length=" + buffer.length + "; regionStart=" + offset
|
||||
+ "; regionLength=" + count);
|
||||
}
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
expand(len);
|
||||
System.arraycopy(buffer, offset, buf, this.count, len);
|
||||
this.count += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte {@code oneByte} to the OutputStream. Only the
|
||||
* low order byte of {@code oneByte} is written.
|
||||
*
|
||||
* @param oneByte
|
||||
* the byte to be written.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(int oneByte) {
|
||||
if (count == buf.length) {
|
||||
expand(1);
|
||||
}
|
||||
buf[count++] = (byte) oneByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the contents of this stream and writes it to the output stream
|
||||
* {@code out}.
|
||||
*
|
||||
* @param out
|
||||
* an OutputStream on which to write the contents of this stream.
|
||||
* @throws java.io.IOException
|
||||
* if an error occurs while writing to {@code out}.
|
||||
*/
|
||||
public synchronized void writeTo(OutputStream out) throws IOException {
|
||||
out.write(buf, 0, count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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
|
||||
* 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 java.util.Random;
|
||||
|
||||
// Get most code from android.util.MathUtils
|
||||
public final class MathUtils {
|
||||
private static final Random sRandom = new Random();
|
||||
private static final float DEG_TO_RAD = 3.1415926f / 180.0f;
|
||||
private static final float RAD_TO_DEG = 180.0f / 3.1415926f;
|
||||
|
||||
public static float abs(float v) {
|
||||
return v > 0 ? v : -v;
|
||||
}
|
||||
|
||||
public static float log(float a) {
|
||||
return (float) Math.log(a);
|
||||
}
|
||||
|
||||
public static float exp(float a) {
|
||||
return (float) Math.exp(a);
|
||||
}
|
||||
|
||||
public static float pow(float a, float b) {
|
||||
return (float) Math.pow(a, b);
|
||||
}
|
||||
|
||||
public static float max(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float max(int a, int b, int c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float max(float... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
float n = arg[0];
|
||||
float m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m > n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static int max(int... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
int n = arg[0];
|
||||
int m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m > n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static float min(float a, float b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(int a, int b, int c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(float... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
float n = arg[0];
|
||||
float m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m < n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static int min(int... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
int n = arg[0];
|
||||
int m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m < n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float x2, float y2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
return (float) Math.hypot(x, y);
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float z1, float x2, float y2, float z2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
final float z = (z2 - z1);
|
||||
return (float) Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public static float mag(float a, float b) {
|
||||
return (float) Math.hypot(a, b);
|
||||
}
|
||||
|
||||
public static float mag(float a, float b, float c) {
|
||||
return (float) Math.sqrt(a * a + b * b + c * c);
|
||||
}
|
||||
|
||||
public static float sq(float v) {
|
||||
return v * v;
|
||||
}
|
||||
|
||||
public static float radians(float degrees) {
|
||||
return degrees * DEG_TO_RAD;
|
||||
}
|
||||
|
||||
public static float degrees(float radians) {
|
||||
return radians * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
public static float acos(float value) {
|
||||
return (float) Math.acos(value);
|
||||
}
|
||||
|
||||
public static float asin(float value) {
|
||||
return (float) Math.asin(value);
|
||||
}
|
||||
|
||||
public static float atan(float value) {
|
||||
return (float) Math.atan(value);
|
||||
}
|
||||
|
||||
public static float atan2(float a, float b) {
|
||||
return (float) Math.atan2(a, b);
|
||||
}
|
||||
|
||||
public static float tan(float angle) {
|
||||
return (float) Math.tan(angle);
|
||||
}
|
||||
|
||||
public static int lerp(int start, int stop, float amount) {
|
||||
return start + (int) ((stop - start) * amount);
|
||||
}
|
||||
|
||||
public static float lerp(float start, float stop, float amount) {
|
||||
return start + (stop - start) * amount;
|
||||
}
|
||||
|
||||
public static float norm(float start, float stop, float value) {
|
||||
return (value - start) / (stop - start);
|
||||
}
|
||||
|
||||
public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
|
||||
return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static int clamp(int x, int min, int max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static long clamp(long x, long min, long max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next power of two.
|
||||
* Returns the input if it is already power of 2.
|
||||
* Throws IllegalArgumentException if the input is <= 0 or
|
||||
* the answer overflows.
|
||||
*
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
public static int nextPowerOf2(int n) {
|
||||
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
|
||||
n -= 1;
|
||||
n |= n >> 16;
|
||||
n |= n >> 8;
|
||||
n |= n >> 4;
|
||||
n |= n >> 2;
|
||||
n |= n >> 1;
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return is a and b is in the same side to standard.
|
||||
* If one of a and b equals standard, return true.
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param standard
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSameSide(float a, float b, float standard) {
|
||||
return (a >= standard && b >= standard) || (a <= standard && b <= standard);
|
||||
}
|
||||
|
||||
public static int random(int howbig) {
|
||||
return (int) (sRandom.nextFloat() * howbig);
|
||||
}
|
||||
|
||||
public static int random(int howsmall, int howbig) {
|
||||
if (howsmall >= howbig)
|
||||
return howsmall;
|
||||
return (int) (sRandom.nextFloat() * (howbig - howsmall) + howsmall);
|
||||
}
|
||||
|
||||
public static float random(float howbig) {
|
||||
return sRandom.nextFloat() * howbig;
|
||||
}
|
||||
|
||||
public static float random(float howsmall, float howbig) {
|
||||
if (howsmall >= howbig)
|
||||
return howsmall;
|
||||
return sRandom.nextFloat() * (howbig - howsmall) + howsmall;
|
||||
}
|
||||
|
||||
public static void randomSeed(long seed) {
|
||||
sRandom.setSeed(seed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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;
|
||||
|
||||
public class NotEqualsException extends Exception {
|
||||
|
||||
public NotEqualsException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,807 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Vibrator;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class Utils {
|
||||
@SuppressWarnings("unused")
|
||||
private static String TAG = Utils.class.getSimpleName();
|
||||
|
||||
public static final DateFormat DATE_FORMATE_FOR_GALLERY = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
|
||||
|
||||
public static final DateFormat DATE_FORMATE_FOR_UPDATE = new SimpleDateFormat("yyyyMMdd", Locale.US);
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
|
||||
/**
|
||||
* Colse is. Don't worry about anything.
|
||||
*
|
||||
* @param is
|
||||
*/
|
||||
public static void closeQuietly(Closeable is) {
|
||||
try {
|
||||
if (is != null)
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put InputStream to File, default bufferSize is 512 * 1024
|
||||
*
|
||||
* @param is
|
||||
* @param file
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static void inputStream2File(InputStream is, File file) throws IOException {
|
||||
OutputStream os = new FileOutputStream(file);
|
||||
copy(is, os);
|
||||
os.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy from is to os
|
||||
*
|
||||
* @param is
|
||||
* @param os
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static void copy(InputStream is, OutputStream os) throws IOException {
|
||||
copy(is, os, 512 * 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy from is to os
|
||||
*
|
||||
* @param is
|
||||
* @param os
|
||||
* @param size Buffer size
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static void copy(InputStream is, OutputStream os, int size) throws IOException {
|
||||
byte[] buffer = new byte[size];
|
||||
int bytesRead;
|
||||
while((bytesRead = is.read(buffer)) !=-1)
|
||||
os.write(buffer, 0, bytesRead);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get String from InputStream
|
||||
*
|
||||
* @param is
|
||||
* @param charset
|
||||
* @param size The InputStream size you estimate
|
||||
* @return
|
||||
*/
|
||||
public static String inputStream2String(InputStream is, String charset, int size) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
|
||||
String str = null;
|
||||
try {
|
||||
copy(is, baos, 1024);
|
||||
str = baos.toString(charset);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
closeQuietly(baos);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not work for dir, Copy from scr to dst, no matter dst exist or not.
|
||||
*
|
||||
* @param src
|
||||
* @param dst
|
||||
* @return True for success, false for fail
|
||||
*/
|
||||
public static boolean copyFile(File src, File dst) {
|
||||
FileInputStream fis = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fis = new FileInputStream(src);
|
||||
fos = new FileOutputStream(dst);
|
||||
copy(fis, fos);
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
} finally {
|
||||
closeQuietly(fis);
|
||||
closeQuietly(fos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you want to copy on new thread, use it to know
|
||||
* success or not.
|
||||
*/
|
||||
public static interface OnCopyFileOverListener {
|
||||
public void onCopyFileOver(boolean success, File src, File dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy file in new thread
|
||||
*
|
||||
* @param src
|
||||
* @param dst
|
||||
* @param l
|
||||
*/
|
||||
public static void copyFileInNewThread(final File src, final File dst, final OnCopyFileOverListener l) {
|
||||
execute(false, new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return copyFile(src, dst);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (l != null)
|
||||
l.onCopyFileOver(result, src, dst);
|
||||
}
|
||||
}, (Void[]) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to delete file or dir and it's children.
|
||||
*
|
||||
* @param file The file to deleted.
|
||||
* @return True if file is delete, children included.
|
||||
*/
|
||||
public static boolean deleteFile(File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = true;
|
||||
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files)
|
||||
deleteFile(f);
|
||||
}
|
||||
if (file.exists()) {
|
||||
// Avoid EBUSY (Device or resource busy) error
|
||||
final File to = new File(file.getAbsolutePath()
|
||||
+ System.currentTimeMillis());
|
||||
if (file.renameTo(to))
|
||||
result &= to.delete();
|
||||
else
|
||||
result &= file.delete();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you want to copy on new thread, use it to know
|
||||
* success or not.
|
||||
*/
|
||||
public static interface OnDeleteFileOverListener {
|
||||
public void onDeleteFileOver(boolean success, File file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new thread to delete dir
|
||||
*/
|
||||
public static void deleteFileInNewThread(final File file, final OnDeleteFileOverListener l) {
|
||||
execute(false, new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return deleteFile(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (l != null)
|
||||
l.onDeleteFileOver(result, file);
|
||||
}
|
||||
}, (Void[]) null);
|
||||
}
|
||||
|
||||
public static int getDateForUpdate() {
|
||||
return Utils.parseIntSafely(DATE_FORMATE_FOR_UPDATE.format(new Date()), 0);
|
||||
}
|
||||
|
||||
|
||||
private static String[] SIZE_UNIT = {"%.2f B", "%.2f KB", "%.2f MB", "%.2f GB"};
|
||||
|
||||
public static String sizeToString(long size) {
|
||||
int length = SIZE_UNIT.length;
|
||||
|
||||
float sizeFloat = size;
|
||||
for (int i = 0; i < length - 1; i++) {
|
||||
if (sizeFloat < 1024)
|
||||
return String.format(SIZE_UNIT[i], sizeFloat);
|
||||
sizeFloat /= 1024;
|
||||
}
|
||||
return String.format(SIZE_UNIT[length - 1], sizeFloat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an {@link android.os.AsyncTask} on a thread pool
|
||||
*
|
||||
* @param forceSerial True to force the task to run in serial order
|
||||
* @param task Task to execute
|
||||
* @param args Optional arguments to pass to
|
||||
* {@link android.os.AsyncTask#execute(Object[])}
|
||||
* @param <T> Task argument type
|
||||
*/
|
||||
@SafeVarargs
|
||||
@SuppressLint("NewApi")
|
||||
public static <T> void execute(final boolean forceSerial, final AsyncTask<T, ?, ?> task,
|
||||
final T... args) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.DONUT) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This class can only be used on API 4 and newer.");
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB || forceSerial) {
|
||||
task.execute(args);
|
||||
} else {
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, args);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] ESCAPE_CHARATER_LIST = {
|
||||
"&",
|
||||
"<",
|
||||
">",
|
||||
""",
|
||||
"'",
|
||||
"×"
|
||||
};
|
||||
|
||||
private static final String[] UNESCAPE_CHARATER_LIST = {
|
||||
"&",
|
||||
"<",
|
||||
">",
|
||||
"\"",
|
||||
"'",
|
||||
"×"
|
||||
};
|
||||
|
||||
/**
|
||||
* Mostly title and url need it
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String unescapeXml(String str) {
|
||||
return replaceEach(str, ESCAPE_CHARATER_LIST, UNESCAPE_CHARATER_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make file name valid by removing invalid character and set max length
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static String standardizeFilename(String name) {
|
||||
String result = name;
|
||||
// Remove invaild char
|
||||
result = result.replaceAll("[\\\\/:*?\"<>\\|]", "");
|
||||
// Make sure size
|
||||
if (result.length() > 255)
|
||||
result = result.substring(0, 255);
|
||||
// Avoid start with whitespace or end with whitespace
|
||||
result = result.replaceAll("^[\\s]+", "").replace("[\\s]+$", "");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the elements of the provided array into a single String
|
||||
* containing the provided list of elements.
|
||||
*
|
||||
* @param array
|
||||
* @param separator
|
||||
* @return
|
||||
*/
|
||||
public static String join(final Object[] array,
|
||||
final Object separator) {
|
||||
final StringBuilder sb = new StringBuilder(array.length * 16);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (i != 0)
|
||||
sb.append(separator);
|
||||
sb.append(array[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A {@code null} reference passed to this method is a no-op, or if
|
||||
* any "search string" or "string to replace" is null, that replace will be
|
||||
* ignored. This will not repeat. For repeating replaces, call the
|
||||
* overloaded method.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceEach(null, *, *) = null
|
||||
* StringUtils.replaceEach("", *, *) = ""
|
||||
* StringUtils.replaceEach("aba", null, null) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[0], null) = "aba"
|
||||
* StringUtils.replaceEach("aba", null, new String[0]) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
|
||||
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
|
||||
* (example of how it does not repeat)
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* text to search and replace in, no-op if null
|
||||
* @param searchList
|
||||
* the Strings to search for, no-op if null
|
||||
* @param replacementList
|
||||
* the Strings to replace them with, no-op if null
|
||||
* @return the text with any replacements processed, {@code null} if
|
||||
* null String input
|
||||
* @throws IllegalArgumentException
|
||||
* if the lengths of the arrays are not the same (null is ok,
|
||||
* and/or size 0)
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
|
||||
return replaceEach(text, searchList, replacementList, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A {@code null} reference passed to this method is a no-op, or if
|
||||
* any "search string" or "string to replace" is null, that replace will be
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceEach(null, *, *, *) = null
|
||||
* StringUtils.replaceEach("", *, *, *) = ""
|
||||
* StringUtils.replaceEach("aba", null, null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
|
||||
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
|
||||
* (example of how it repeats)
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* text to search and replace in, no-op if null
|
||||
* @param searchList
|
||||
* the Strings to search for, no-op if null
|
||||
* @param replacementList
|
||||
* the Strings to replace them with, no-op if null
|
||||
* @param repeat if true, then replace repeatedly
|
||||
* until there are no more possible replacements or timeToLive < 0
|
||||
* @param timeToLive
|
||||
* if less than 0 then there is a circular reference and endless
|
||||
* loop
|
||||
* @return the text with any replacements processed, {@code null} if
|
||||
* null String input
|
||||
* @throws IllegalStateException
|
||||
* if the search is repeating and there is an endless loop due
|
||||
* to outputs of one being inputs to another
|
||||
* @throws IllegalArgumentException
|
||||
* if the lengths of the arrays are not the same (null is ok,
|
||||
* and/or size 0)
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
private static String replaceEach(
|
||||
final String text, final String[] searchList, final String[] replacementList, final boolean repeat, final int timeToLive) {
|
||||
|
||||
// mchyzer Performance note: This creates very few new objects (one major goal)
|
||||
// let me know if there are performance requests, we can create a harness to measure
|
||||
|
||||
if (text == null || TextUtils.isEmpty(text) || searchList == null ||
|
||||
searchList.length == 0 || replacementList == null || replacementList.length == 0) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// if recursing, this shouldn't be less than 0
|
||||
if (timeToLive < 0) {
|
||||
throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
|
||||
"output of one loop is the input of another");
|
||||
}
|
||||
|
||||
final int searchLength = searchList.length;
|
||||
final int replacementLength = replacementList.length;
|
||||
|
||||
// make sure lengths are ok, these need to be equal
|
||||
if (searchLength != replacementLength) {
|
||||
throw new IllegalArgumentException("Search and Replace array lengths don't match: "
|
||||
+ searchLength
|
||||
+ " vs "
|
||||
+ replacementLength);
|
||||
}
|
||||
|
||||
// keep track of which still have matches
|
||||
final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
|
||||
|
||||
// index on index that the match was found
|
||||
int textIndex = -1;
|
||||
int replaceIndex = -1;
|
||||
int tempIndex = -1;
|
||||
|
||||
// index of replace array that will replace the search string found
|
||||
// NOTE: logic duplicated below START
|
||||
for (int i = 0; i < searchLength; i++) {
|
||||
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
|
||||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
tempIndex = text.indexOf(searchList[i]);
|
||||
|
||||
// see if we need to keep searching for this
|
||||
if (tempIndex == -1) {
|
||||
noMoreMatchesForReplIndex[i] = true;
|
||||
} else {
|
||||
if (textIndex == -1 || tempIndex < textIndex) {
|
||||
textIndex = tempIndex;
|
||||
replaceIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: logic mostly below END
|
||||
|
||||
// no search strings found, we are done
|
||||
if (textIndex == -1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
|
||||
// get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
|
||||
int increase = 0;
|
||||
|
||||
// count the replacement text elements that are larger than their corresponding text being replaced
|
||||
for (int i = 0; i < searchList.length; i++) {
|
||||
if (searchList[i] == null || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
final int greater = replacementList[i].length() - searchList[i].length();
|
||||
if (greater > 0) {
|
||||
increase += 3 * greater; // assume 3 matches
|
||||
}
|
||||
}
|
||||
// have upper-bound at 20% increase, then let Java take over
|
||||
increase = Math.min(increase, text.length() / 5);
|
||||
|
||||
final StringBuilder buf = new StringBuilder(text.length() + increase);
|
||||
|
||||
while (textIndex != -1) {
|
||||
|
||||
for (int i = start; i < textIndex; i++) {
|
||||
buf.append(text.charAt(i));
|
||||
}
|
||||
buf.append(replacementList[replaceIndex]);
|
||||
|
||||
start = textIndex + searchList[replaceIndex].length();
|
||||
|
||||
textIndex = -1;
|
||||
replaceIndex = -1;
|
||||
tempIndex = -1;
|
||||
// find the next earliest match
|
||||
// NOTE: logic mostly duplicated above START
|
||||
for (int i = 0; i < searchLength; i++) {
|
||||
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
|
||||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
tempIndex = text.indexOf(searchList[i], start);
|
||||
|
||||
// see if we need to keep searching for this
|
||||
if (tempIndex == -1) {
|
||||
noMoreMatchesForReplIndex[i] = true;
|
||||
} else {
|
||||
if (textIndex == -1 || tempIndex < textIndex) {
|
||||
textIndex = tempIndex;
|
||||
replaceIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: logic duplicated above END
|
||||
|
||||
}
|
||||
final int textLength = text.length();
|
||||
for (int i = start; i < textLength; i++) {
|
||||
buf.append(text.charAt(i));
|
||||
}
|
||||
final String result = buf.toString();
|
||||
if (!repeat) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 for false, Non 0 for true
|
||||
*
|
||||
* @param integer
|
||||
* @return
|
||||
*/
|
||||
public static boolean int2boolean(int integer) {
|
||||
return integer != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* false for 0, true for 1
|
||||
*
|
||||
* @param bool
|
||||
* @return
|
||||
*/
|
||||
public static int boolean2int(boolean bool) {
|
||||
return bool ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ASCII characters up to but not including the next "\r\n", or
|
||||
* "\n".
|
||||
*
|
||||
* @throws java.io.EOFException if the stream is exhausted before the next
|
||||
* newline character.
|
||||
*/
|
||||
public static String readAsciiLine(final InputStream in) throws IOException {
|
||||
final StringBuilder result = new StringBuilder(80);
|
||||
while (true) {
|
||||
final int c = in.read();
|
||||
if (c == -1) {
|
||||
throw new EOFException();
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
result.append((char)c);
|
||||
}
|
||||
final int length = result.length();
|
||||
if (length > 0 && result.charAt(length - 1) == '\r') {
|
||||
result.setLength(length - 1);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to make dir exists
|
||||
*
|
||||
* @param dir
|
||||
* @param force
|
||||
*/
|
||||
public static void ensureDir(File dir, boolean force) {
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
} else if (force && dir.isFile()) {
|
||||
dir.delete();
|
||||
dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if point is in the rect
|
||||
*
|
||||
* @param area
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public static boolean isInArea(int[] area, int x, int y) {
|
||||
if (area == null)
|
||||
throw new NullPointerException("area == null");
|
||||
if (area.length != 4)
|
||||
throw new IllegalArgumentException(
|
||||
"area's length should be 4, but it's length is " + area.length);
|
||||
if (x >= area[0] && x < area[2] && y >= area[1] && y < area[3])
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is color is opaque
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
// TODO put it to UiUtils.class
|
||||
public static boolean isOpaque(int color) {
|
||||
return color >>> 24 == 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws AssertionError if the input is false.
|
||||
* @param cond
|
||||
*/
|
||||
public static void assertTrue(boolean cond) {
|
||||
if (!cond) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vibrator
|
||||
*
|
||||
* @param context
|
||||
* @param milliseconds Duration
|
||||
*/
|
||||
public static void vibrator(Context context, long milliseconds) {
|
||||
// If user force disable vibrator permission, may throw error
|
||||
try {
|
||||
Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(milliseconds);
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Removes duplicate adjacent slashes and any trailing slash.
|
||||
private static String fixSlashes(String origPath) {
|
||||
// Remove duplicate adjacent slashes.
|
||||
boolean lastWasSlash = false;
|
||||
char[] newPath = origPath.toCharArray();
|
||||
int length = newPath.length;
|
||||
int newLength = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char ch = newPath[i];
|
||||
if (ch == '/') {
|
||||
if (!lastWasSlash) {
|
||||
newPath[newLength++] = File.separatorChar;
|
||||
lastWasSlash = true;
|
||||
}
|
||||
} else {
|
||||
newPath[newLength++] = ch;
|
||||
lastWasSlash = false;
|
||||
}
|
||||
}
|
||||
// Remove any trailing slash (unless this is the root of the file system).
|
||||
if (lastWasSlash && newLength > 1) {
|
||||
newLength--;
|
||||
}
|
||||
// Reuse the original string if possible.
|
||||
return (newLength != length) ? new String(newPath, 0, newLength) : origPath;
|
||||
}
|
||||
|
||||
// Joins two path components, adding a separator only if necessary.
|
||||
public static String joinPath(String dirPath, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name == null");
|
||||
}
|
||||
if (dirPath == null || TextUtils.isEmpty(dirPath)) {
|
||||
return fixSlashes(name);
|
||||
} else if (TextUtils.isEmpty(name)) {
|
||||
return fixSlashes(dirPath);
|
||||
} else {
|
||||
int prefixLength = dirPath.length();
|
||||
boolean haveSlash = (prefixLength > 0 && dirPath.charAt(prefixLength - 1) == File.separatorChar);
|
||||
if (!haveSlash) {
|
||||
haveSlash = (name.length() > 0 && name.charAt(0) == File.separatorChar);
|
||||
}
|
||||
return fixSlashes(haveSlash ? (dirPath + name) : (dirPath + File.separatorChar + name));
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseIntSafely(String str, int defaultValue) {
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (Throwable e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static float parseFloatSafely(String str, float defaultValue) {
|
||||
try {
|
||||
return Float.parseFloat(str);
|
||||
} catch (Throwable e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] toIntArray(Collection<? extends Number> collection) {
|
||||
Object[] boxedArray = collection.toArray();
|
||||
int len = boxedArray.length;
|
||||
int[] array = new int[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
// checkNotNull for GWT (do not optimize)
|
||||
array[i] = ((Number) (boxedArray[i])).intValue();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// Throws NullPointerException if the input is null.
|
||||
public static <T> T checkNotNull(T object) {
|
||||
if (object == null) throw new NullPointerException();
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension from target string, int lower case
|
||||
*
|
||||
* @param name
|
||||
* @param defautl
|
||||
* @return
|
||||
*/
|
||||
public static String getExtension(String name, String defautl) {
|
||||
String ext = MimeTypeMap.getFileExtensionFromUrl(name);
|
||||
if (TextUtils.isEmpty(ext))
|
||||
ext = defautl;
|
||||
return ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will return null if can't find mime
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
public static String getMimeType(String url) {
|
||||
String type = null;
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
|
||||
if (extension != null) {
|
||||
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
||||
type = mime.getMimeTypeFromExtension(extension);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* If get error return "";
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getSystemProperties(String key) {
|
||||
try {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class c = Class.forName("android.os.SystemProperties");
|
||||
@SuppressWarnings("unchecked")
|
||||
Method m = c.getDeclaredMethod("get", String.class);
|
||||
m.setAccessible(true);
|
||||
String result = (String) m.invoke(null, key);
|
||||
return result == null ? EMPTY_STRING : result;
|
||||
} catch (Throwable e) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,8 @@ public final class ViewUtils {
|
||||
* Set the enabled state of this view.
|
||||
*
|
||||
* @param v the view to set the enabled state
|
||||
* @param visibility One of GONE, INVISIBLE, or GONE.
|
||||
* @param visibility One of {@link android.view.View#GONE},
|
||||
* {@link android.view.View#INVISIBLE}, or {@link android.view.View#GONE}.
|
||||
*/
|
||||
public static void setVisibility(View v, int visibility) {
|
||||
if (visibility != v.getVisibility()) {
|
||||
|
||||
Reference in new issue
Block a user