Improve GallerySpider
This commit is contained in:
1 parent
ec7e3948d9
commit
702376979a
32 files changed
+2181
-528
No files matched your search
@@ -26,7 +26,7 @@ import java.io.File;
|
||||
*/
|
||||
public final class AppConfig {
|
||||
|
||||
private static final String APP_DIRNAME = "EhViewer";
|
||||
private static final String APP_DIRNAME = "EhViewer-Test";
|
||||
private static final String CRASH_DIRNAME = "crash";
|
||||
private static final String DOWNLOAD_DIRNAME = "download";
|
||||
private static final String TORRENT_DIRNAME = "torrent";
|
||||
|
||||
@@ -257,7 +257,7 @@ public final class EhClient {
|
||||
mEhHttpRequest = null;
|
||||
mListener = null;
|
||||
} else if (status == Status.FINISHED) {
|
||||
mEhHttpRequest.cancel();
|
||||
mEhHttpRequest.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class EhEngine {
|
||||
String body = response.getString();
|
||||
return SignInParser.parse(body);
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
@@ -71,7 +71,7 @@ class EhEngine {
|
||||
String body = response.getString();
|
||||
return GalleryListParser.parse(body, source);
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
@@ -93,7 +93,7 @@ class EhEngine {
|
||||
String body = response.getString();
|
||||
return PopularParser.parse(body);
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
@@ -116,7 +116,7 @@ class EhEngine {
|
||||
GalleryDetailParser.REQUEST_PREVIEW |
|
||||
GalleryDetailParser.REQUEST_COMMENT);
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
@@ -134,7 +134,7 @@ class EhEngine {
|
||||
String body = response.getString();
|
||||
return GalleryDetailParser.parsePreview(body, source);
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
@@ -174,7 +174,7 @@ class EhEngine {
|
||||
|
||||
return list;
|
||||
} catch (Exception e) {
|
||||
if (httpRequest.isCanceled()) {
|
||||
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
|
||||
throw new CanceledException();
|
||||
} else {
|
||||
throw e;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.hippo.ehviewer.client.data.GalleryApiDetail;
|
||||
import com.hippo.ehviewer.client.data.GalleryBase;
|
||||
import com.hippo.ehviewer.util.EhUtils;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -42,10 +43,10 @@ public class GalleryApiParser {
|
||||
gad.gid = g.getInt("gid");
|
||||
gad.token = g.getString("token");
|
||||
gad.archiverKey = g.getString("archiver_key");
|
||||
gad.title = g.getString("title");
|
||||
gad.titleJpn = g.getString("title_jpn");
|
||||
gad.title = StringUtils.unescapeXml(g.getString("title"));
|
||||
gad.titleJpn = StringUtils.unescapeXml(g.getString("title_jpn"));
|
||||
gad.category = EhUtils.getCategory(g.getString("category"));
|
||||
gad.thumb = g.getString("thumb");
|
||||
gad.thumb = StringUtils.unescapeXml(g.getString("thumb"));
|
||||
gad.uploader = g.getString("uploader");
|
||||
gad.posted = GalleryBase.DEFAULT_DATE_FORMAT.format(new Date(Long.parseLong(g.getString("posted")) * 1000));
|
||||
gad.pageCount = Integer.parseInt(g.getString("filecount"));
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
|
||||
package com.hippo.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GalleryBase {
|
||||
public class GalleryBase implements Parcelable {
|
||||
|
||||
public static final DateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
|
||||
|
||||
@@ -108,4 +111,52 @@ public class GalleryBase {
|
||||
}
|
||||
simpleLanguage = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(this.gid);
|
||||
dest.writeString(this.token);
|
||||
dest.writeString(this.title);
|
||||
dest.writeString(this.titleJpn);
|
||||
dest.writeString(this.thumb);
|
||||
dest.writeInt(this.category);
|
||||
dest.writeString(this.posted);
|
||||
dest.writeString(this.uploader);
|
||||
dest.writeFloat(this.rating);
|
||||
dest.writeString(this.simpleLanguage);
|
||||
}
|
||||
|
||||
public GalleryBase() {
|
||||
}
|
||||
|
||||
protected GalleryBase(Parcel in) {
|
||||
this.gid = in.readInt();
|
||||
this.token = in.readString();
|
||||
this.title = in.readString();
|
||||
this.titleJpn = in.readString();
|
||||
this.thumb = in.readString();
|
||||
this.category = in.readInt();
|
||||
this.posted = in.readString();
|
||||
this.uploader = in.readString();
|
||||
this.rating = in.readFloat();
|
||||
this.simpleLanguage = in.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryBase> CREATOR = new Parcelable.Creator<GalleryBase>() {
|
||||
|
||||
@Override
|
||||
public GalleryBase createFromParcel(Parcel source) {
|
||||
return new GalleryBase(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryBase[] newArray(int size) {
|
||||
return new GalleryBase[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
public interface GalleryProvider {
|
||||
|
||||
/**
|
||||
* There is no worker to get the page now.
|
||||
* There is no guarantee that the {@link GalleryProviderListener} will be
|
||||
* called, so do some reactions after get it.
|
||||
*/
|
||||
Object RESULT_NONE = new Object();
|
||||
|
||||
/**
|
||||
* Worker failed to get the page
|
||||
* There is no guarantee that the {@link GalleryProviderListener} will be
|
||||
* called, so do some reactions after get it.
|
||||
*/
|
||||
Object RESULT_FAILED = new Object();
|
||||
|
||||
/**
|
||||
* {@link GalleryProviderListener} will be calls soon.
|
||||
*/
|
||||
Object RESULT_WAIT = new Object();
|
||||
|
||||
/**
|
||||
* Request is out of range
|
||||
*/
|
||||
Object RESULT_OUT_OF_RANGE = new Object();
|
||||
|
||||
/**
|
||||
* Request special index page
|
||||
*
|
||||
* @param index the index to request
|
||||
* @return Might be on of {@link #RESULT_NONE}, {@link #RESULT_FAILED},
|
||||
* {@link #RESULT_WAIT}, {@link #RESULT_OUT_OF_RANGE}. It could be
|
||||
* {@link Float} it means the percent of getting the page
|
||||
*/
|
||||
Object request(int index);
|
||||
|
||||
/**
|
||||
* Just like {@link #request(int)}, but it ignores cache or local file
|
||||
*
|
||||
* @param index the index to request
|
||||
* @return just like {@link #request(int)}
|
||||
*/
|
||||
Object forceRequest(int index);
|
||||
|
||||
/**
|
||||
* Gallery provider may fails, so we need
|
||||
*/
|
||||
void restart();
|
||||
|
||||
void retry();
|
||||
|
||||
void addGalleryProviderListener(GalleryProviderListener listener);
|
||||
|
||||
void removeGalleryProviderListener(GalleryProviderListener listener);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public interface GalleryProviderListener {
|
||||
|
||||
void onTotallyFailed(Exception e);
|
||||
|
||||
void onPartlyFailed(Exception e);
|
||||
|
||||
void onGetSize(int size);
|
||||
|
||||
void onGetBitmap(int index, @Nullable Bitmap bitmap);
|
||||
|
||||
void onPagePercent(int index, float percent);
|
||||
|
||||
void onPageSucceed(int index);
|
||||
|
||||
void onPageFailed(int index, Exception e);
|
||||
}
|
||||
@@ -21,17 +21,24 @@ import android.os.Process;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryBase;
|
||||
import com.hippo.ehviewer.network.EhHttpClient;
|
||||
import com.hippo.ehviewer.util.DBUtils;
|
||||
import com.hippo.ehviewer.util.Settings;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.OSUtils;
|
||||
import com.hippo.yorozuya.PriorityThreadFactory;
|
||||
import com.hippo.yorozuya.SafeSparseArray;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListener {
|
||||
public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListener {
|
||||
|
||||
private static SafeSparseArray<GallerySpider> mMap = new SafeSparseArray<>();
|
||||
|
||||
@@ -40,11 +47,16 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
|
||||
private ImageHandler.Mode mMode;
|
||||
private GalleryBase mGalleryBase;
|
||||
private UniFile mDownloadDir;
|
||||
private ThreadFactory mThreadFactory = new PriorityThreadFactory("SpiderQueen",
|
||||
Process.THREAD_PRIORITY_BACKGROUND);
|
||||
private SpiderQueen mSpiderQueen;
|
||||
|
||||
private List<GallerySpiderListener> mGallerySpiderListeners = new ArrayList<>();
|
||||
private AtomicLongArray mTotalSizes;
|
||||
private AtomicLongArray mReceivedSizes;
|
||||
private AtomicReferenceArray<Float> mPercents;
|
||||
|
||||
private List<GalleryProviderListener> mGalleryProviderListeners = new ArrayList<>();
|
||||
|
||||
private int mReference = 0;
|
||||
|
||||
@@ -53,15 +65,18 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
sSpiderInfoDir = spiderInfoDir;
|
||||
}
|
||||
|
||||
public static GallerySpider obtain(GalleryBase galleryBase, ImageHandler.Mode mode) {
|
||||
public static GallerySpider obtain(GalleryBase galleryBase, ImageHandler.Mode mode) throws IOException {
|
||||
int key = galleryBase.gid;
|
||||
GallerySpider gs = mMap.get(key);
|
||||
if (gs == null) {
|
||||
// A new GallerySpider
|
||||
gs = new GallerySpider(galleryBase, mode);
|
||||
gs.start();
|
||||
mMap.put(key, gs);
|
||||
} else if (mode == ImageHandler.Mode.DOWNLOAD && gs.mMode == ImageHandler.Mode.READ) {
|
||||
gs.setMode(ImageHandler.Mode.DOWNLOAD);
|
||||
// The spider add to db in the construct of GallerySpider
|
||||
} else {
|
||||
// Touch the date
|
||||
DBUtils.touchDirname(galleryBase.gid);
|
||||
}
|
||||
gs.mReference++;
|
||||
return gs;
|
||||
@@ -75,17 +90,25 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
}
|
||||
}
|
||||
|
||||
private GallerySpider(GalleryBase galleryBase, ImageHandler.Mode mode) {
|
||||
private static String downloadDirname(GalleryBase galleryBase) {
|
||||
return FileUtils.ensureFilename(galleryBase.gid + '-' + galleryBase.title); // TODO use jpn title ?
|
||||
}
|
||||
|
||||
private GallerySpider(GalleryBase galleryBase, ImageHandler.Mode mode) throws IOException {
|
||||
mGalleryBase = galleryBase;
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
public void addSpiderListener(GallerySpiderListener listener) {
|
||||
mGallerySpiderListeners.add(listener);
|
||||
}
|
||||
String dirname = DBUtils.getDirname(galleryBase.gid);
|
||||
if (dirname == null) {
|
||||
dirname = downloadDirname(galleryBase);
|
||||
DBUtils.addDirname(galleryBase, dirname);
|
||||
}
|
||||
|
||||
public void removeSpiderListener(GallerySpiderListener listener) {
|
||||
mGallerySpiderListeners.remove(listener);
|
||||
mDownloadDir = Settings.getDownloadPath().createDirectory(dirname);
|
||||
|
||||
if (mDownloadDir == null) {
|
||||
throw new IOException("Can't get download dir");
|
||||
}
|
||||
}
|
||||
|
||||
public void setMode(ImageHandler.Mode mode) {
|
||||
@@ -103,7 +126,7 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
|
||||
if (mSpiderQueen == null) {
|
||||
mSpiderQueen = new SpiderQueen(sHttpClient, sSpiderInfoDir,
|
||||
mGalleryBase, mMode, this);
|
||||
mGalleryBase, mMode, mDownloadDir, this);
|
||||
mThreadFactory.newThread(mSpiderQueen).start();
|
||||
}
|
||||
}
|
||||
@@ -127,9 +150,47 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(int index) {
|
||||
public void addGalleryProviderListener(GalleryProviderListener listener) {
|
||||
mGalleryProviderListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeGalleryProviderListener(GalleryProviderListener listener) {
|
||||
mGalleryProviderListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object request(int index) {
|
||||
if (mSpiderQueen != null) {
|
||||
mSpiderQueen.request(index);
|
||||
Object result = mSpiderQueen.request(index);
|
||||
if (result == SpiderQueen.RESULT_SPIDER) {
|
||||
result = mPercents.get(index);
|
||||
if (result == null) {
|
||||
result = 0.0f;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object forceRequest(int index) {
|
||||
return null;// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart() {
|
||||
if (mSpiderQueen != null) {
|
||||
mSpiderQueen.restart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retry() {
|
||||
if (mSpiderQueen != null) {
|
||||
mSpiderQueen.retry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,33 +201,85 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (mSpiderQueen != null) {
|
||||
return mSpiderQueen.pages();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetPages(final int pages) {
|
||||
public void onTotallyFailed(final Exception e) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onGetPages(pages);
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onTotallyFailed(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSpiderPage(final int index, final float percent) {
|
||||
public void onPartlyFailed(final Exception e) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onSpiderPage(index, percent);
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onPartlyFailed(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDone() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetPages(final int pages) {
|
||||
mTotalSizes = new AtomicLongArray(pages);
|
||||
mReceivedSizes = new AtomicLongArray(pages);
|
||||
mPercents = new AtomicReferenceArray<>(pages);
|
||||
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onGetSize(pages);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSpiderStart(final int index, final long totalSize) {
|
||||
if (mTotalSizes != null && index >= 0 && index < mTotalSizes.length()) {
|
||||
mTotalSizes.lazySet(index, totalSize);
|
||||
}
|
||||
|
||||
mPercents.lazySet(index, 0.0f);
|
||||
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onPagePercent(index, 0.0f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSpiderPage(final int index, long receivedSize) {
|
||||
if (mReceivedSizes != null && index >= 0 && index < mReceivedSizes.length()) {
|
||||
mReceivedSizes.lazySet(index, receivedSize);
|
||||
}
|
||||
|
||||
long totalSize= mTotalSizes.get(index);
|
||||
final float percent = totalSize == 0 ? 0.0f :
|
||||
(float) receivedSize / (float) totalSize;
|
||||
|
||||
mPercents.lazySet(index, percent);
|
||||
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onPagePercent(index, percent);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -177,32 +290,20 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onSpiderSucceed(index);
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onPageSucceed(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSpiderFailed(final int index) {
|
||||
public void onSpiderFailed(final int index, final Exception e) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onSpiderFailed(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetNone(final int index) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onGetNone(index);
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onPageFailed(index, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -213,53 +314,10 @@ public class GallerySpider implements GalleryLoaderImpl, SpiderQueen.SpiderListe
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
for (GalleryProviderListener l : mGalleryProviderListeners) {
|
||||
l.onGetBitmap(index, bitmap);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetSpider(final int index, final float percent) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onGetSpider(index, percent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetFailed(final int index) {
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (GallerySpiderListener l : mGallerySpiderListeners) {
|
||||
l.onGetFailed(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface GallerySpiderListener {
|
||||
|
||||
void onGetPages(int pages);
|
||||
|
||||
void onSpiderPage(int index, float percent);
|
||||
|
||||
void onSpiderSucceed(int index);
|
||||
|
||||
void onSpiderFailed(int index);
|
||||
|
||||
void onGetNone(int index);
|
||||
|
||||
void onGetBitmap(int index, Bitmap bitmap);
|
||||
|
||||
void onGetSpider(int index, float percent);
|
||||
|
||||
void onGetFailed(int index);
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,20 @@ package com.hippo.ehviewer.gallery;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.hippo.beerbelly.SimpleDiskCache;
|
||||
import com.hippo.conaco.BitmapPool;
|
||||
import com.hippo.ehviewer.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.client.data.GalleryBase;
|
||||
import com.hippo.ehviewer.dao.SpiderObj;
|
||||
import com.hippo.ehviewer.util.DBUtils;
|
||||
import com.hippo.ehviewer.util.Settings;
|
||||
import com.hippo.httpclient.HttpClient;
|
||||
import com.hippo.httpclient.HttpResponse;
|
||||
import com.hippo.io.UniFileInputStreamPipe;
|
||||
import com.hippo.io.UniFileOutputStreamPipe;
|
||||
import com.hippo.network.DownloadClient;
|
||||
import com.hippo.network.DownloadRequest;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
@@ -39,10 +41,11 @@ import com.hippo.yorozuya.io.OutputStreamPipe;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
public class ImageHandler {
|
||||
|
||||
public static String[] POSSIBLE_IMAGE_EXTENSIONS = new String[] {
|
||||
public static final String[] POSSIBLE_IMAGE_EXTENSIONS = new String[] {
|
||||
"jpg", "jpeg", "png", "gif"
|
||||
};
|
||||
|
||||
@@ -50,8 +53,8 @@ public class ImageHandler {
|
||||
|
||||
private GalleryBase mGalleryBase;
|
||||
private UniFile mDownloadDir;
|
||||
private Mode mMode;
|
||||
private String[] mFilenames;
|
||||
private transient Mode mMode;
|
||||
private AtomicReferenceArray<String> mFilenames;
|
||||
private BitmapPool mBitmapPool = new BitmapPool();
|
||||
|
||||
public enum Mode {
|
||||
@@ -63,34 +66,11 @@ public class ImageHandler {
|
||||
sDiskCache = diskCache;
|
||||
}
|
||||
|
||||
public ImageHandler(GalleryBase galleryBase, int pages, Mode mode) {
|
||||
public ImageHandler(GalleryBase galleryBase, int pages, Mode mode, UniFile downloadDir) {
|
||||
mGalleryBase = galleryBase;
|
||||
mMode = mode;
|
||||
mFilenames = new String[pages];
|
||||
|
||||
// TODO Let GallerySpider to handle db, just pass dirname to ImageHandler
|
||||
String dirname;
|
||||
SpiderObj spiderObj = DBUtils.getSpiderObj(galleryBase.gid);
|
||||
if (spiderObj == null) {
|
||||
spiderObj = newSpiderObj();
|
||||
DBUtils.insertSpiderObj(spiderObj);
|
||||
}
|
||||
dirname = spiderObj.getDirname();
|
||||
mDownloadDir = Settings.getDownloadPath().createDirectory(dirname);
|
||||
}
|
||||
|
||||
public String downloadDirname() {
|
||||
return FileUtils.ensureFilename(mGalleryBase.gid + '-' + mGalleryBase.title);
|
||||
}
|
||||
|
||||
private SpiderObj newSpiderObj() {
|
||||
SpiderObj spiderObj = new SpiderObj();
|
||||
spiderObj.setGid((long) mGalleryBase.gid);
|
||||
spiderObj.setLastIndex(0);
|
||||
spiderObj.setDirname(downloadDirname());
|
||||
spiderObj.setDownload(mMode == Mode.DOWNLOAD);
|
||||
spiderObj.setTime(System.currentTimeMillis());
|
||||
return spiderObj;
|
||||
mFilenames = new AtomicReferenceArray<>(pages);
|
||||
mDownloadDir = downloadDir;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
@@ -112,6 +92,18 @@ public class ImageHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable String getImageFilename(int index) {
|
||||
String filename = mFilenames.get(index);
|
||||
if (filename == null) {
|
||||
filename = guessImageFilename(index);
|
||||
if (filename != null) {
|
||||
// Put filename into array
|
||||
mFilenames.lazySet(index, filename);
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
private InputStreamPipe getReadInputStreamPipe(int index) {
|
||||
String key = EhCacheKeyFactory.getImageKey(mGalleryBase.gid, index);
|
||||
return sDiskCache.getInputStreamPipe(key);
|
||||
@@ -144,109 +136,183 @@ public class ImageHandler {
|
||||
mBitmapPool.addReusableBitmap(bitmap);
|
||||
}
|
||||
|
||||
public boolean contain(int index) {
|
||||
if (mMode == Mode.READ) {
|
||||
String key = EhCacheKeyFactory.getImageKey(mGalleryBase.gid, index);
|
||||
return sDiskCache.contain(key);
|
||||
} else if (mMode == Mode.DOWNLOAD) {
|
||||
if (index >= mFilenames.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String filename;
|
||||
|
||||
filename = mFilenames[index];
|
||||
if (filename != null) {
|
||||
// Check it in dir
|
||||
if (mDownloadDir.findFile(filename) != null) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
filename = guessImageFilename(index);
|
||||
if (filename != null) {
|
||||
// Put filename into array
|
||||
mFilenames[index] = filename;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find image in cache
|
||||
InputStreamPipe isPipe = getReadInputStreamPipe(index);
|
||||
if (isPipe == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OutputStream os = null;
|
||||
try {
|
||||
isPipe.obtain();
|
||||
|
||||
if (filename == null) {
|
||||
// Try to get extension
|
||||
InputStream is = isPipe.open();
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
isPipe.close();
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(options.outMimeType);
|
||||
if (extension == null) {
|
||||
// Can't get extension;
|
||||
return false;
|
||||
}
|
||||
filename = getImageFilename(index, extension);
|
||||
// Put filename into array
|
||||
mFilenames[index] = filename;
|
||||
}
|
||||
|
||||
UniFile file = mDownloadDir.createFile(filename);
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
os = file.openOutputStream();
|
||||
IOUtils.copy(isPipe.open(), os);
|
||||
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
isPipe.close();
|
||||
isPipe.release();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
private boolean containFromRead(int index) {
|
||||
String key = EhCacheKeyFactory.getImageKey(mGalleryBase.gid, index);
|
||||
return sDiskCache.contain(key);
|
||||
}
|
||||
|
||||
public void save(int index, HttpResponse response) {
|
||||
private boolean containFromDownload(int index) {
|
||||
if (index >= mFilenames.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String filename = mFilenames.get(index);
|
||||
if (filename != null) {
|
||||
// Check it in dir
|
||||
if (mDownloadDir.findFile(filename) != null) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
filename = guessImageFilename(index);
|
||||
if (filename != null) {
|
||||
// Put filename into array
|
||||
mFilenames.lazySet(index, filename);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find image in cache
|
||||
InputStreamPipe isPipe = getReadInputStreamPipe(index);
|
||||
if (isPipe == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy image from cache to dir
|
||||
OutputStream os = null;
|
||||
try {
|
||||
isPipe.obtain();
|
||||
|
||||
if (filename == null) {
|
||||
// Try to get extension
|
||||
InputStream is = isPipe.open();
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
isPipe.close();
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(options.outMimeType);
|
||||
if (extension == null) {
|
||||
// Can't get extension;
|
||||
return false;
|
||||
}
|
||||
filename = getImageFilename(index, extension);
|
||||
// Put filename into array
|
||||
mFilenames.lazySet(index, filename);
|
||||
}
|
||||
|
||||
UniFile file = mDownloadDir.createFile(filename);
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
os = file.openOutputStream();
|
||||
IOUtils.copy(isPipe.open(), os);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
return false;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
isPipe.close();
|
||||
isPipe.release();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contain(int index) {
|
||||
if (mMode == Mode.READ) {
|
||||
return containFromRead(index);
|
||||
} else if (mMode == Mode.DOWNLOAD) {
|
||||
return containFromDownload(index);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getExtension(HttpResponse response, String defaultExtension) {
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(
|
||||
response.getHeaderField("Content-Type"));
|
||||
if (extension == null) {
|
||||
extension = MimeTypeMap.getFileExtensionFromUrl(response.getUrl().toString());
|
||||
if (extension == null) {
|
||||
extension = defaultExtension;
|
||||
}
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
public void save(HttpClient httpClient, String url, int index, SaveHelper helper) throws Exception {
|
||||
OutputStreamPipe osPipe = null;
|
||||
|
||||
if (mMode == Mode.READ) {
|
||||
osPipe = getReadOutputStreamPipe(index);
|
||||
} else if (mMode == Mode.DOWNLOAD) {
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(
|
||||
response.getHeaderField("Content-Type"));
|
||||
if (extension == null) {
|
||||
extension = MimeTypeMap.getFileExtensionFromUrl(response.getUrl().toString());
|
||||
if (extension == null) {
|
||||
extension = "jpg";
|
||||
}
|
||||
}
|
||||
String filename = getImageFilename(index, extension);
|
||||
// Put filename into array
|
||||
mFilenames[index] = filename;
|
||||
// Let download listener to fix filename
|
||||
String filename = getImageFilename(index, "jpg");
|
||||
osPipe = getDownloadOutputStreamPipe(filename);
|
||||
}
|
||||
|
||||
if (osPipe == null) {
|
||||
return;
|
||||
throw new IOException("Can't create OutputStreamPipe");
|
||||
}
|
||||
|
||||
try {
|
||||
osPipe.obtain();
|
||||
IOUtils.copy(response.getInputStream(), osPipe.open());
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
} finally {
|
||||
osPipe.close();
|
||||
osPipe.release();
|
||||
if (helper.mCancel) {
|
||||
throw new IOException("Cancelled");
|
||||
}
|
||||
|
||||
DownloadListener downloadListener = new DownloadListener(index, helper);
|
||||
DownloadRequest request = new DownloadRequest();
|
||||
helper.mRequest = request;
|
||||
request.setHttpClient(httpClient);
|
||||
request.setUrl(url);
|
||||
request.setOSPipe(osPipe);
|
||||
request.setListener(downloadListener);
|
||||
DownloadClient.execute(request);
|
||||
|
||||
if (downloadListener.mException != null) {
|
||||
// Remove failed stuff
|
||||
if (mMode == Mode.READ) {
|
||||
sDiskCache.remove(EhCacheKeyFactory.getImageKey(mGalleryBase.gid, index));
|
||||
} else if (mMode == Mode.DOWNLOAD) {
|
||||
String filename = getImageFilename(index);
|
||||
if (filename != null) {
|
||||
UniFile file = mDownloadDir.findFile(filename);
|
||||
if (file != null) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw downloadListener.mException;
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadListener extends DownloadClient.SimpleDownloadListener {
|
||||
|
||||
private int mIndex;
|
||||
private SaveHelper mHelper;
|
||||
|
||||
private Exception mException;
|
||||
|
||||
public DownloadListener(int index, SaveHelper listener) {
|
||||
mIndex = index;
|
||||
mHelper = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onFixname(String newFilename, String newExtension, String oldFilename) {
|
||||
String filename;
|
||||
if (TextUtils.isEmpty(newExtension)) {
|
||||
filename = oldFilename;
|
||||
} else {
|
||||
filename = FileUtils.getNameFromFilename(oldFilename) + '.' + newExtension;
|
||||
}
|
||||
mFilenames.lazySet(mIndex, filename);
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(long totalSize) {
|
||||
mHelper.onStartSaving(totalSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDonwlad(long receivedSize) {
|
||||
mHelper.onSave(receivedSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Exception e) {
|
||||
mException = e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,14 +321,7 @@ public class ImageHandler {
|
||||
if (mMode == Mode.READ) {
|
||||
isPipe = getReadInputStreamPipe(index);
|
||||
} else if (mMode == Mode.DOWNLOAD) {
|
||||
String filename = mFilenames[index];
|
||||
if (filename == null) {
|
||||
filename = guessImageFilename(index);
|
||||
if (filename != null) {
|
||||
// Put filename into array
|
||||
mFilenames[index] = filename;
|
||||
}
|
||||
}
|
||||
String filename = getImageFilename(index);
|
||||
if (filename != null) {
|
||||
isPipe = getDownloadInputStreamPipe(filename);
|
||||
}
|
||||
@@ -302,4 +361,22 @@ public class ImageHandler {
|
||||
isPipe.release();
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class SaveHelper {
|
||||
|
||||
private DownloadRequest mRequest;
|
||||
|
||||
private boolean mCancel = false;
|
||||
|
||||
public abstract void onStartSaving(long totalSize);
|
||||
|
||||
public abstract void onSave(long receivedSize);
|
||||
|
||||
public void cancel() {
|
||||
mCancel = true;
|
||||
if (mRequest != null) {
|
||||
mRequest.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class SpiderInfo {
|
||||
.append(Integer.toString(previewCountPerPage)).append('\n');
|
||||
|
||||
if (tokens != null) {
|
||||
int length = Math.min(tokens.maxValidIndex(), pages);
|
||||
int length = Math.min(tokens.maxValidIndex() + 1, pages);
|
||||
for (int i = 0; i < length; i++) {
|
||||
String token = tokens.get(i);
|
||||
if (token != null) {
|
||||
|
||||
@@ -28,50 +28,62 @@ import com.hippo.ehviewer.network.EhHttpRequest;
|
||||
import com.hippo.ehviewer.util.Settings;
|
||||
import com.hippo.httpclient.HttpRequest;
|
||||
import com.hippo.httpclient.HttpResponse;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.AutoExpandArray;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.PriorityThreadFactory;
|
||||
import com.hippo.yorozuya.Say;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class SpiderQueen implements Runnable {
|
||||
|
||||
private static final String TAG = SpiderQueen.class.getSimpleName();
|
||||
|
||||
public static final Object RESULT_SPIDER = new Object();
|
||||
|
||||
public static final String SPIDER_FILENAME = ".ehviewer";
|
||||
|
||||
private static final String[] URL_509_SUFFIX_ARRAY = {
|
||||
"/509.gif",
|
||||
"/509s.gif"
|
||||
};
|
||||
|
||||
public static final int PAGE_STATE_NONE = 0;
|
||||
public static final int PAGE_STATE_DOWNLOADING = 1;
|
||||
public static final int PAGE_STATE_SPIDER = 1;
|
||||
public static final int PAGE_STATE_SUCCEED = 2;
|
||||
public static final int PAGE_STATE_FAILED = 3;
|
||||
|
||||
public EhHttpClient mHttpClient;
|
||||
public File mSpiderInfoDir;
|
||||
public GalleryBase mGalleryBase;
|
||||
private UniFile mDownloadDir;
|
||||
public ImageHandler mImageHandler;
|
||||
public int mSource;
|
||||
private ImageHandler.Mode mMode;
|
||||
private SpiderListener mSpiderListener;
|
||||
|
||||
public SpiderInfo mSpiderInfo;
|
||||
public int mNextIndex;
|
||||
public int[] mPageStates;
|
||||
|
||||
public boolean mHasCheckIndexAgain = true;
|
||||
public transient int mNextIndex;
|
||||
public AtomicIntegerArray mPageStates;
|
||||
|
||||
private transient HttpRequest mCurrentHttpRequest;
|
||||
|
||||
private transient boolean mRestart = false;
|
||||
private transient boolean mRetry = false;
|
||||
private transient boolean mStop = false;
|
||||
|
||||
private ThreadFactory mThreadFactory = new PriorityThreadFactory("SpiderWorker",
|
||||
Process.THREAD_PRIORITY_BACKGROUND);
|
||||
@@ -79,23 +91,27 @@ public class SpiderQueen implements Runnable {
|
||||
private LinkedList<Integer> mRequestTokens = new LinkedList<>();
|
||||
private Render mRender;
|
||||
|
||||
// The lock to make Spider Queen sleep
|
||||
private final Object mQueenLock = new Object();
|
||||
// The lock to make worker sleep
|
||||
private final Object mWorkerLock = new Object();
|
||||
private final Object mPageLock = new Object();
|
||||
// The lock for next index
|
||||
private final Object mNextIndexLock = new Object();
|
||||
private final ReentrantLock mWorkerArrayLock = new ReentrantLock();
|
||||
// Lock for mRequestTokens and mSpiderInfo.tokens
|
||||
private final Object mTokenLock = new Object();
|
||||
|
||||
private boolean mStop = false;
|
||||
|
||||
public SpiderQueen(EhHttpClient httpClient, File spiderInfoDir,
|
||||
GalleryBase galleryBase, ImageHandler.Mode mode, SpiderListener listener) {
|
||||
GalleryBase galleryBase, ImageHandler.Mode mode, UniFile downloadDir,
|
||||
SpiderListener listener) {
|
||||
mHttpClient = httpClient;
|
||||
mSpiderInfoDir = spiderInfoDir;
|
||||
mGalleryBase = galleryBase;
|
||||
mSource = Settings.getEhSource();
|
||||
mMode = mode;
|
||||
mDownloadDir = downloadDir;
|
||||
mSpiderListener = listener;
|
||||
|
||||
mSource = Settings.getEhSource();
|
||||
}
|
||||
|
||||
public void setMode(ImageHandler.Mode mode) {
|
||||
@@ -105,11 +121,53 @@ public class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void request(int index) {
|
||||
if (mRender == null) {
|
||||
mSpiderListener.onGetNone(index);
|
||||
// Is the spider queen ready to handle request
|
||||
private boolean isReady() {
|
||||
return mPageStates != null && mSpiderInfo != null &&
|
||||
mRender != null && mImageHandler != null;
|
||||
}
|
||||
|
||||
public Object request(int index) {
|
||||
if (isReady()) {
|
||||
// check range
|
||||
if (index < 0 || index >= mSpiderInfo.pages) {
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
int state = mPageStates.get(index);
|
||||
switch (state) {
|
||||
case PAGE_STATE_NONE:
|
||||
// TODO set next index
|
||||
return GalleryProvider.RESULT_NONE;
|
||||
case PAGE_STATE_SPIDER:
|
||||
return RESULT_SPIDER;
|
||||
case PAGE_STATE_SUCCEED:
|
||||
// Add a render request
|
||||
mRender.request(index);
|
||||
return GalleryProvider.RESULT_WAIT;
|
||||
case PAGE_STATE_FAILED:
|
||||
return GalleryProvider.RESULT_FAILED;
|
||||
default:
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
mRender.request(index);
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
public Object forceRequest(int index) {
|
||||
if (isReady()) {
|
||||
// check range
|
||||
if (index < 0 || index >= mSpiderInfo.pages) {
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
// TODO
|
||||
throw new IllegalStateException();
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
return GalleryProvider.RESULT_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,163 +177,371 @@ public class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mStop = true;
|
||||
if (mCurrentHttpRequest != null) {
|
||||
mCurrentHttpRequest.disconnect();
|
||||
mCurrentHttpRequest = null;
|
||||
}
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
mRestart = true;
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void retry() {
|
||||
mRetry = true;
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Say.d(TAG, "Spider Queen starts");
|
||||
Say.d(TAG, "Spider Queen start");
|
||||
|
||||
while (!mStop) {
|
||||
try {
|
||||
doIt();
|
||||
} catch (Exception e) {
|
||||
// Stop action, disconnect current HttpRequest may raise Exception
|
||||
// No need to report it
|
||||
if (mStop) {
|
||||
break;
|
||||
}
|
||||
|
||||
mRestart = false;
|
||||
mSpiderListener.onTotallyFailed(e);
|
||||
|
||||
while (!mStop && !mRestart) {
|
||||
synchronized (mQueenLock) {
|
||||
try {
|
||||
mQueenLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Say.d(TAG, "Spider Queen end");
|
||||
}
|
||||
|
||||
private SpiderInfo readSpiderInfoFromDataDir() {
|
||||
SpiderInfo spiderInfo;
|
||||
int gid = mGalleryBase.gid;
|
||||
|
||||
File file = new File(mSpiderInfoDir, Integer.toString(gid));
|
||||
if (!file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
spiderInfo = SpiderInfo.read(is);
|
||||
if (gid != spiderInfo.galleryBase.gid) {
|
||||
throw new IllegalStateException("Gid not match in SpiderInfo file");
|
||||
}
|
||||
return spiderInfo;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
private SpiderInfo readSpiderInfoFromDownloadDir() {
|
||||
if (mMode == ImageHandler.Mode.DOWNLOAD) {
|
||||
SpiderInfo spiderInfo;
|
||||
UniFile file = mDownloadDir.findFile(SPIDER_FILENAME);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = file.openInputStream();
|
||||
spiderInfo = SpiderInfo.read(is);
|
||||
if (mGalleryBase.gid != spiderInfo.galleryBase.gid) {
|
||||
throw new IllegalStateException("Gid not match in SpiderInfo file");
|
||||
}
|
||||
return spiderInfo;
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSpiderInfoToDataDir(SpiderInfo spiderInfo) {
|
||||
OutputStream os = null;
|
||||
try {
|
||||
File file = new File(mSpiderInfoDir, Integer.toString(mGalleryBase.gid));
|
||||
os = new FileOutputStream(file);
|
||||
spiderInfo.write(os);
|
||||
} catch (Exception e) {
|
||||
// Ingore
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSpiderInfoToDownloadDir(SpiderInfo spiderInfo) {
|
||||
if (mMode == ImageHandler.Mode.DOWNLOAD) {
|
||||
OutputStream os = null;
|
||||
try {
|
||||
UniFile file = mDownloadDir.findFile(SPIDER_FILENAME);
|
||||
if (file != null) {
|
||||
os = file.openOutputStream();
|
||||
spiderInfo.write(os);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ingore
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SpiderInfo getSpiderInfoFromInternet(EhConfig ehConfig) throws Exception {
|
||||
SpiderInfo spiderInfo;
|
||||
|
||||
// Use normal size to get more preview
|
||||
ehConfig.previewSize = EhConfig.PREVIEW_SIZE_NORMAL;
|
||||
ehConfig.setDirty();
|
||||
|
||||
EhHttpRequest request = new EhHttpRequest();
|
||||
request.setEhConfig(ehConfig);
|
||||
request.setUrl(EhUrl.getDetailUrl(mSource, mGalleryBase.gid, mGalleryBase.token, 0));
|
||||
|
||||
try {
|
||||
mCurrentHttpRequest = request;
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
String body = response.getString();
|
||||
SpiderParser.Result result = SpiderParser.parse(body, SpiderParser.REQUEST_ALL);
|
||||
spiderInfo = new SpiderInfo();
|
||||
spiderInfo.galleryBase = mGalleryBase;
|
||||
spiderInfo.pages = result.pages;
|
||||
spiderInfo.previewSize = result.previewSize;
|
||||
spiderInfo.previewPages = result.previewPages;
|
||||
spiderInfo.previewCountPerPage = result.tokens.size();
|
||||
spiderInfo.tokens = new AutoExpandArray<>(spiderInfo.pages);
|
||||
int size = result.tokens.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
spiderInfo.tokens.set(i, result.tokens.get(i));
|
||||
}
|
||||
|
||||
return spiderInfo;
|
||||
} finally {
|
||||
mCurrentHttpRequest = null;
|
||||
request.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private SpiderParser.Result getTokensFromInternet(int index, EhConfig ehConfig) throws Exception {
|
||||
int previewIndex = index / mSpiderInfo.previewCountPerPage;
|
||||
|
||||
EhHttpRequest request = new EhHttpRequest();
|
||||
request.setEhConfig(ehConfig);
|
||||
request.setUrl(EhUrl.getDetailUrl(mSource, mGalleryBase.gid, mGalleryBase.token, previewIndex));
|
||||
try {
|
||||
mCurrentHttpRequest = request;
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
String body = response.getString();
|
||||
return SpiderParser.parse(body, SpiderParser.REQUEST_PREVIEW);
|
||||
} finally {
|
||||
mCurrentHttpRequest = null;
|
||||
request.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void doToken(SpiderInfo spiderInfo, EhConfig ehConfig) throws Exception {
|
||||
Integer index;
|
||||
synchronized (mTokenLock) {
|
||||
index = mRequestTokens.poll();
|
||||
}
|
||||
|
||||
if (index == null) {
|
||||
// Can't get request index, just wait here
|
||||
synchronized (mQueenLock) {
|
||||
try {
|
||||
mQueenLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
// Go into a new loop
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if we has the token first
|
||||
String token;
|
||||
synchronized (mTokenLock) {
|
||||
token = spiderInfo.tokens.get(index);
|
||||
}
|
||||
if (token == null) {
|
||||
SpiderParser.Result result = null;
|
||||
Exception exception = null;
|
||||
|
||||
// Try twice
|
||||
int k = 2;
|
||||
while (k-- > 0) {
|
||||
try {
|
||||
result = getTokensFromInternet(index, ehConfig);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
synchronized (mTokenLock) {
|
||||
AutoExpandArray<String> tokens = spiderInfo.tokens;
|
||||
List<String> list = result.tokens;
|
||||
int start = result.startIndex;
|
||||
int end = start + list.size();
|
||||
if (index < start || index >= end) {
|
||||
// TODO Maybe need a way to handle it
|
||||
throw new Exception("Can't get no target index when find token");
|
||||
}
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
tokens.set(i, list.get(i - start));
|
||||
}
|
||||
}
|
||||
|
||||
// Write spider into file
|
||||
writeSpiderInfoToDataDir(spiderInfo);
|
||||
writeSpiderInfoToDownloadDir(spiderInfo);
|
||||
}
|
||||
|
||||
// Notify
|
||||
synchronized (mWorkerLock) {
|
||||
mWorkerLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleToken(SpiderInfo spiderInfo, EhConfig ehConfig) {
|
||||
while (!mStop) {
|
||||
try {
|
||||
doToken(spiderInfo, ehConfig);
|
||||
} catch (Exception e) {
|
||||
// Stop action, disconnect current HttpRequest may raise Exception
|
||||
// No need to report it
|
||||
if (mStop) {
|
||||
break;
|
||||
}
|
||||
|
||||
mRetry = false;
|
||||
mSpiderListener.onPartlyFailed(e);
|
||||
|
||||
while (!mStop && !mRetry) {
|
||||
synchronized (mQueenLock) {
|
||||
try {
|
||||
mQueenLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void doIt() throws Exception {
|
||||
Say.d(TAG, "Spider Queen do it");
|
||||
|
||||
EhConfig ehConfig = mHttpClient.getEhConfigClone();
|
||||
GalleryBase gb = mGalleryBase;
|
||||
SpiderInfo spiderInfo = null;
|
||||
SpiderInfo spiderInfo = mSpiderInfo;
|
||||
|
||||
// 1. get spider info from dir
|
||||
File spiderInfoFile = new File(mSpiderInfoDir, Integer.toString(gb.gid));
|
||||
if (spiderInfoFile.exists()) {
|
||||
try {
|
||||
spiderInfo = SpiderInfo.read(new FileInputStream(spiderInfoFile));
|
||||
if (gb.gid != spiderInfo.galleryBase.gid) {
|
||||
spiderInfo = null;
|
||||
throw new IllegalStateException("Gid not match in SpiderInfo file");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileUtils.delete(spiderInfoFile);
|
||||
}
|
||||
if (spiderInfo == null) { // If it is not null, may it is
|
||||
spiderInfo = readSpiderInfoFromDataDir();
|
||||
}
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. if can't get spider info from dir, get it from internet
|
||||
// 2. get spider info download dir
|
||||
if (spiderInfo == null) {
|
||||
// Use normal size to get more preview
|
||||
ehConfig.previewSize = EhConfig.PREVIEW_SIZE_NORMAL;
|
||||
ehConfig.setDirty();
|
||||
|
||||
EhHttpRequest request = new EhHttpRequest();
|
||||
request.setEhConfig(ehConfig);
|
||||
request.setUrl(EhUrl.getDetailUrl(mSource, gb.gid, gb.token, 0));
|
||||
try {
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
String body = response.getString();
|
||||
SpiderParser.Result result = SpiderParser.parse(body, SpiderParser.REQUEST_ALL);
|
||||
spiderInfo = new SpiderInfo();
|
||||
spiderInfo.galleryBase = gb;
|
||||
spiderInfo.pages = result.pages;
|
||||
spiderInfo.previewSize = result.previewSize;
|
||||
spiderInfo.previewPages = result.previewPages;
|
||||
spiderInfo.previewCountPerPage = result.tokens.size();
|
||||
spiderInfo.tokens = new AutoExpandArray<>(spiderInfo.pages);
|
||||
} catch (Exception e) {
|
||||
// TODO Add a listener to tell spider error
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} finally {
|
||||
request.disconnect();
|
||||
}
|
||||
spiderInfo = readSpiderInfoFromDownloadDir();
|
||||
}
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. get spider info internet
|
||||
if (spiderInfo == null) {
|
||||
// spiderInfo must be non null or throw Exception
|
||||
spiderInfo = getSpiderInfoFromInternet(ehConfig);
|
||||
}
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. write it to file
|
||||
writeSpiderInfoToDataDir(spiderInfo);
|
||||
writeSpiderInfoToDownloadDir(spiderInfo);
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Perpare
|
||||
// Update EhConfig preview size
|
||||
ehConfig.previewSize = spiderInfo.previewSize;
|
||||
// Write to file
|
||||
try {
|
||||
spiderInfo.write(new FileOutputStream(spiderInfoFile));
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
// Listener
|
||||
mSpiderListener.onGetPages(spiderInfo.pages);
|
||||
|
||||
// 3. prepare
|
||||
ehConfig.setDirty();
|
||||
mSpiderInfo = spiderInfo;
|
||||
mNextIndex = 0;
|
||||
mPageStates = new int[spiderInfo.pages];
|
||||
Arrays.fill(mPageStates, PAGE_STATE_NONE);
|
||||
// Init AtomicIntegerArray stuff
|
||||
int length = spiderInfo.pages;
|
||||
mPageStates = new AtomicIntegerArray(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
mPageStates.lazySet(i, PAGE_STATE_NONE);
|
||||
}
|
||||
mSpiderWorkers = new SpiderWorker[3];
|
||||
mImageHandler = new ImageHandler(gb, spiderInfo.pages, mMode);
|
||||
mImageHandler = new ImageHandler(gb, spiderInfo.pages, mMode, mDownloadDir);
|
||||
// Check stop
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Make Worker to do work
|
||||
// 6. Make workers and render to work
|
||||
ensureWorker();
|
||||
mRender = new Render();
|
||||
Thread thread = new Thread(mRender, "Render");
|
||||
thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
thread.start();
|
||||
|
||||
// 4. Get tokens
|
||||
while (!mStop) {
|
||||
Integer index;
|
||||
synchronized (mTokenLock) {
|
||||
index = mRequestTokens.poll();
|
||||
}
|
||||
// 7. tell listener pages
|
||||
mSpiderListener.onGetPages(spiderInfo.pages);
|
||||
|
||||
if (index == null) {
|
||||
// Can't get request index
|
||||
synchronized (mQueenLock) {
|
||||
try {
|
||||
mQueenLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we has the token first
|
||||
String token;
|
||||
synchronized (mTokenLock) {
|
||||
token = spiderInfo.tokens.get(index);
|
||||
}
|
||||
if (token == null) {
|
||||
SpiderParser.Result result;
|
||||
int previewIndex = index / spiderInfo.previewCountPerPage;
|
||||
EhHttpRequest request = new EhHttpRequest();
|
||||
request.setEhConfig(ehConfig);
|
||||
request.setUrl(EhUrl.getDetailUrl(mSource, gb.gid, gb.token, previewIndex));
|
||||
try {
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
String body = response.getString();
|
||||
result = SpiderParser.parse(body, SpiderParser.REQUEST_PREVIEW);
|
||||
} catch (Exception e) {
|
||||
// TODO Add a listener to tell spider error
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} finally {
|
||||
request.disconnect();
|
||||
}
|
||||
synchronized (mTokenLock) {
|
||||
AutoExpandArray<String> tokens = spiderInfo.tokens;
|
||||
List<String> list = result.tokens;
|
||||
int start = result.startIndex;
|
||||
int end = start + list.size();
|
||||
for (int i = start; i < end; i++) {
|
||||
tokens.set(i, list.get(i - start));
|
||||
}
|
||||
}
|
||||
// Write to file
|
||||
try {
|
||||
spiderInfo.write(new FileOutputStream(spiderInfoFile));
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
// Notify
|
||||
synchronized (mWorkerLock) {
|
||||
mWorkerLock.notifyAll();
|
||||
}
|
||||
}
|
||||
// 8. Get tokens
|
||||
handleToken(spiderInfo, ehConfig);
|
||||
|
||||
// 5. The queen dies, workers die, render dies
|
||||
mWorkerArrayLock.lock();
|
||||
|
||||
for (SpiderWorker worker : mSpiderWorkers) {
|
||||
if (worker != null) {
|
||||
worker.mStop = true;
|
||||
worker.stop();
|
||||
}
|
||||
}
|
||||
synchronized (mWorkerLock) {
|
||||
@@ -285,9 +551,6 @@ public class SpiderQueen implements Runnable {
|
||||
mWorkerArrayLock.unlock();
|
||||
|
||||
mRender.stop();
|
||||
|
||||
|
||||
Say.d(TAG, "Spider dies");
|
||||
}
|
||||
|
||||
public int pages() {
|
||||
@@ -298,13 +561,6 @@ public class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mStop = true;
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureWorker() {
|
||||
mWorkerArrayLock.lock();
|
||||
|
||||
@@ -320,8 +576,8 @@ public class SpiderQueen implements Runnable {
|
||||
mWorkerArrayLock.unlock();
|
||||
}
|
||||
|
||||
public void setNextIndex(int index) {
|
||||
synchronized (mPageLock) {
|
||||
private void setNextIndex(int index) {
|
||||
synchronized (mNextIndexLock) {
|
||||
if (mSpiderInfo != null && index < mSpiderInfo.pages) {
|
||||
mNextIndex = index;
|
||||
}
|
||||
@@ -333,49 +589,71 @@ public class SpiderQueen implements Runnable {
|
||||
String token = mSpiderInfo.tokens.get(index);
|
||||
if (token == null && !mRequestTokens.contains(index)) {
|
||||
mRequestTokens.offer(index);
|
||||
}
|
||||
// Notify Queen
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
|
||||
// Notify Queen
|
||||
synchronized (mQueenLock) {
|
||||
mQueenLock.notify();
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
private int getRequestIndex() {
|
||||
synchronized (mPageLock) {
|
||||
synchronized (mNextIndexLock) {
|
||||
int length = mSpiderInfo.pages;
|
||||
if (mNextIndex >= length) {
|
||||
if (!mHasCheckIndexAgain) {
|
||||
mHasCheckIndexAgain = true;
|
||||
mNextIndex = 0;
|
||||
|
||||
for (;;) {
|
||||
if (mNextIndex >= length) {
|
||||
if (!mHasCheckIndexAgain) {
|
||||
mHasCheckIndexAgain = true;
|
||||
mNextIndex = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int index = mNextIndex;
|
||||
if (mPageStates.get(mNextIndex++) == PAGE_STATE_NONE) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
while (mNextIndex < length && mPageStates[mNextIndex] != PAGE_STATE_NONE) {
|
||||
mNextIndex++;
|
||||
}
|
||||
|
||||
return mNextIndex >= length ? -1 : mNextIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
class SpiderWorker implements Runnable {
|
||||
|
||||
private final String TAG = SpiderWorker.class.getSimpleName();
|
||||
private String TAG = SpiderWorker.class.getSimpleName();
|
||||
|
||||
public int mIndex;
|
||||
|
||||
private transient HttpRequest mCurrentHttpRequest;
|
||||
private transient ImageHandler.SaveHelper mCurrentSaveHelper;
|
||||
|
||||
public boolean mStop = false;
|
||||
|
||||
public SpiderWorker(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
// Notify by Queen
|
||||
public void stop() {
|
||||
mStop = true;
|
||||
if (mCurrentHttpRequest != null) {
|
||||
mCurrentHttpRequest.disconnect();
|
||||
mCurrentHttpRequest = null;
|
||||
}
|
||||
if (mCurrentSaveHelper != null) {
|
||||
mCurrentSaveHelper.cancel();
|
||||
mCurrentSaveHelper = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getImageUrl(int gid, String token, int index) throws Exception {
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.setUrl(EhUrl.getPageUrl(mSource, mGalleryBase.gid, token, index));
|
||||
request.setUrl(EhUrl.getPageUrl(mSource, gid, token, index));
|
||||
try {
|
||||
mCurrentHttpRequest = request;
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
String body = response.getString();
|
||||
String imageUrl = GalleryPageParser.parse(body);
|
||||
@@ -387,36 +665,34 @@ public class SpiderQueen implements Runnable {
|
||||
}
|
||||
return imageUrl;
|
||||
} finally {
|
||||
mCurrentHttpRequest = null;
|
||||
request.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Say.d(TAG, "Spider worker starts");
|
||||
TAG = Thread.currentThread().getName();
|
||||
|
||||
Say.d(TAG, Thread.currentThread().getName() + " starts");
|
||||
|
||||
GalleryBase gb = mGalleryBase;
|
||||
ImageHandler handler = mImageHandler;
|
||||
|
||||
while (!mStop) {
|
||||
// Get index
|
||||
int index;
|
||||
synchronized (mPageLock) {
|
||||
index = getRequestIndex();
|
||||
if (index == -1) {
|
||||
// There is no more
|
||||
break;
|
||||
} else {
|
||||
mPageStates[index] = PAGE_STATE_DOWNLOADING;
|
||||
}
|
||||
final int index = getRequestIndex();
|
||||
if (index == -1) {
|
||||
// There is no more
|
||||
break;
|
||||
} else {
|
||||
mPageStates.lazySet(index, PAGE_STATE_SPIDER);
|
||||
}
|
||||
|
||||
// Check contain
|
||||
if (handler.contain(index)) {
|
||||
// Find it, no need to download
|
||||
synchronized (mPageLock) {
|
||||
mPageStates[index] = PAGE_STATE_SUCCEED;
|
||||
}
|
||||
mPageStates.lazySet(index, PAGE_STATE_SUCCEED);
|
||||
// Listener
|
||||
mSpiderListener.onSpiderSucceed(index);
|
||||
continue;
|
||||
@@ -446,42 +722,40 @@ public class SpiderQueen implements Runnable {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Get image url
|
||||
String imageUrl;
|
||||
try {
|
||||
imageUrl = getImageUrl(gb.gid, token, index);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// TODO notify 509 error
|
||||
synchronized (mPageLock) {
|
||||
mPageStates[index] = PAGE_STATE_FAILED;
|
||||
}
|
||||
mPageStates.lazySet(index, PAGE_STATE_FAILED);
|
||||
// Listener
|
||||
mSpiderListener.onSpiderFailed(index);
|
||||
mSpiderListener.onSpiderFailed(index, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download image
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.setUrl(imageUrl);
|
||||
try {
|
||||
HttpResponse response = mHttpClient.execute(request);
|
||||
handler.save(index, response);
|
||||
mCurrentSaveHelper = new ImageHandler.SaveHelper() {
|
||||
@Override
|
||||
public void onStartSaving(long totalSize) {
|
||||
mSpiderListener.onSpiderStart(index, totalSize);
|
||||
}
|
||||
|
||||
synchronized (mPageLock) {
|
||||
mPageStates[index] = PAGE_STATE_SUCCEED;
|
||||
}
|
||||
@Override
|
||||
public void onSave(long receivedSize) {
|
||||
mSpiderListener.onSpiderPage(index, receivedSize);
|
||||
}
|
||||
};
|
||||
handler.save(mHttpClient, imageUrl, index, mCurrentSaveHelper);
|
||||
mPageStates.set(index, PAGE_STATE_SUCCEED);
|
||||
// Listener
|
||||
mSpiderListener.onSpiderSucceed(index);
|
||||
} catch (Exception e) {
|
||||
synchronized (mPageLock) {
|
||||
mPageStates[index] = PAGE_STATE_FAILED;
|
||||
}
|
||||
mPageStates.set(index, PAGE_STATE_FAILED);
|
||||
// Listener
|
||||
mSpiderListener.onSpiderFailed(index);
|
||||
mSpiderListener.onSpiderFailed(index, e);
|
||||
} finally {
|
||||
request.disconnect();
|
||||
mCurrentSaveHelper = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +764,7 @@ public class SpiderQueen implements Runnable {
|
||||
mSpiderWorkers[mIndex] = null;
|
||||
mWorkerArrayLock.unlock();
|
||||
|
||||
Say.d(TAG, "Spider Worker dies");
|
||||
Say.d(TAG, Thread.currentThread().getName() + " dies");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,25 +811,7 @@ public class SpiderQueen implements Runnable {
|
||||
continue;
|
||||
}
|
||||
|
||||
int state;
|
||||
synchronized (mPageLock) {
|
||||
state = mPageStates[index];
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case PAGE_STATE_NONE:
|
||||
mSpiderListener.onGetNone(index);
|
||||
break;
|
||||
case PAGE_STATE_DOWNLOADING:
|
||||
mSpiderListener.onGetSpider(index, 0.5f); // TODO
|
||||
break;
|
||||
case PAGE_STATE_SUCCEED:
|
||||
mSpiderListener.onGetBitmap(index, mImageHandler == null ? null : mImageHandler.getBitmap(index));
|
||||
break;
|
||||
default:
|
||||
case PAGE_STATE_FAILED:
|
||||
mSpiderListener.onGetFailed(index);
|
||||
}
|
||||
mSpiderListener.onGetBitmap(index, mImageHandler == null ? null : mImageHandler.getBitmap(index));
|
||||
}
|
||||
|
||||
Say.d(TAG, "Render dies");
|
||||
@@ -564,20 +820,26 @@ public class SpiderQueen implements Runnable {
|
||||
|
||||
public interface SpiderListener {
|
||||
|
||||
void onTotallyFailed(Exception e);
|
||||
|
||||
void onPartlyFailed(Exception e);
|
||||
|
||||
/**
|
||||
* The whole gallery has been spidered
|
||||
* TODO
|
||||
*/
|
||||
void onDone();
|
||||
|
||||
void onGetPages(int pages);
|
||||
|
||||
void onSpiderPage(int index, float percent);
|
||||
void onSpiderStart(int index, long totalSize);
|
||||
|
||||
void onSpiderPage(int index, long receivedSize);
|
||||
|
||||
void onSpiderSucceed(int index);
|
||||
|
||||
void onSpiderFailed(int index);
|
||||
|
||||
void onGetNone(int index);
|
||||
void onSpiderFailed(int index, Exception e);
|
||||
|
||||
void onGetBitmap(int index, Bitmap bitmap);
|
||||
|
||||
void onGetSpider(int index, float percent);
|
||||
|
||||
void onGetFailed(int index);
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,7 @@ import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.Say;
|
||||
@@ -39,9 +36,8 @@ public class GalleryView extends RecyclerView {
|
||||
|
||||
private Mode mMode;
|
||||
private int mHalfInterval;
|
||||
private int mSize = 20;
|
||||
private boolean mNeedAdjustPosition;
|
||||
|
||||
private GalleryAdapter mAdapter;
|
||||
private GalleryLayoutManager mLayoutManager;
|
||||
|
||||
public enum Mode {
|
||||
@@ -70,8 +66,6 @@ public class GalleryView extends RecyclerView {
|
||||
|
||||
mLayoutManager = new GalleryLayoutManager(context);
|
||||
setLayoutManager(mLayoutManager);
|
||||
mAdapter = new GalleryAdapter();
|
||||
setAdapter(mAdapter);
|
||||
addItemDecoration(new IntervalDecoration());
|
||||
addOnScrollListener(new ScrollListener());
|
||||
setHasFixedSize(true);
|
||||
@@ -95,6 +89,9 @@ public class GalleryView extends RecyclerView {
|
||||
for (int i = 0; i < length; i++) {
|
||||
((RecyclerView.LayoutParams) getChildAt(i).getLayoutParams()).mInsetsDirty = true;
|
||||
}
|
||||
if (mode == Mode.LEFT_TO_RIGHT || mode == Mode.RIGHT_TO_LEFT) {
|
||||
mNeedAdjustPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,6 +103,16 @@ public class GalleryView extends RecyclerView {
|
||||
}
|
||||
}
|
||||
|
||||
public void scrollByEfficiently(int x, int y) {
|
||||
if (x != 0 || y != 0) {
|
||||
scrollBy(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private int size() {
|
||||
return getAdapter().getItemCount();
|
||||
}
|
||||
|
||||
// Only work for Mode.LEFT_TO_RIGHT and Mode.RIGHT_TO_LEFT
|
||||
private View findCenterXView() {
|
||||
int centerX = getWidth() / 2;
|
||||
@@ -141,7 +148,7 @@ public class GalleryView extends RecyclerView {
|
||||
if (mMode == Mode.TOP_TO_BOTTOM) {
|
||||
smoothScrollToPosition(page);
|
||||
} else {
|
||||
page = MathUtils.clamp(page, 0, mSize - 1);
|
||||
page = MathUtils.clamp(page, 0, size() - 1);
|
||||
try {
|
||||
int offsetX = getOffsetToPage(page);
|
||||
smoothScrollBy(offsetX, 0);
|
||||
@@ -156,10 +163,10 @@ public class GalleryView extends RecyclerView {
|
||||
if (mMode == Mode.TOP_TO_BOTTOM) {
|
||||
scrollToPosition(page);
|
||||
} else {
|
||||
page = MathUtils.clamp(page, 0, mSize - 1);
|
||||
page = MathUtils.clamp(page, 0, size() - 1);
|
||||
try {
|
||||
int offsetX = getOffsetToPage(page);
|
||||
scrollBy(offsetX, 0);
|
||||
scrollByEfficiently(offsetX, 0);
|
||||
} catch (Exception e) {
|
||||
Say.d(TAG, e.getMessage());
|
||||
scrollToPosition(page);
|
||||
@@ -180,36 +187,6 @@ public class GalleryView extends RecyclerView {
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private ImageView imageView;
|
||||
|
||||
public GalleryHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
imageView = (ImageView) itemView;
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
|
||||
@Override
|
||||
public GalleryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new GalleryHolder(new ImageView(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(GalleryHolder holder, int position) {
|
||||
holder.imageView.setImageDrawable(getResources().getDrawable(position == 0 ?
|
||||
R.drawable.ic_heart : R.drawable.ic_sad));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryLayoutManager extends LinearLayoutManager {
|
||||
|
||||
public GalleryLayoutManager(Context context) {
|
||||
@@ -240,8 +217,12 @@ public class GalleryView extends RecyclerView {
|
||||
super.onLayoutChildren(recycler, state);
|
||||
|
||||
// Ensure page is in center
|
||||
if (mMode == Mode.LEFT_TO_RIGHT || mMode == Mode.RIGHT_TO_LEFT) {
|
||||
scrollToPage(getCurrentPage());
|
||||
if (mNeedAdjustPosition) {
|
||||
mNeedAdjustPosition = false;
|
||||
int currentPage = getCurrentPage();
|
||||
if (currentPage != RecyclerView.NO_POSITION) {
|
||||
scrollToPage(currentPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,10 +255,10 @@ public class GalleryView extends RecyclerView {
|
||||
int width = getWidth();
|
||||
if (left > interval &&
|
||||
((mMode == Mode.LEFT_TO_RIGHT && mCurrentPage > 0) ||
|
||||
(mMode == Mode.RIGHT_TO_LEFT && mCurrentPage < mSize - 1))) { // Scroll left
|
||||
(mMode == Mode.RIGHT_TO_LEFT && mCurrentPage < size() - 1))) { // Scroll left
|
||||
scrollX = -width - interval + left;
|
||||
} else if (left < -interval &&
|
||||
((mMode == Mode.LEFT_TO_RIGHT && mCurrentPage < mSize - 1) ||
|
||||
((mMode == Mode.LEFT_TO_RIGHT && mCurrentPage < size() - 1) ||
|
||||
(mMode == Mode.RIGHT_TO_LEFT && mCurrentPage > 0))) { // Scroll right
|
||||
scrollX = width + interval + left;
|
||||
} else {
|
||||
@@ -302,7 +283,7 @@ public class GalleryView extends RecyclerView {
|
||||
} else {
|
||||
outRect.left = mHalfInterval;
|
||||
}
|
||||
if (index == mSize - 1) {
|
||||
if (index == size() - 1) {
|
||||
outRect.right = 0;
|
||||
} else {
|
||||
outRect.right = mHalfInterval;
|
||||
@@ -315,7 +296,7 @@ public class GalleryView extends RecyclerView {
|
||||
} else {
|
||||
outRect.right = mHalfInterval;
|
||||
}
|
||||
if (index == mSize - 1) {
|
||||
if (index == size() - 1) {
|
||||
outRect.left = 0;
|
||||
} else {
|
||||
outRect.left = mHalfInterval;
|
||||
@@ -328,7 +309,7 @@ public class GalleryView extends RecyclerView {
|
||||
} else {
|
||||
outRect.top = mHalfInterval;
|
||||
}
|
||||
if (index == mSize - 1) {
|
||||
if (index == size() - 1) {
|
||||
outRect.bottom = 0;
|
||||
} else {
|
||||
outRect.bottom = mHalfInterval;
|
||||
|
||||
@@ -16,17 +16,250 @@
|
||||
|
||||
package com.hippo.ehviewer.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.data.GalleryBase;
|
||||
import com.hippo.ehviewer.gallery.GalleryProvider;
|
||||
import com.hippo.ehviewer.gallery.GalleryProviderListener;
|
||||
import com.hippo.ehviewer.gallery.GallerySpider;
|
||||
import com.hippo.ehviewer.gallery.ImageHandler;
|
||||
import com.hippo.ehviewer.gallery.widget.GalleryView;
|
||||
import com.hippo.util.SystemUiHelper;
|
||||
import com.hippo.widget.ProgressView;
|
||||
import com.hippo.yorozuya.Say;
|
||||
|
||||
public class GalleryActivity extends AppCompatActivity {
|
||||
import java.io.IOException;
|
||||
|
||||
// TODO Adjust RecyclerView recycle logic or Create a bitmap map here
|
||||
public class GalleryActivity extends AppCompatActivity implements GalleryProviderListener {
|
||||
|
||||
public static final String KEY_GALLERY_BASE = "gallery_base";
|
||||
|
||||
private SystemUiHelper mSystemUiHelper;
|
||||
|
||||
private GalleryView mGalleryView;
|
||||
|
||||
private GalleryAdapter mAdapter;
|
||||
|
||||
private GallerySpider mGallerySpider;
|
||||
|
||||
private GalleryBase mGalleryBase;
|
||||
|
||||
private int mSize;
|
||||
|
||||
private boolean mReady = false;
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
if (intent == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mGalleryBase = intent.getParcelableExtra(KEY_GALLERY_BASE);
|
||||
if (mGalleryBase == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mGallerySpider = GallerySpider.obtain(mGalleryBase, ImageHandler.Mode.READ);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e); // TODO
|
||||
}
|
||||
mGallerySpider.addGalleryProviderListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mSystemUiHelper = new SystemUiHelper(this, SystemUiHelper.LEVEL_IMMERSIVE,
|
||||
SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES | SystemUiHelper.FLAG_IMMERSIVE_STICKY);
|
||||
mSystemUiHelper.hide();
|
||||
|
||||
setContentView(R.layout.activity_gallery);
|
||||
|
||||
mGalleryView = (GalleryView) findViewById(R.id.gallery_view);
|
||||
|
||||
mAdapter = new GalleryAdapter();
|
||||
mGalleryView.setAdapter(mAdapter);
|
||||
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
mGallerySpider.removeGalleryProviderListener(this);
|
||||
if (mGallerySpider != null) {
|
||||
GallerySpider.release(mGallerySpider);
|
||||
mGallerySpider = null;
|
||||
}
|
||||
// TODO free all bitmap in ImageView
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTotallyFailed(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartlyFailed(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetSize(int size) {
|
||||
|
||||
Say.d("TAG", "size = " + size);
|
||||
|
||||
mReady = true;
|
||||
mSize = size;
|
||||
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetBitmap(int index, @Nullable Bitmap bitmap) {
|
||||
GalleryHolder holder = (GalleryHolder) mGalleryView.findViewHolderForAdapterPosition(index);
|
||||
if (holder == null) {
|
||||
mGallerySpider.releaseBitmap(bitmap);
|
||||
} else {
|
||||
if (bitmap == null) {
|
||||
clearDrawable(holder.imageView, getResources().getDrawable(R.drawable.ic_sad));
|
||||
holder.imageView.setVisibility(View.VISIBLE);
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
} else {
|
||||
clearDrawable(holder.imageView, new BitmapDrawable(getResources(), bitmap));
|
||||
holder.imageView.setVisibility(View.VISIBLE);
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPagePercent(int index, float percent) {
|
||||
GalleryHolder holder = (GalleryHolder) mGalleryView.findViewHolderForAdapterPosition(index);
|
||||
if (holder != null) {
|
||||
clearDrawable(holder.imageView, null);
|
||||
holder.imageView.setVisibility(View.GONE);
|
||||
ProgressView progressView = holder.progressView;
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
progressView.setIndeterminate(false);
|
||||
progressView.setProgress(percent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSucceed(int index) {
|
||||
GalleryHolder holder = (GalleryHolder) mGalleryView.findViewHolderForAdapterPosition(index);
|
||||
if (holder != null) {
|
||||
bind(holder, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFailed(int index, Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
GalleryHolder holder = (GalleryHolder) mGalleryView.findViewHolderForAdapterPosition(index);
|
||||
if (holder != null) {
|
||||
clearDrawable(holder.imageView, null);
|
||||
holder.imageView.setVisibility(View.VISIBLE);
|
||||
holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_sad)); // TODO
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void bind(GalleryHolder holder, int index) {
|
||||
clearDrawable(holder.imageView, null);
|
||||
|
||||
if (mReady) {
|
||||
Object result = mGallerySpider.request(index);
|
||||
if (result instanceof Float) {
|
||||
holder.imageView.setVisibility(View.GONE);
|
||||
ProgressView progressView = holder.progressView;
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
progressView.setIndeterminate(false);
|
||||
progressView.setProgress((Float) result);
|
||||
|
||||
} else if (result == GalleryProvider.RESULT_NONE) {
|
||||
holder.imageView.setVisibility(View.GONE);
|
||||
ProgressView progressView = holder.progressView;
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
progressView.setIndeterminate(true);
|
||||
|
||||
} else if (result == GalleryProvider.RESULT_WAIT) {
|
||||
holder.imageView.setVisibility(View.GONE);
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
|
||||
} else if (result == GalleryProvider.RESULT_FAILED) {
|
||||
holder.imageView.setVisibility(View.VISIBLE);
|
||||
holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_sad)); // TODO
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
|
||||
} else {
|
||||
holder.imageView.setVisibility(View.VISIBLE);
|
||||
holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_heart)); // TODO
|
||||
holder.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
} else {
|
||||
holder.imageView.setVisibility(View.GONE);
|
||||
holder.progressView.setVisibility(View.VISIBLE);
|
||||
holder.progressView.setIndeterminate(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDrawable(ImageView imageView, Drawable drawable) {
|
||||
Drawable oldDrawable = imageView.getDrawable();
|
||||
if (oldDrawable instanceof BitmapDrawable) {
|
||||
mGallerySpider.releaseBitmap(((BitmapDrawable) oldDrawable).getBitmap());
|
||||
}
|
||||
imageView.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
class GalleryHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public ImageView imageView;
|
||||
public ProgressView progressView;
|
||||
|
||||
public GalleryHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
imageView = (ImageView) itemView.findViewById(R.id.image);
|
||||
progressView = (ProgressView) itemView.findViewById(R.id.progress);
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryAdapter extends RecyclerView.Adapter<GalleryHolder> {
|
||||
|
||||
@Override
|
||||
public GalleryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new GalleryHolder(getLayoutInflater().inflate(
|
||||
R.layout.item_gallery, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(GalleryHolder holder, int position) {
|
||||
bind(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mReady ? mSize : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,6 +281,7 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
|
||||
|
||||
if (v == mRead) {
|
||||
Intent intent = new Intent(getStageActivity(), GalleryActivity.class);
|
||||
intent.putExtra(GalleryActivity.KEY_GALLERY_BASE, mGalleryDetail);
|
||||
getStageActivity().startActivity(intent);
|
||||
} else if (v == mDownload) {
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import com.hippo.ehviewer.client.data.GalleryBase;
|
||||
import com.hippo.ehviewer.dao.DaoMaster;
|
||||
import com.hippo.ehviewer.dao.DaoSession;
|
||||
import com.hippo.ehviewer.dao.DirnameObj;
|
||||
import com.hippo.ehviewer.dao.DirnameObjDao;
|
||||
import com.hippo.ehviewer.dao.GalleryBaseObj;
|
||||
import com.hippo.ehviewer.dao.GalleryBaseObjDao;
|
||||
import com.hippo.ehviewer.dao.SpiderObj;
|
||||
|
||||
public class DBUtils {
|
||||
|
||||
@@ -38,14 +39,6 @@ public class DBUtils {
|
||||
sDaoSession = daoMaster.newSession();
|
||||
}
|
||||
|
||||
public static SpiderObj getSpiderObj(int gid) {
|
||||
return sDaoSession.getSpiderObjDao().load((long) gid);
|
||||
}
|
||||
|
||||
public static void insertSpiderObj(SpiderObj spiderObj) {
|
||||
sDaoSession.getSpiderObjDao().insert(spiderObj);
|
||||
}
|
||||
|
||||
public static void addGalleryBase(GalleryBase galleryBase) {
|
||||
GalleryBaseObjDao galleryBaseObjDao = sDaoSession.getGalleryBaseObjDao();
|
||||
GalleryBaseObj galleryBaseObj = galleryBaseObjDao.load((long) galleryBase.gid);
|
||||
@@ -82,4 +75,36 @@ public class DBUtils {
|
||||
galleryBaseObjDao.update(galleryBaseObj);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDirname(int gid) {
|
||||
DirnameObjDao dirnameObjDao = sDaoSession.getDirnameObjDao();
|
||||
DirnameObj dirnameObj = dirnameObjDao.load((long) gid);
|
||||
if (dirnameObj != null) {
|
||||
// Touch the date
|
||||
dirnameObj.setTime(System.currentTimeMillis());
|
||||
dirnameObjDao.update(dirnameObj);
|
||||
return dirnameObj.getDirname();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addDirname(GalleryBase galleryBase, String dirname) {
|
||||
addGalleryBase(galleryBase);
|
||||
|
||||
DirnameObj dirnameObj = new DirnameObj();
|
||||
dirnameObj.setGid((long) galleryBase.gid);
|
||||
dirnameObj.setDirname(dirname);
|
||||
dirnameObj.setTime(System.currentTimeMillis());
|
||||
sDaoSession.getDirnameObjDao().insert(dirnameObj);
|
||||
}
|
||||
|
||||
public static void touchDirname(int gid) {
|
||||
DirnameObjDao dirnameObjDao = sDaoSession.getDirnameObjDao();
|
||||
DirnameObj dirnameObj = dirnameObjDao.load((long) gid);
|
||||
if (dirnameObj != null) {
|
||||
dirnameObj.setTime(System.currentTimeMillis());
|
||||
dirnameObjDao.update(dirnameObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ import com.hippo.ehviewer.BuildConfig;
|
||||
import com.hippo.ehviewer.client.EhConfig;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public final class Settings {
|
||||
|
||||
@@ -192,7 +195,9 @@ public final class Settings {
|
||||
|
||||
|
||||
public static UniFile getDownloadPath() {
|
||||
return UniFile.fromFile(AppConfig.getDownloadDir());
|
||||
File file = AppConfig.getDownloadDir();
|
||||
FileUtils.ensureDirectory(file);
|
||||
return UniFile.fromFile(file);
|
||||
}
|
||||
|
||||
/****** Advance ******/
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.network;
|
||||
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.hippo.httpclient.HttpClient;
|
||||
import com.hippo.httpclient.HttpRequest;
|
||||
import com.hippo.httpclient.HttpResponse;
|
||||
import com.hippo.io.UniFileOutputStreamPipe;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.Utilities;
|
||||
import com.hippo.yorozuya.io.OutputStreamPipe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class DownloadClient {
|
||||
|
||||
private static long transferData(InputStream in, OutputStream out, OnDownloadListener listener)
|
||||
throws Exception {
|
||||
final byte data[] = new byte[1024 * 4];
|
||||
long receivedSize = 0;
|
||||
|
||||
while (true) {
|
||||
int bytesRead = in.read(data);
|
||||
if (bytesRead == -1) {
|
||||
break;
|
||||
}
|
||||
out.write(data, 0, bytesRead);
|
||||
receivedSize += bytesRead;
|
||||
if (listener != null) {
|
||||
listener.onDonwlad(receivedSize);
|
||||
}
|
||||
}
|
||||
|
||||
return receivedSize;
|
||||
}
|
||||
|
||||
public static void execute(DownloadRequest request) {
|
||||
OnDownloadListener listener = request.mListener;
|
||||
HttpClient httpClient = request.mHttpClient;
|
||||
HttpRequest httpRequest = request.mHttpRequest;
|
||||
if (httpRequest == null) {
|
||||
httpRequest = new HttpRequest();
|
||||
request.mHttpRequest = httpRequest;
|
||||
}
|
||||
try {
|
||||
httpRequest.setUrl(new URL(request.mUrl));
|
||||
} catch (MalformedURLException e) {
|
||||
// Listener
|
||||
if (listener != null) {
|
||||
listener.onFailed(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
OutputStreamPipe osPipe = null;
|
||||
try {
|
||||
// Listener
|
||||
if (listener != null) {
|
||||
listener.onStartDownloading();
|
||||
}
|
||||
|
||||
HttpResponse httpResponse = httpClient.execute(httpRequest);
|
||||
|
||||
osPipe = request.mOSPipe;
|
||||
if (osPipe == null) {
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(httpResponse.getContentType());
|
||||
if (extension == null) {
|
||||
extension = MimeTypeMap.getFileExtensionFromUrl(request.mUrl);
|
||||
}
|
||||
String name = Utilities.getNameFromUrl(request.mUrl);
|
||||
String filename;
|
||||
if (listener != null) {
|
||||
filename = listener.onFixname(name, extension, request.mFilename);
|
||||
} else {
|
||||
filename = request.mFilename;
|
||||
}
|
||||
request.mFilename = filename;
|
||||
|
||||
// TODO use download file name then rename
|
||||
UniFile uniFile = request.mDir.createDirectory(filename);
|
||||
if (uniFile == null) {
|
||||
// Listener
|
||||
if (listener != null) {
|
||||
listener.onFailed(new IOException("Can't create file " + filename));
|
||||
}
|
||||
return;
|
||||
}
|
||||
osPipe = new UniFileOutputStreamPipe(uniFile);
|
||||
}
|
||||
osPipe.obtain();
|
||||
|
||||
long contentLength = httpResponse.getContentLength();
|
||||
|
||||
// Listener
|
||||
if (listener != null) {
|
||||
listener.onConnect(contentLength);
|
||||
}
|
||||
|
||||
long receivedSize = transferData(httpResponse.getInputStream(), osPipe.open(), listener);
|
||||
|
||||
// TODO check contentLength == receivedSize
|
||||
|
||||
// Listener
|
||||
if (listener != null) {
|
||||
listener.onSucceed();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// remove download failed file
|
||||
if (request.mFilename != null && request.mDir != null) {
|
||||
UniFile file = request.mDir.findFile(request.mFilename);
|
||||
if (file != null) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.onFailed(e);
|
||||
}
|
||||
} finally {
|
||||
httpRequest.disconnect();
|
||||
if (osPipe != null) {
|
||||
osPipe.close();
|
||||
osPipe.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleDownloadListener implements OnDownloadListener {
|
||||
|
||||
@Override
|
||||
public void onStartDownloading() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onFixname(String newFilename, String newExtension, String oldFilename) {
|
||||
return oldFilename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(long totalSize) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDonwlad(long receivedSize) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Exception e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSucceed() {
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnDownloadListener {
|
||||
|
||||
void onStartDownloading();
|
||||
|
||||
String onFixname(String filename, String extension, String oldFilename);
|
||||
|
||||
void onConnect(long totalSize);
|
||||
|
||||
void onDonwlad(long receivedSize);
|
||||
|
||||
void onFailed(Exception e);
|
||||
|
||||
void onSucceed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.network;
|
||||
|
||||
import com.hippo.httpclient.HttpClient;
|
||||
import com.hippo.httpclient.HttpRequest;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.io.OutputStreamPipe;
|
||||
|
||||
public class DownloadRequest {
|
||||
|
||||
HttpClient mHttpClient;
|
||||
HttpRequest mHttpRequest;
|
||||
String mUrl;
|
||||
UniFile mDir;
|
||||
String mFilename;
|
||||
OutputStreamPipe mOSPipe;
|
||||
DownloadClient.OnDownloadListener mListener;
|
||||
|
||||
public void setHttpClient(HttpClient httpClient) {
|
||||
mHttpClient = httpClient;
|
||||
}
|
||||
|
||||
public void setHttpRequest(HttpRequest httpRequest) {
|
||||
mHttpRequest = httpRequest;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
mUrl = url;
|
||||
}
|
||||
|
||||
public void setDir(UniFile dir) {
|
||||
mDir = dir;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
mFilename = filename;
|
||||
}
|
||||
|
||||
public void setOSPipe(OutputStreamPipe OSPipe) {
|
||||
mOSPipe = OSPipe;
|
||||
}
|
||||
|
||||
public void setListener(DownloadClient.OnDownloadListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (mHttpRequest != null) {
|
||||
mHttpRequest.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Helper for controlling the visibility of the System UI across the various API levels. To use
|
||||
* this API, instantiate an instance of this class with the required level. The level specifies the
|
||||
* extent to which the System UI's visibility is changed when you call {@link #hide()}
|
||||
* or {@link #toggle()}.
|
||||
*/
|
||||
public final class SystemUiHelper {
|
||||
|
||||
/**
|
||||
* In this level, the helper will toggle low profile mode.
|
||||
*/
|
||||
public static final int LEVEL_LOW_PROFILE = 0;
|
||||
|
||||
/**
|
||||
* In this level, the helper will toggle the visibility of the status bar.
|
||||
* If there is a navigation bar, it will toggle low profile mode.
|
||||
*/
|
||||
public static final int LEVEL_HIDE_STATUS_BAR = 1;
|
||||
|
||||
/**
|
||||
* In this level, the helper will toggle the visibility of the navigation bar
|
||||
* (if present and if possible) and status bar. In cases where the navigation
|
||||
* bar is present but cannot be hidden, it will toggle low profile mode.
|
||||
*/
|
||||
public static final int LEVEL_LEAN_BACK = 2;
|
||||
|
||||
/**
|
||||
* In this level, the helper will toggle the visibility of the navigation bar
|
||||
* (if present and if possible) and status bar, in an immersive mode. This means that the app
|
||||
* will continue to receive all touch events. The user can reveal the system bars with an
|
||||
* inward swipe along the region where the system bars normally appear.
|
||||
*
|
||||
* <p>The {@link #FLAG_IMMERSIVE_STICKY} flag can be used to control how the system bars are
|
||||
* displayed.
|
||||
*/
|
||||
public static final int LEVEL_IMMERSIVE = 3;
|
||||
|
||||
/**
|
||||
* When this flag is set, the
|
||||
* {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}
|
||||
* flag will be set on older devices, making the status bar "float" on top
|
||||
* of the activity layout. This is most useful when there are no controls at
|
||||
* the top of the activity layout.
|
||||
* <p>
|
||||
* This flag isn't used on newer devices because the <a
|
||||
* href="http://developer.android.com/design/patterns/actionbar.html">action
|
||||
* bar</a>, the most important structural element of an Android app, should
|
||||
* be visible and not obscured by the system UI.
|
||||
*/
|
||||
public static final int FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES = 0x1;
|
||||
|
||||
/**
|
||||
* Used with {@link #LEVEL_IMMERSIVE}. When this flag is set, an inward swipe in the system
|
||||
* bars areas will cause the system bars to temporarily appear in a semi-transparent state,
|
||||
* but no flags are cleared, and your system UI visibility change listeners are not triggered.
|
||||
* The bars automatically hide again after a short delay, or if the user interacts with the
|
||||
* middle of the screen.
|
||||
*/
|
||||
public static final int FLAG_IMMERSIVE_STICKY = 0x2;
|
||||
|
||||
private static final String LOG_TAG = SystemUiHelper.class.getSimpleName();
|
||||
|
||||
private final SystemUiHelperImpl mImpl;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final Runnable mHideRunnable;
|
||||
|
||||
/**
|
||||
* Construct a new SystemUiHelper.
|
||||
*
|
||||
* @param activity The Activity who's system UI should be changed
|
||||
* @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE},
|
||||
* {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or
|
||||
* {@link #LEVEL_IMMERSIVE}
|
||||
* @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and
|
||||
* {@link #FLAG_IMMERSIVE_STICKY}
|
||||
*/
|
||||
public SystemUiHelper(Activity activity, int level, int flags) {
|
||||
this(activity, level, flags, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new SystemUiHelper.
|
||||
*
|
||||
* @param activity The Activity who's system UI should be changed
|
||||
* @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE},
|
||||
* {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or
|
||||
* {@link #LEVEL_IMMERSIVE}
|
||||
* @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and
|
||||
* {@link #FLAG_IMMERSIVE_STICKY}
|
||||
* @param listener A listener which is called when the system visibility is changed
|
||||
*/
|
||||
public SystemUiHelper(Activity activity, int level, int flags,
|
||||
OnVisibilityChangeListener listener) {
|
||||
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mHideRunnable = new HideRunnable();
|
||||
|
||||
// Create impl
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mImpl = new SystemUiHelperImplKK(activity, level, flags, listener);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
mImpl = new SystemUiHelperImplJB(activity, level, flags, listener);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
mImpl = new SystemUiHelperImplICS(activity, level, flags, listener);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mImpl = new SystemUiHelperImplHC(activity, level, flags, listener);
|
||||
} else {
|
||||
mImpl = new SystemUiHelperImplBase(activity, level, flags, listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the system UI is currently showing. What this means depends on the mode this
|
||||
* {@link SystemUiHelper} was instantiated with.
|
||||
*/
|
||||
public boolean isShowing() {
|
||||
return mImpl.isShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the system UI. What this means depends on the mode this {@link SystemUiHelper} was
|
||||
* instantiated with.
|
||||
*
|
||||
* <p>Any currently queued delayed hide requests will be removed.
|
||||
*/
|
||||
public void show() {
|
||||
// Ensure that any currently queued hide calls are removed
|
||||
removeQueuedRunnables();
|
||||
|
||||
mImpl.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the system UI. What this means depends on the mode this {@link SystemUiHelper} was
|
||||
* instantiated with.
|
||||
*
|
||||
* <p>Any currently queued delayed hide requests will be removed.
|
||||
*/
|
||||
public void hide() {
|
||||
// Ensure that any currently queued hide calls are removed
|
||||
removeQueuedRunnables();
|
||||
|
||||
mImpl.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that the system UI is hidden after a delay.
|
||||
*
|
||||
* <p>Any currently queued delayed hide requests will be removed.
|
||||
*
|
||||
* @param delayMillis The delay (in milliseconds) until the Runnable
|
||||
* will be executed.
|
||||
*/
|
||||
public void delayHide(long delayMillis) {
|
||||
// Ensure that any currently queued hide calls are removed
|
||||
removeQueuedRunnables();
|
||||
|
||||
mHandler.postDelayed(mHideRunnable, delayMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle whether the system UI is displayed.
|
||||
*/
|
||||
public void toggle() {
|
||||
if (mImpl.isShowing()) {
|
||||
mImpl.hide();
|
||||
} else {
|
||||
mImpl.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeQueuedRunnables() {
|
||||
// Ensure that any currently queued hide calls are removed
|
||||
mHandler.removeCallbacks(mHideRunnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback interface used to listen for system UI visibility changes.
|
||||
*/
|
||||
public interface OnVisibilityChangeListener {
|
||||
/**
|
||||
* Called when the system UI visibility has changed.
|
||||
*
|
||||
* @param visible True if the system UI is visible.
|
||||
*/
|
||||
void onVisibilityChange(boolean visible);
|
||||
}
|
||||
|
||||
static abstract class SystemUiHelperImpl {
|
||||
|
||||
final Activity mActivity;
|
||||
final int mLevel;
|
||||
final int mFlags;
|
||||
final OnVisibilityChangeListener mOnVisibilityChangeListener;
|
||||
|
||||
boolean mIsShowing = true;
|
||||
|
||||
SystemUiHelperImpl(Activity activity, int level, int flags,
|
||||
OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
mActivity = activity;
|
||||
mLevel = level;
|
||||
mFlags = flags;
|
||||
mOnVisibilityChangeListener = onVisibilityChangeListener;
|
||||
}
|
||||
|
||||
abstract void show();
|
||||
abstract void hide();
|
||||
|
||||
boolean isShowing() {
|
||||
return mIsShowing;
|
||||
}
|
||||
|
||||
void setIsShowing(boolean isShowing) {
|
||||
mIsShowing = isShowing;
|
||||
|
||||
if (mOnVisibilityChangeListener != null) {
|
||||
mOnVisibilityChangeListener.onVisibilityChange(mIsShowing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation. Used on API level 10 and below.
|
||||
*/
|
||||
static class SystemUiHelperImplBase extends SystemUiHelperImpl {
|
||||
|
||||
SystemUiHelperImplBase(Activity activity, int level, int flags,
|
||||
OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
super(activity, level, flags, onVisibilityChangeListener);
|
||||
|
||||
if ((mFlags & SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) != 0) {
|
||||
mActivity.getWindow().addFlags(
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void show() {
|
||||
if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) {
|
||||
mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
setIsShowing(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void hide() {
|
||||
if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) {
|
||||
mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
setIsShowing(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class HideRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl
|
||||
implements View.OnSystemUiVisibilityChangeListener {
|
||||
|
||||
final View mDecorView;
|
||||
|
||||
SystemUiHelperImplHC(Activity activity, int level, int flags,
|
||||
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
super(activity, level, flags, onVisibilityChangeListener);
|
||||
|
||||
mDecorView = activity.getWindow().getDecorView();
|
||||
mDecorView.setOnSystemUiVisibilityChangeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void show() {
|
||||
mDecorView.setSystemUiVisibility(createShowFlags());
|
||||
}
|
||||
|
||||
@Override
|
||||
void hide() {
|
||||
mDecorView.setSystemUiVisibility(createHideFlags());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onSystemUiVisibilityChange(int visibility) {
|
||||
if ((visibility & createTestFlags()) != 0) {
|
||||
onSystemUiHidden();
|
||||
} else {
|
||||
onSystemUiShown();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSystemUiShown() {
|
||||
ActionBar ab = mActivity.getActionBar();
|
||||
if (ab != null) {
|
||||
ab.show();
|
||||
}
|
||||
|
||||
mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
setIsShowing(true);
|
||||
}
|
||||
|
||||
protected void onSystemUiHidden() {
|
||||
ActionBar ab = mActivity.getActionBar();
|
||||
if (ab != null) {
|
||||
ab.hide();
|
||||
}
|
||||
|
||||
mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
setIsShowing(false);
|
||||
}
|
||||
|
||||
protected int createShowFlags() {
|
||||
return View.STATUS_BAR_VISIBLE;
|
||||
}
|
||||
|
||||
protected int createHideFlags() {
|
||||
return View.STATUS_BAR_HIDDEN;
|
||||
}
|
||||
|
||||
protected int createTestFlags() {
|
||||
return View.STATUS_BAR_HIDDEN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
class SystemUiHelperImplICS extends SystemUiHelperImplHC {
|
||||
|
||||
SystemUiHelperImplICS(Activity activity, int level, int flags,
|
||||
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
super(activity, level, flags, onVisibilityChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createShowFlags() {
|
||||
return View.SYSTEM_UI_FLAG_VISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createTestFlags() {
|
||||
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
|
||||
// Intentionally override test flags.
|
||||
return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
}
|
||||
|
||||
return View.SYSTEM_UI_FLAG_LOW_PROFILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createHideFlags() {
|
||||
int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
|
||||
|
||||
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
|
||||
flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
class SystemUiHelperImplJB extends SystemUiHelperImplICS {
|
||||
|
||||
SystemUiHelperImplJB(Activity activity, int level, int flags,
|
||||
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
super(activity, level, flags, onVisibilityChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createShowFlags() {
|
||||
int flag = super.createShowFlags();
|
||||
|
||||
if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) {
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
|
||||
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createHideFlags() {
|
||||
int flag = super.createHideFlags();
|
||||
|
||||
if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) {
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
|
||||
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
|
||||
flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSystemUiShown() {
|
||||
if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
|
||||
// Manually show the action bar when in low profile mode.
|
||||
ActionBar ab = mActivity.getActionBar();
|
||||
if (ab != null) {
|
||||
ab.show();
|
||||
}
|
||||
}
|
||||
|
||||
setIsShowing(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSystemUiHidden() {
|
||||
if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
|
||||
// Manually hide the action bar when in low profile mode.
|
||||
ActionBar ab = mActivity.getActionBar();
|
||||
if (ab != null) {
|
||||
ab.hide();
|
||||
}
|
||||
}
|
||||
|
||||
setIsShowing(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
class SystemUiHelperImplKK extends SystemUiHelperImplJB {
|
||||
|
||||
SystemUiHelperImplKK(Activity activity, int level, int flags,
|
||||
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
|
||||
super(activity, level, flags, onVisibilityChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int createHideFlags() {
|
||||
int flag = super.createHideFlags();
|
||||
|
||||
if (mLevel == SystemUiHelper.LEVEL_IMMERSIVE) {
|
||||
// If the client requested immersive mode, and we're on Android 4.4
|
||||
// or later, add relevant flags. Applying HIDE_NAVIGATION without
|
||||
// IMMERSIVE prevents the activity from accepting all touch events,
|
||||
// so we only do this on Android 4.4 and later (where IMMERSIVE is
|
||||
// present).
|
||||
flag |= ((mFlags & SystemUiHelper.FLAG_IMMERSIVE_STICKY) != 0)
|
||||
? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
: View.SYSTEM_UI_FLAG_IMMERSIVE;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user