diff --git a/jni/Application.mk b/jni/Application.mk
index 4554c7e18..2ab6fe60a 100644
--- a/jni/Application.mk
+++ b/jni/Application.mk
@@ -1,2 +1,2 @@
-APP_ABI := all
+APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-14
diff --git a/jni/gif.c b/jni/gif.c
index 7106d0f97..2b06b36b2 100644
--- a/jni/gif.c
+++ b/jni/gif.c
@@ -10,6 +10,12 @@
static int errorCode;
+// TODO is GIF87a work fine ?
+
+static inline freeSafely(void* p) {
+ if (p != NULL) free(p);
+}
+
/*
* Return NULL if error
*/
@@ -66,7 +72,7 @@ static int streamReadFun(GifFileType* gif, GifByteType* bytes, int size) {
/*
* stack store the index to draw
* stack capacity should be imageCount * sizeof(int).
- * When read it, use for(; size > 0; size--).
+ * When read it, use from tail to head
*/
static void getDrawStack(GIF* gif, int targetIndex, int* which, int* stack,
int* stackSize) {
@@ -313,7 +319,7 @@ static void render(GIF* gif, int index) {
int height = gif->gifFile->SHeight;
int imageCount = gif->gifFile->ImageCount;
int which, stackSize;
- int* stack = (int*) malloc(imageCount * sizeof(int));
+ int stack[imageCount];
void* tempPoint;
int tempInt;
@@ -346,9 +352,6 @@ static void render(GIF* gif, int index) {
glTexSubImage2D(DEFAULT_TARGET, 0, 0, 0, width,
height, gif->format, DEFAULT_TYPE, gif->pixels);
- // release
- free(stack);
-
// update pxlIndex
gif->pxlIndex = index;
@@ -372,8 +375,10 @@ static void render(GIF* gif, int index) {
}
if (gif->bak == NULL)
gif->bak = malloc(size);
- memcpy(gif->bak, gif->pixels, size);
- gif->bakIndex = index;
+ if (gif->bak != NULL) {
+ memcpy(gif->bak, gif->pixels, size);
+ gif->bakIndex = index;
+ }
}
}
@@ -388,6 +393,9 @@ static GIF* analysisGifFileType(GifFileType* gifFile, int format, int* delays) {
int imageCount = gifFile->ImageCount;
gif = (GIF*) malloc(sizeof(GIF));
+ if (gif == NULL)
+ return NULL;
+
gif->gifFile = gifFile;
gif->trans = (int*) malloc(imageCount * sizeof(int));
gif->disposals = (int*) malloc(imageCount * sizeof(int));
@@ -410,6 +418,14 @@ static GIF* analysisGifFileType(GifFileType* gifFile, int format, int* delays) {
gif->bak = NULL;
gif->bakIndex = -1;
+ if (gif->trans == NULL || gif->disposals == NULL || gif->pixels == NULL) {
+ freeSafely(gif->pixels);
+ freeSafely(gif->disposals);
+ freeSafely(gif->trans);
+ free(gif);
+ return NULL;
+ }
+
trans = gif->trans;
disposals = gif->disposals;
for (i = 0; i < imageCount; i++) {
@@ -471,15 +487,19 @@ jobject GIF_DecodeStream(JNIEnv* env, jobject is, jint format) {
// Check image count
imageCount = gifFile->ImageCount;
if (imageCount < 1) {
- DGifCloseFile(gif->gifFile, &errorCode);
+ DGifCloseFile(gifFile, &errorCode);
return NULL;
}
// Get GIF
delays = (int*) malloc(imageCount * sizeof(int));
+ if (delays == NULL) {
+ DGifCloseFile(gifFile, &errorCode);
+ return NULL;
+ }
gif = analysisGifFileType(gifFile, format, delays);
if (gif == NULL) {
- DGifCloseFile(gif->gifFile, &errorCode);
+ DGifCloseFile(gifFile, &errorCode);
return NULL;
}
@@ -512,10 +532,10 @@ void GIF_Free(JNIEnv* env, int nativeImage) {
GIF* gif = (GIF*) nativeImage;
DGifCloseFile(gif->gifFile, &errorCode);
- if (gif->trans != NULL) free(gif->trans);
- if (gif->disposals != NULL) free(gif->disposals);
- if (gif->pixels != NULL) free(gif->pixels);
- if (gif->bak != NULL) free(gif->bak);
+ freeSafely(gif->trans);
+ freeSafely(gif->disposals);
+ freeSafely(gif->pixels);
+ freeSafely(gif->bak);
free(gif);
}
diff --git a/jni/image.c b/jni/image.c
index 7905a6e4d..b298bb3fc 100644
--- a/jni/image.c
+++ b/jni/image.c
@@ -52,15 +52,13 @@ Java_com_hippo_ehviewer_gallery_image_Image_nativeFree(JNIEnv* env,
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_gallery_image_Image_nativeRender(JNIEnv* env,
- jclass clazz, jint target, jint level, jint xoffset, jint yoffset,
- jint format, jint type, jint nativeImage, jint fileFormat) {
+ jclass clazz, jint format, jint type, jint nativeImage, jint fileFormat) {
// TODO
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_gallery_image_GifImage_nativeRender(JNIEnv* env,
- jclass clazz, jint target, jint level, jint xoffset, jint yoffset,
- jint format, jint type, jint nativeImage, jint fileFormat, jint index) {
+ jclass clazz, jint format, jint type, jint nativeImage, jint fileFormat, jint index) {
if (type != DEFAULT_TYPE || fileFormat != FORMAT_GIF)
return;
diff --git a/res/layout/gl_root_group.xml b/res/layout/gl_root_group.xml
index 6d1f73cc9..a0ce72968 100644
--- a/res/layout/gl_root_group.xml
+++ b/res/layout/gl_root_group.xml
@@ -34,6 +34,17 @@
android:layout_marginRight="12dp"
android:textColor="@color/secondary_text_light" />
+
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/hippo/ehviewer/drawable/BatteryDrawable.java b/src/com/hippo/ehviewer/drawable/BatteryDrawable.java
new file mode 100644
index 000000000..3f884d2ce
--- /dev/null
+++ b/src/com/hippo/ehviewer/drawable/BatteryDrawable.java
@@ -0,0 +1,141 @@
+/*
+ * 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.drawable;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class BatteryDrawable extends Drawable {
+
+ private static final String TAG = BatteryDrawable.class.getSimpleName();
+
+ public static final int WARN_LIMIT = 15;
+
+ private int mColor = Color.WHITE;
+ private int mWarnColor = Color.RED;
+ private int mElect = -1;
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+
+ public BatteryDrawable() {
+ updatePaint();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mElect == -1)
+ return;
+
+ Rect bounds = getBounds();
+ int width = bounds.width();
+ int height = bounds.height();
+ int strokeWidth = (int)(Math.sqrt(width * width + height * height) * 0.06f);
+ int halfStrokeWidth = strokeWidth / 2;
+ int turn1 = width * 6 / 7;
+ int turn2 = height / 3;
+ int start = strokeWidth;
+ int stop = turn1 - halfStrokeWidth;
+ int levelEnd = start + (stop - start) * mElect / 100;
+
+ float[] drawPoints = {
+ halfStrokeWidth, 0 + strokeWidth, halfStrokeWidth, height - strokeWidth,
+ 0, halfStrokeWidth, turn1 - halfStrokeWidth, halfStrokeWidth,
+ 0, height - halfStrokeWidth, turn1 - halfStrokeWidth, height - halfStrokeWidth,
+ turn1, 0, turn1, turn2 - halfStrokeWidth,
+ turn1, height, turn1, height - turn2 + halfStrokeWidth,
+ turn1 - halfStrokeWidth, turn2, width, turn2,
+ turn1 - halfStrokeWidth, height - turn2, width, height - turn2,
+ width - halfStrokeWidth, turn2 + halfStrokeWidth, width - halfStrokeWidth, height - turn2 - halfStrokeWidth};
+
+ mPaint.setStrokeWidth(strokeWidth);
+ canvas.drawLines(drawPoints, mPaint);
+ canvas.drawRect(start, 0 + strokeWidth, levelEnd, height - strokeWidth, mPaint);
+ }
+
+ private boolean isWarn() {
+ return mElect <= WARN_LIMIT;
+ }
+
+ public void setColor(int color) {
+ if (mColor == color)
+ return;
+
+ mColor = color;
+ if (!isWarn()) {
+ mPaint.setColor(mColor);
+ invalidateSelf();
+ }
+ }
+
+ public void setWarnColor(int color) {
+ if (mWarnColor == color)
+ return;
+
+ mWarnColor = color;
+ if (isWarn()) {
+ mPaint.setColor(mWarnColor);
+ invalidateSelf();
+ }
+ }
+
+ public void setElect(int elect) {
+ if (mElect == elect)
+ return;
+
+ mElect = elect;
+ updatePaint();
+ }
+
+ public void setElect(int elect, boolean warn) {
+ if (mElect == elect)
+ return;
+
+ mElect = elect;
+ updatePaint(warn);
+ }
+
+ private void updatePaint() {
+ updatePaint(isWarn());
+ }
+
+ private void updatePaint(boolean warn) {
+ if (warn)
+ mPaint.setColor(mWarnColor);
+ else
+ mPaint.setColor(mColor);
+ invalidateSelf();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // Empty
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ // Empty
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
diff --git a/src/com/hippo/ehviewer/gallery/image/GifImage.java b/src/com/hippo/ehviewer/gallery/image/GifImage.java
index 0215cddf3..b9e170c06 100644
--- a/src/com/hippo/ehviewer/gallery/image/GifImage.java
+++ b/src/com/hippo/ehviewer/gallery/image/GifImage.java
@@ -16,7 +16,6 @@
package com.hippo.ehviewer.gallery.image;
-import javax.microedition.khronos.opengles.GL11;
public class GifImage extends Image {
@@ -25,7 +24,7 @@ public class GifImage extends Image {
private final int mImageCount;
private final int[] mDelayArray;
private long mDelaySum;
- private final long mStartTime;
+ private long mStartTime;
private int mLastIndex = -1;
@@ -38,15 +37,19 @@ public class GifImage extends Image {
mDelaySum = 0;
for (int delay : delayArray)
mDelaySum += delay;
- mStartTime = System.currentTimeMillis();
}
private int getCurIndex() {
- long curTime = System.currentTimeMillis();
- long time = (curTime - mStartTime) % mDelaySum;
- int end = mImageCount - 1;
int index = 0;
- for (; index < end && (time -= mDelayArray[index]) > 0; index++);
+ if (mLastIndex == -1) {
+ index = 0;
+ mStartTime = System.currentTimeMillis();
+ } else {
+ long curTime = System.currentTimeMillis();
+ long time = (curTime - mStartTime) % mDelaySum;
+ int end = mImageCount - 1;
+ for (; index < end && (time -= mDelayArray[index]) > 0; index++);
+ }
return index;
}
@@ -60,7 +63,7 @@ public class GifImage extends Image {
if (mNativeImage != 0) {
int index = getCurIndex();
if (mLastIndex != index) {
- nativeRender(GL11.GL_TEXTURE_2D, 0, 0, 0, mFormat, mType, mNativeImage, FORMAT_GIF, index);
+ nativeRender(mFormat, mType, mNativeImage, FORMAT_GIF, index);
mLastIndex = index;
}
}
@@ -70,6 +73,5 @@ public class GifImage extends Image {
System.loadLibrary("image");
}
- private static native void nativeRender(int target, int level, int xoffset,
- int yoffset, int format, int type, int nativeImage, int fileFormat, int index);
+ private static native void nativeRender(int format, int type, int nativeImage, int fileFormat, int index);
}
diff --git a/src/com/hippo/ehviewer/gallery/image/Image.java b/src/com/hippo/ehviewer/gallery/image/Image.java
index c17ce6db6..4ffe7f766 100644
--- a/src/com/hippo/ehviewer/gallery/image/Image.java
+++ b/src/com/hippo/ehviewer/gallery/image/Image.java
@@ -18,8 +18,6 @@ package com.hippo.ehviewer.gallery.image;
import java.io.InputStream;
-import javax.microedition.khronos.opengles.GL11;
-
public class Image {
@@ -74,8 +72,7 @@ public class Image {
public void render() {
if (mNativeImage != 0) {
- nativeRender(GL11.GL_TEXTURE_2D, 0, 0, 0,
- mFormat, mType, mNativeImage, mFileFormat);
+ nativeRender(mFormat, mType, mNativeImage, mFileFormat);
}
}
@@ -108,5 +105,5 @@ public class Image {
private static native Image nativeDecodeStream(InputStream is, int format);
private static native void nativeFree(int mNativeImage, int format);
- private static native void nativeRender(int target, int level, int xoffset, int yoffset, int format, int type, int nativeImage, int fileFormat);
+ private static native void nativeRender(int format, int type, int nativeImage, int fileFormat);
}
diff --git a/src/com/hippo/ehviewer/widget/BatteryView.java b/src/com/hippo/ehviewer/widget/BatteryView.java
new file mode 100644
index 000000000..f3d2d55e4
--- /dev/null
+++ b/src/com/hippo/ehviewer/widget/BatteryView.java
@@ -0,0 +1,158 @@
+/*
+ * 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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.BatteryManager;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.hippo.ehviewer.R;
+import com.hippo.ehviewer.drawable.BatteryDrawable;
+import com.hippo.ehviewer.util.Ui;
+
+public class BatteryView extends TextView {
+ @SuppressWarnings("unused")
+ private static final String TAG = BatteryView.class.getSimpleName();
+
+ private int mLevel = 0;
+ private boolean mCharging = false;
+
+ private BatteryDrawable mDrawable;
+
+ private boolean mAttached = false;
+ private boolean mIsChargerWorking = false;
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
+ * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ boolean charging = (intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
+ == BatteryManager.BATTERY_STATUS_CHARGING);
+
+ if (mLevel != level || mCharging != charging) {
+ mLevel = level;
+ mCharging = charging;
+ if (mCharging && mLevel != 100) {
+ startCharger();
+ } else {
+ stopCharger();
+ mDrawable.setElect(mLevel);
+ }
+ setText(String.valueOf(mLevel) + "%");
+ }
+ }
+ };
+
+ private final Runnable mCharger = new Runnable() {
+
+ private int level = 0;
+
+ @Override
+ public void run() {
+ level += 5;
+ if (level > mLevel)
+ level = 0;
+ mDrawable.setElect(level, mLevel <= BatteryDrawable.WARN_LIMIT);
+ getHandler().postDelayed(mCharger, 500);
+ }
+ };
+
+ public BatteryView(Context context) {
+ super(context);
+ init();
+ }
+
+ public BatteryView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ init();
+
+ TypedArray typedArray = context.obtainStyledAttributes(
+ attrs, R.styleable.BatteryView, defStyleAttr, 0);
+ int color = typedArray.getColor(R.styleable.BatteryView_bvColor, Color.WHITE);
+ typedArray.recycle();
+
+ mDrawable.setColor(color);
+ }
+
+ private void init() {
+ mDrawable = new BatteryDrawable();
+ // TODO how to get size ?
+ int height = Ui.dp2pix(12);
+ mDrawable.setBounds(0, 0, (int) (height / 0.618f), height);
+ setCompoundDrawables(mDrawable, null, null, null);
+ setCompoundDrawablePadding(Ui.dp2pix(5));
+ }
+
+ private void startCharger() {
+ if (!mIsChargerWorking) {
+ getHandler().post(mCharger);
+ mIsChargerWorking = true;
+ }
+ }
+
+ private void stopCharger() {
+ if (mIsChargerWorking) {
+ getHandler().removeCallbacks(mCharger);
+ mIsChargerWorking = false;
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (!mAttached) {
+ mAttached = true;
+
+ registerReceiver();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mAttached) {
+ unregisterReceiver();
+ stopCharger();
+ mAttached = false;
+ }
+ }
+
+ private void registerReceiver() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
+ }
+
+ private void unregisterReceiver() {
+ getContext().unregisterReceiver(mIntentReceiver);
+ }
+}