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); + } +}