完善阅读界面

This commit is contained in:
seven332 committed 2014-08-20 17:16:43 +08:00
1 parent a2bbe855e8
commit e451db6032
16 files changed
+822 -88

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+68 -2
View File
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -25,4 +25,70 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<com.hippo.ehviewer.widget.TextClock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginTop="12dp"
android:layout_marginRight="12dp"
android:textColor="@color/secondary_text_light" />
<LinearLayout
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="top"
android:background="@color/background_little_dark"
android:orientation="horizontal"
android:clickable="true"
android:visibility="gone" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="24.0dp"
android:layout_marginRight="24.0dp"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/secondary_text_light"
android:textSize="@dimen/text_large" />
</LinearLayout>
<LinearLayout
android:id="@+id/nav_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:background="@color/background_little_dark"
android:orientation="horizontal"
android:clickable="true"
android:visibility="gone" >
<SeekBar
android:id="@+id/page_seeker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_gravity="center" />
</LinearLayout>
<TextView
android:id="@+id/seeker_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="52dp"
android:ellipsize="start"
android:singleLine="true"
android:textColor="@color/secondary_text_light"
android:textSize="@dimen/text_large"
android:visibility="gone"
android:background="@drawable/seeker_bubble" />
</FrameLayout>
+27
View File
@@ -26,6 +26,7 @@
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/content" />
@@ -35,6 +36,7 @@
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="4"
android:layout_gravity="center"
android:entries="@array/mode_list" />
</LinearLayout>
@@ -48,6 +50,7 @@
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/api" />
@@ -57,7 +60,31 @@
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="4"
android:layout_gravity="center"
android:entries="@array/api_mode_list" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="0.0dip"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:text="Lofi 分辨率" />
<Spinner
android:id="@+id/lofi_resolution_list"
android:layout_width="0.0dip"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:layout_weight="4"
android:layout_gravity="center"
android:entries="@array/lofi_resolution_list" />
</LinearLayout>
</merge>
+6
View File
@@ -33,6 +33,12 @@
<item>exhentai.org</item>
</string-array>
<string-array name="lofi_resolution_list" translatable="false">
<item>460x</item>
<item>780x</item>
<item>980x</item>
</string-array>
<string-array name="comment_long_click">
<item>@android:string/copy</item>
<item>@string/uploader_others</item>
+1
View File
@@ -61,6 +61,7 @@
<color name="background_light">#fffafafa</color>
<color name="background_dark">#ff212121</color>
<color name="background_little_light">#ffe5e5e5</color>
<color name="background_little_dark">#ff353535</color>
<color name="primary_text_dark">#de000000</color>
<color name="secondary_text_dark">#8a000000</color>
+2 -1
View File
@@ -173,7 +173,8 @@ public class EhInfo {
public void setCookie(HttpURLConnection conn, String previewMode) {
String cookie = "ipb_member_id=" + mInfoPref.getString(KEY_MEMBER_ID, DEFAULT_MEMBER_ID) +
"; ipb_pass_hash=" + mInfoPref.getString(KEY_PASS_HASH, DEFAULT_PASS_HASH) +
"; uconfig=" + (previewMode == null ? mUconfig : getUconfigString(previewMode));
"; uconfig=" + (previewMode == null ? mUconfig : getUconfigString(previewMode)) +
"; xres=" + Config.getLofiResolution();
conn.setRequestProperty("Cookie", cookie);
}
@@ -192,9 +192,9 @@ public class ExDownloader implements Runnable {
if (mImageNum != -1) {
return mImageNum;
} else {
return Math.max(Math.max(mStartIndex - 1,
return Math.max(Math.max(Math.max(mStartIndex - 1,
Math.max(mCurMaxPreviewPage, mPreviewPageNum - 1) * mPreviewPerPage),
mPageTokeArray.maxValidIndex());
mPageTokeArray.maxValidIndex()), 0);
}
}
@@ -425,7 +425,6 @@ public class ExDownloader implements Runnable {
// A loop to get page token
while (true) {
// Check stop
if (mStopWork)
break;
@@ -448,9 +447,6 @@ public class ExDownloader implements Runnable {
}
}
Log.d(TAG, "noTokenIndex = " + noTokenIndex);
Log.d(TAG, "pageIndex = " + pageIndex);
if (noTokenIndex != -1) {
if (mPageTokeArray.get(noTokenIndex) == null) {
getDetailInfo(noTokenIndex / mPreviewPerPage, ediFile, false);
@@ -462,6 +458,8 @@ public class ExDownloader implements Runnable {
mCurRequestPageIndex = -1;
}
ensureWorkers();
synchronized(mWorkerGetIndexLock) {mWorkerGetIndexLock.notifyAll();}
synchronized(mWorkerLock) {mWorkerLock.notifyAll();}
}
+111 -65
View File
@@ -43,6 +43,7 @@ import com.hippo.ehviewer.util.MathUtils;
import com.hippo.ehviewer.util.TimeRunner;
import com.hippo.ehviewer.util.Ui;
import com.hippo.ehviewer.util.Utils;
import com.hippo.ehviewer.widget.MaterialToast;
// TODO 双击加载该页
// TODO 长按从该页开始加载
@@ -126,12 +127,9 @@ public class GalleryView extends GLView
private int scrollXOffset = 0;
private int scrollYOffset = 0;
private int stopScrollXOffset = 0;
@SuppressWarnings("unused")
private int stopScrollYOffset = 0;
private float mScale = 1;
private boolean mShowEdegTip = true;
private int mWidth = -1;
private int mHeight = -1;
@@ -157,13 +155,16 @@ public class GalleryView extends GLView
private boolean mShowTapArea = false;
private boolean mShowTapAreaTurn = false;
private OnEdgeListener mOnEdgeListener;
private OnTapTextListener mOnTapTextListener;
private OnScrollPageListener mOnScrollPageListener;
//private boolean mLocked = false;
//private boolean mLocked = false;
public interface OnEdgeListener {
public void onFirstPageEdge();
public void onLastPageEdge();
private OnTapTextListener mOnTapTextListener;
private GalleryViewListener mGalleryViewListener;
public interface GalleryViewListener {
public void onTapCenter();
public void onPageChanged(int index);
public void onSizeUpdate(int size);
}
public interface OnTapTextListener {
@@ -171,20 +172,12 @@ public class GalleryView extends GLView
public void onTapDoubleText(int index);
}
public interface OnScrollPageListener {
public void onScrollPage(int index);
}
public void setOnEdgeListener(OnEdgeListener l) {
mOnEdgeListener = l;
}
public void setOnTapTextListener(OnTapTextListener l) {
mOnTapTextListener = l;
}
public void setOnScrollPageListener(OnScrollPageListener l) {
mOnScrollPageListener = l;
public void setGalleryViewListener(GalleryViewListener l) {
mGalleryViewListener = l;
}
public GalleryView(Context context, ImageSet imageSet, int startIndex) {
@@ -224,6 +217,7 @@ public class GalleryView extends GLView
@Override
public void run() {
checkSize();
setState();
if (!isEnsureSize && !mImageSet.isStop())
AppHandler.getInstance().postDelayed(this, 500);
}
@@ -232,6 +226,7 @@ public class GalleryView extends GLView
* Check size
*/
private void checkSize() {
int oldSize = mSize;
int size = mImageSet.getSize();
if (size == -1) {
isEnsureSize = false;
@@ -240,6 +235,9 @@ public class GalleryView extends GLView
isEnsureSize = true;
mSize = size;
}
if (mSize != oldSize && mGalleryViewListener != null)
mGalleryViewListener.onSizeUpdate(mSize);
}
/**
@@ -253,28 +251,31 @@ public class GalleryView extends GLView
mState = STATE_LAST;
}
private boolean checkIndex(int index) {
private boolean isIndexLoadable(int index) {
int targetIndex = index - mCurIndex + 1;
return targetIndex == CUR_TARGET_INDEX ||
(mState != STATE_FIRST && targetIndex == PRE_TARGET_INDEX) ||
(mState != STATE_LAST && targetIndex == NEXT_TARGET_INDEX);
}
private boolean isIndexValid(int index) {
return index >= 0 && index < mSize;
}
@Override
public void onGetImage(int index) {
if (checkIndex(index)) {
if (isIndexLoadable(index)) {
loadImage(index);
}
}
@Override
public void onDownloading(int index, float percent) {
public synchronized void onDownloading(int index, float percent) {
int targetIndex = index - mCurIndex + 1;
if (checkIndex(index)) {
if (isIndexLoadable(index)) {
// Free what do not need
ShowItem showItem = showItems[targetIndex];
if (showItem != null)
showItem.recycle();
if (showItems[targetIndex] != null)
showItems[targetIndex].recycle();
showItems[targetIndex] = new Text(
String.format("正在加载 %d%%", Math.round(percent * 100)));
@@ -283,18 +284,17 @@ public class GalleryView extends GLView
}
@Override
public void onDecodeOver(int index, Object res) {
public synchronized void onDecodeOver(int index, Object res) {
int targetIndex = index - mCurIndex + 1;
if (!checkIndex(index)) {
if (!isIndexLoadable(index)) {
// If it do not need any more, free
if (res != null && res instanceof Bitmap)
((Bitmap)res).recycle();
} else {
// Free what do not need
ShowItem showItem = showItems[targetIndex];
if (showItem != null)
showItem.recycle();
if (showItems[targetIndex] != null)
showItems[targetIndex].recycle();
if (res == null) {
showItems[targetIndex] = new Text("读取图片错误"); // TODO
@@ -313,6 +313,14 @@ public class GalleryView extends GLView
}
}
public int getCurIndex() {
return mCurIndex;
}
public int getSize() {
return mSize;
}
private void drawTapArea(GLCanvas canvas) {
// Background
canvas.fillRect(0, 0, mWidth, mHeight, TAP_AREA_MASK_COLOR);
@@ -344,7 +352,8 @@ public class GalleryView extends GLView
}
@Override
protected void render(GLCanvas canvas) {
protected synchronized void render(GLCanvas canvas) {
// If it is not render thread, do not render
super.render(canvas);
boolean hasMovie = false;
@@ -622,8 +631,8 @@ public class GalleryView extends GLView
*
* @param index
*/
private void loadImage(int index) {
if (!checkIndex(index))
private synchronized void loadImage(int index) {
if (!isIndexLoadable(index))
return;
int targetIndex = index - mCurIndex + 1;
@@ -715,14 +724,13 @@ public class GalleryView extends GLView
* You'd better resetSizePosition(PRE_TARGET_INDEX) before
* @return
*/
private boolean goToPrePage() {
private synchronized boolean goToPrePage() {
if (isFirstPage())
return false;
ShowItem showItem;
showItem = showItems[NEXT_TARGET_INDEX];
if (showItem != null)
showItem.recycle();
if (showItems[NEXT_TARGET_INDEX] != null)
showItems[NEXT_TARGET_INDEX].recycle();
showItems[NEXT_TARGET_INDEX] = showItems[CUR_TARGET_INDEX];
showItems[CUR_TARGET_INDEX] = showItems[PRE_TARGET_INDEX];
showItems[PRE_TARGET_INDEX] = null;
@@ -741,8 +749,8 @@ public class GalleryView extends GLView
setState();
loadImage(mCurIndex-1);
if (mOnScrollPageListener != null) {
mOnScrollPageListener.onScrollPage(mCurIndex);
if (mGalleryViewListener != null) {
mGalleryViewListener.onPageChanged(mCurIndex);
}
invalidate();
@@ -753,14 +761,13 @@ public class GalleryView extends GLView
* You'd better resetSizePosition(NEXT_TARGET_INDEX) before
* @return
*/
private boolean goToNextPage() {
private synchronized boolean goToNextPage() {
if (isLastPage())
return false;
ShowItem showItem;
showItem = showItems[PRE_TARGET_INDEX];
if (showItem != null)
showItem.recycle();
if (showItems[PRE_TARGET_INDEX] != null)
showItems[PRE_TARGET_INDEX].recycle();
showItems[PRE_TARGET_INDEX] = showItems[CUR_TARGET_INDEX];
showItems[CUR_TARGET_INDEX] = showItems[NEXT_TARGET_INDEX];
showItems[NEXT_TARGET_INDEX] = null;
@@ -778,20 +785,58 @@ public class GalleryView extends GLView
setState();
loadImage(mCurIndex+1);
if (mOnScrollPageListener != null) {
mOnScrollPageListener.onScrollPage(mCurIndex);
if (mGalleryViewListener != null) {
mGalleryViewListener.onPageChanged(mCurIndex);
}
invalidate();
return true;
}
public synchronized boolean goToPage(int index) {
if (!isIndexValid(index))
return false;
if (index == mCurIndex) {
return true;
} else if (index == mCurIndex - 1) {
return goToPrePage();
} else if (index == mCurIndex + 1) {
return goToNextPage();
} else {
// Free
if (showItems[PRE_TARGET_INDEX] != null)
showItems[PRE_TARGET_INDEX].recycle();
if (showItems[CUR_TARGET_INDEX] != null)
showItems[CUR_TARGET_INDEX].recycle();
if (showItems[NEXT_TARGET_INDEX] != null)
showItems[NEXT_TARGET_INDEX].recycle();
mCurIndex = index;
setState();
loadImage(mCurIndex-1);
loadImage(mCurIndex);
loadImage(mCurIndex+1);
if (mGalleryViewListener != null) {
mGalleryViewListener.onPageChanged(mCurIndex);
}
invalidate();
return true;
}
}
@Override
protected boolean onTouch(MotionEvent event) {
mGestureRecognizer.onTouchEvent(event);
return true;
}
private void setTouchLocked(boolean locked) {
}
private class MyGestureListener implements GestureRecognizer.Listener {
private boolean isScale = false;
@@ -880,21 +925,28 @@ public class GalleryView extends GLView
if (Utils.isInArea(leftArea, (int)x, (int)y)) {
// TODO goto bottom first the to pre page
resetSizePosition(PRE_TARGET_INDEX);
if (!goToPrePage() && mOnEdgeListener != null)
mOnEdgeListener.onFirstPageEdge();
if (!goToPrePage()) {
// Get to first page
MaterialToast.showToast("第一页"); // TODO
}
} else if (Utils.isInArea(topArea, (int)x, (int)y)) {
zoom(true);
} else if (Utils.isInArea(rightArea, (int)x, (int)y)) {
// TODO goto bottom first the to pre page
resetSizePosition(NEXT_TARGET_INDEX);
if (!goToNextPage() && mOnEdgeListener != null)
mOnEdgeListener.onLastPageEdge();
if (!goToNextPage()) {
// Get to last page
MaterialToast.showToast(isEnsureSize ? "最后一页" : "等待获取后续页面"); // TODO
}
} else if (Utils.isInArea(bottomArea, (int)x, (int)y)) {
zoom(false);
} else if (Utils.isInArea(centerArea, (int)x, (int)y)) {
mShowTapArea = true;
mShowTapAreaTurn = true;
invalidate();
if (mGalleryViewListener != null)
mGalleryViewListener.onTapCenter();
//mShowTapArea = true;
//mShowTapAreaTurn = true;
//invalidate();
} else {
// Can't catch tap
}
@@ -914,7 +966,7 @@ public class GalleryView extends GLView
return;
Utils.vibrator(mContext, 100);
mImageSet.setTargetIndex(mCurIndex);
mImageSet.setStartIndex(mCurIndex);
}
@Override
@@ -960,11 +1012,6 @@ public class GalleryView extends GLView
resetSizePosition(PRE_TARGET_INDEX);
} else {
changePage = false;
if (mShowEdegTip) { // First page
mShowEdegTip = false;
if (mOnEdgeListener != null)
mOnEdgeListener.onFirstPageEdge();
}
}
}
else { // Go to righ
@@ -974,11 +1021,6 @@ public class GalleryView extends GLView
resetSizePosition(NEXT_TARGET_INDEX);
} else {
changePage = false;
if (mShowEdegTip) { // last page
mShowEdegTip = false;
if (mOnEdgeListener != null)
mOnEdgeListener.onLastPageEdge();
}
}
}
}
@@ -1054,6 +1096,11 @@ public class GalleryView extends GLView
mReturnTimeRunner.start();
}
break;
case SCROLL_NONE:
if (isFirstPage())
MaterialToast.showToast("第一页");
else if (isLastPage())
MaterialToast.showToast(isEnsureSize ? "最后一页" : "等待获取后续页面"); // TODO
}
return true;
}
@@ -1123,7 +1170,6 @@ public class GalleryView extends GLView
@Override
public void onUp() {
mShowEdegTip = true;
// if use onSingleTapUp, use below
@@ -127,6 +127,10 @@ public class ImageSet implements ExDownloader.ListenerForImageSet {
mExDownloader.setTargetIndex(index);
}
public void setStartIndex(int startIndex) {
mExDownloader.setStartIndex(startIndex);
}
public Object getImage(int index) {
// Try to get file name
String filename = mImageFilenameArray.get(index);
@@ -935,9 +935,7 @@ public class GLES20Canvas implements GLCanvas {
int target = texture.getTarget();
GLES20.glBindTexture(target, texture.getId());
checkError();
// TODO Maybe multi-thread error
if (!bitmap.isRecycled())
GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
}
@Override
@@ -42,6 +42,10 @@ public class GalleryUtils {
sCurrentThread = Thread.currentThread();
}
public static boolean isRenderThread() {
return sCurrentThread == Thread.currentThread();
}
public static void assertNotInRenderThread() {
if (!sWarned) {
if (Thread.currentThread() == sCurrentThread) {
+220 -11
View File
@@ -16,33 +16,65 @@
package com.hippo.ehviewer.ui;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.SeekBar;
import android.widget.TextView;
import com.hippo.ehviewer.AppHandler;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.gallery.GalleryView;
import com.hippo.ehviewer.gallery.data.ImageSet;
import com.hippo.ehviewer.gallery.ui.GLRootView;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.Constants;
import com.hippo.ehviewer.util.Ui;
import com.hippo.ehviewer.widget.AlertButton;
import com.hippo.ehviewer.widget.DialogBuilder;
public class GalleryActivity extends AbstractActivity {
public class GalleryActivity extends AbstractActivity
implements GalleryView.GalleryViewListener, SeekBar.OnSeekBarChangeListener {
@SuppressWarnings("unused")
private final static String TAG = GalleryActivity.class.getSimpleName();
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final int ACTION_BAR_HEIGHT = 48;
private static final int NAV_BAR_HEIGHT = 48;
public final static String KEY_GID = "gid";
public final static String KEY_TOKEN = "token";
public final static String KEY_TITLE = "title";
public final static String KEY_START_INDEX = "start_index";
private RelativeLayout mainView;
private View mainView;
private View mActionBar;
private View mNavBar;
private TextView mTitleView;
private TextView mSeekerBubble;
/** It store index, start from 0, so max is samller than size **/
private SeekBar mPageSeeker;
private ImageSet mImageSet;
private GalleryView mGalleryView;
private int mGid;
private String mTitle;
private ValueAnimator mShowAnimator;
private ValueAnimator mHideAnimator;
private long mHideTaskTimeStamp;
@Override
public void onOrientationChanged(int paddingTop, int paddingBottom) {
// Empty
@@ -63,17 +95,83 @@ public class GalleryActivity extends AbstractActivity {
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}
}
private void initAnimator() {
mShowAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
mShowAnimator.setDuration(Constants.ANIMATE_TIME);
mShowAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
mShowAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float)animation.getAnimatedValue();
mActionBar.setY(- value * Ui.dp2pix(ACTION_BAR_HEIGHT));
mNavBar.setY(mainView.getHeight() - (1.0f - value) * Ui.dp2pix(NAV_BAR_HEIGHT));
mActionBar.requestLayout();
mNavBar.requestLayout();
}
});
mShowAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mActionBar.setVisibility(View.VISIBLE);
mNavBar.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
mHideAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mHideAnimator.setDuration(Constants.ANIMATE_TIME);
mHideAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
mHideAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float)animation.getAnimatedValue();
mActionBar.setY(- value * Ui.dp2pix(ACTION_BAR_HEIGHT));
mNavBar.setY(mainView.getHeight() - (1.0f - value) * Ui.dp2pix(NAV_BAR_HEIGHT));
mActionBar.requestLayout();
mNavBar.requestLayout();
}
});
mHideAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mActionBar.setVisibility(View.VISIBLE);
mNavBar.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
mActionBar.setVisibility(View.GONE);
mNavBar.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
}
@Override
@TargetApi(Build.VERSION_CODES.KITKAT)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gl_root_group);
mainView = findViewById(R.id.main);
mActionBar = findViewById(R.id.action_bar);
mNavBar = findViewById(R.id.nav_bar);
mTitleView = (TextView)findViewById(R.id.title);
mPageSeeker = (SeekBar)findViewById(R.id.page_seeker);
mSeekerBubble = (TextView)findViewById(R.id.seeker_bubble);
// FullScreen
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
mainView = (RelativeLayout)findViewById(R.id.main);
mainView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -84,12 +182,12 @@ public class GalleryActivity extends AbstractActivity {
}
Intent intent = getIntent();
int gid = intent.getIntExtra(KEY_GID, -1);
mGid = intent.getIntExtra(KEY_GID, -1);
String token = intent.getStringExtra(KEY_TOKEN);
String title = intent.getStringExtra(KEY_TITLE);
mTitle = intent.getStringExtra(KEY_TITLE);
int startIndex = intent.getIntExtra(KEY_START_INDEX, 0);
if (gid == -1 || token == null || title == null) {
if (mGid == -1 || token == null || mTitle == null) {
new DialogBuilder(this).setTitle(R.string.error)
.setMessage("数据错误!").setPositiveButton("关闭",
new View.OnClickListener() {
@@ -100,17 +198,128 @@ public class GalleryActivity extends AbstractActivity {
}
}).create().show();
} else {
mImageSet = new ImageSet(gid, token, title, startIndex);
GalleryView isv = new GalleryView(getApplicationContext(), mImageSet, startIndex);
mImageSet = new ImageSet(mGid, token, mTitle, startIndex);
mGalleryView = new GalleryView(getApplicationContext(), mImageSet, startIndex);
GLRootView glrv= (GLRootView)findViewById(R.id.gl_root_view);
glrv.setContentPane(isv);
glrv.setContentPane(mGalleryView);
initAnimator();
// Set listener
mGalleryView.setGalleryViewListener(this);
mPageSeeker.setOnSeekBarChangeListener(this);
updateTitle();
mPageSeeker.setMax(mGalleryView.getSize() - 1);
mPageSeeker.setProgress(startIndex);
// Show first tip
if (Config.getGalleryFirst()) {
Config.setGalleryFirst(false);
new DialogBuilder(this).setTitle("提示")
.setMessage("长按屏幕,可使下载从当前页面开始") // TODO
.setSimpleNegativeButton().create().show();
}
}
}
private void updateTitle() {
StringBuilder sb = new StringBuilder(30);
sb.append(mGalleryView.getCurIndex() + 1).append("/")
.append(mGalleryView.getSize())
.append(" - ").append(mGid);
mTitleView.setText(sb.toString());
}
private void startHideTask() {
mHideTaskTimeStamp = System.currentTimeMillis();
AppHandler.getInstance().postDelayed(new HideTask(mHideTaskTimeStamp), 2000);
}
private void cancelHideTask() {
mHideTaskTimeStamp = 0;
}
@Override
public void onDestroy() {
super.onDestroy();
mImageSet.free();
if (mImageSet != null)
mImageSet.free();
}
@Override
public void onTapCenter() {
if (mShowAnimator.isRunning() || mHideAnimator.isRunning())
return;
if (mActionBar.getVisibility() == View.GONE) {
mShowAnimator.start();
startHideTask();
} else if (mActionBar.getVisibility() == View.VISIBLE) {
mHideAnimator.start();
}
}
// The task to hide actionbar
private class HideTask implements Runnable {
private final long mTimeStamp;
public HideTask(long timeStamp) {
mTimeStamp = timeStamp;
}
@Override
public void run() {
if (mTimeStamp == mHideTaskTimeStamp &&!mShowAnimator.isRunning() &&
!mHideAnimator.isRunning() && mActionBar.getVisibility() == View.VISIBLE)
mHideAnimator.start();
}
}
@Override
public void onPageChanged(int index) {
updateTitle();
mPageSeeker.setProgress(index);
}
@Override
public void onSizeUpdate(int size) {
updateTitle();
mPageSeeker.setMax(size - 1);
}
private void showSeekBubble() {
mSeekerBubble.setVisibility(View.VISIBLE);
}
private void hideSeekBubble() {
mSeekerBubble.setVisibility(View.GONE);
}
public void updateSeekBubble(int progress, int max, int thumbOffset) {
thumbOffset = thumbOffset + Ui.dp2pix(12);
int x = (mainView.getWidth() - 2 * thumbOffset) * progress / max + thumbOffset;
mSeekerBubble.setX(x);
mSeekerBubble.setText(String.valueOf(progress + 1));
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
updateSeekBubble(progress, seekBar.getMax(), seekBar.getThumbOffset());
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
cancelHideTask();
showSeekBubble();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
startHideTask();
hideSeekBubble();
mGalleryView.goToPage(seekBar.getProgress());
}
}
@@ -268,6 +268,8 @@ public class GalleryListActivity extends AbstractGalleryActivity
modeSpinner.setSelection(Config.getMode());
final Spinner apiModeSpinner = (Spinner)customLayout.findViewById(R.id.api_mode_list);
apiModeSpinner.setSelection(Config.getApiMode());
final Spinner lofiResolutionSpinner = (Spinner)customLayout.findViewById(R.id.lofi_resolution_list);
lofiResolutionSpinner.setSelection(Config.getLofiResolution() - 1);
return db.setSimpleNegativeButton()
.setPositiveButton(android.R.string.ok, new View.OnClickListener() {
@Override
@@ -275,6 +277,7 @@ public class GalleryListActivity extends AbstractGalleryActivity
((AlertButton)v).dialog.dismiss();
Config.setMode(modeSpinner.getSelectedItemPosition());
Config.setApiMode(apiModeSpinner.getSelectedItemPosition());
Config.setLofiResolution(lofiResolutionSpinner.getSelectedItemPosition() + 1);
}
}).create();
}
+26
View File
@@ -354,6 +354,21 @@ public class Config {
KEY_PREVIEW_COLUMNS_LANDSCAPE, DEFAULT_PREVIEW_COLUMNS_LANDSCAPE);
}
/****** GalleryActivity ******/
private static final String KEY_GALLERY_FIRST = "gallery_first";
private static final boolean DEFAULT_GALLERY_FIRST = true;
public static boolean getGalleryFirst() {
return mConfigPre.getBoolean(KEY_GALLERY_FIRST, DEFAULT_GALLERY_FIRST);
}
public static void setGalleryFirst(boolean galleryFirst) {
mConfigPre.edit().putBoolean(KEY_GALLERY_FIRST, galleryFirst).apply();
}
/****** Mode an API Mode ******/
private static final String KEY_MODE = "mode";
@@ -362,6 +377,9 @@ public class Config {
private static final String KEY_API_MODE = "api_mode";
private static final int DEFAULT_API_MODE = EhClient.MODE_G;
private static final String KEY_LOFI_RESOLUTION = "lofi_resolution";
private static final int DEFAULT_LOFI_RESOLUTION = 1;
public static int getMode() {
return mConfigPre.getInt(KEY_MODE, DEFAULT_MODE);
}
@@ -378,6 +396,14 @@ public class Config {
mConfigPre.edit().putInt(KEY_API_MODE, apiMode).apply();
}
public static int getLofiResolution() {
return mConfigPre.getInt(KEY_LOFI_RESOLUTION, DEFAULT_LOFI_RESOLUTION);
}
public static void setLofiResolution(int lofiResolution) {
mConfigPre.edit().putInt(KEY_LOFI_RESOLUTION, lofiResolution).apply();
}
/****** Eh Config ******/
private static final String KEY_DEFAULT_CAT = "default_cat";
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2014 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.util;
import android.text.format.DateFormat;
public final class DateUtils {
@SuppressWarnings("deprecation")
public static boolean hasSeconds(CharSequence inFormat) {
return hasDesignator(inFormat, DateFormat.SECONDS);
}
@SuppressWarnings("deprecation")
public static boolean hasDesignator(CharSequence inFormat, char designator) {
if (inFormat == null) return false;
final int length = inFormat.length();
int c;
int count;
for (int i = 0; i < length; i += count) {
count = 1;
c = inFormat.charAt(i);
if (c == DateFormat.QUOTE) {
count = skipQuotedText(inFormat, i, length);
} else if (c == designator) {
return true;
}
}
return false;
}
@SuppressWarnings("deprecation")
private static int skipQuotedText(CharSequence s, int i, int len) {
if (i + 1 < len && s.charAt(i + 1) == DateFormat.QUOTE) {
return 2;
}
int count = 1;
// skip leading quote
i++;
while (i < len) {
char c = s.charAt(i);
if (c == DateFormat.QUOTE) {
count++;
// QUOTEQUOTE -> QUOTE
if (i + 1 < len && s.charAt(i + 1) == DateFormat.QUOTE) {
i++;
} else {
break;
}
} else {
i++;
count++;
}
}
return count;
}
}
@@ -0,0 +1,264 @@
/*
* Copyright (C) 2014 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.widget;
import java.util.Calendar;
import java.util.TimeZone;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.ViewDebug.ExportedProperty;
import android.widget.TextView;
import com.hippo.ehviewer.util.DateUtils;
public class TextClock extends TextView {
public static final CharSequence DEFAULT_FORMAT_12_HOUR = "hh:mm a";
public static final CharSequence DEFAULT_FORMAT_24_HOUR = "HH:mm";
private CharSequence mFormat12;
private CharSequence mFormat24;
@ExportedProperty
private CharSequence mFormat;
@ExportedProperty
private boolean mHasSeconds;
private boolean mAttached;
private Calendar mTime;
private String mTimeZone;
private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra("time-zone");
createTime(timeZone);
}
onTimeChanged();
}
};
private final Runnable mTicker = new Runnable() {
@Override
public void run() {
onTimeChanged();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
getHandler().postAtTime(mTicker, next);
}
};
public TextClock(Context context) {
this(context, null);
}
public TextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mFormat12 = DEFAULT_FORMAT_12_HOUR;
mFormat24 = DEFAULT_FORMAT_24_HOUR;
mTimeZone = TimeZone.getDefault().getID();
init();
}
private void init() {
createTime(mTimeZone);
// Wait until onAttachedToWindow() to handle the ticker
chooseFormat(false);
}
private void createTime(String timeZone) {
if (timeZone != null) {
mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
} else {
mTime = Calendar.getInstance();
}
}
public CharSequence getFormat12Hour() {
return mFormat12;
}
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
chooseFormat();
onTimeChanged();
}
public CharSequence getFormat24Hour() {
return mFormat24;
}
public void setFormat24Hour(CharSequence format) {
mFormat24 = format;
chooseFormat();
onTimeChanged();
}
public boolean is24HourModeEnabled() {
return DateFormat.is24HourFormat(getContext());
}
public String getTimeZone() {
return mTimeZone;
}
public void setTimeZone(String timeZone) {
mTimeZone = timeZone;
createTime(timeZone);
onTimeChanged();
}
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
*
* Calling this method does not schedule or unschedule the time ticker.
*/
private void chooseFormat() {
chooseFormat(true);
}
public CharSequence getFormat() {
return mFormat;
}
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
*
* @param handleTicker true if calling this method should schedule/unschedule the
* time ticker, false otherwise
*/
private void chooseFormat(boolean handleTicker) {
final boolean format24Requested = is24HourModeEnabled();
if (format24Requested) {
mFormat = mFormat24;
} else {
mFormat = mFormat12;
}
boolean hadSeconds = mHasSeconds;
mHasSeconds = DateUtils.hasSeconds(mFormat);
if (handleTicker && mAttached && hadSeconds != mHasSeconds) {
if (hadSeconds) getHandler().removeCallbacks(mTicker);
else mTicker.run();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mAttached) {
mAttached = true;
registerReceiver();
registerObserver();
createTime(mTimeZone);
if (mHasSeconds) {
mTicker.run();
} else {
onTimeChanged();
}
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAttached) {
unregisterReceiver();
unregisterObserver();
getHandler().removeCallbacks(mTicker);
mAttached = false;
}
}
private void registerReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
}
private void registerObserver() {
final ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver);
}
private void unregisterReceiver() {
getContext().unregisterReceiver(mIntentReceiver);
}
private void unregisterObserver() {
final ContentResolver resolver = getContext().getContentResolver();
resolver.unregisterContentObserver(mFormatChangeObserver);
}
private void onTimeChanged() {
mTime.setTimeInMillis(System.currentTimeMillis());
setText(DateFormat.format(mFormat, mTime));
}
}