+
diff --git a/app/src/main/java/com/hippo/util/ViewUtils.java b/app/src/main/java/com/hippo/util/ViewUtils.java
new file mode 100644
index 000000000..9c1368cfb
--- /dev/null
+++ b/app/src/main/java/com/hippo/util/ViewUtils.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015 Hippo Seven
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hippo.util;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+public final class ViewUtils {
+
+ /**
+ * Get view center location in window
+ *
+ * @param view the view to check
+ * @param location an array of two integers in which to hold the coordinates
+ */
+ public static void getCenterInWindows(View view, int[] location) {
+ getLocationInWindow(view, location);
+ location[0] += view.getWidth() / 2;
+ location[1] += view.getHeight() / 2;
+ }
+
+ /**
+ * Get view location in window
+ *
+ * @param view the view to check
+ * @param location an array of two integers in which to hold the coordinates
+ */
+ public static void getLocationInWindow(View view, int[] location) {
+ if (location == null || location.length < 2) {
+ throw new IllegalArgumentException(
+ "location must be an array of two integers");
+ }
+
+ float[] position = new float[2];
+
+ position[0] = view.getLeft();
+ position[1] = view.getTop();
+
+ ViewParent viewParent = view.getParent();
+ while (viewParent instanceof View) {
+ view = (View) viewParent;
+ if (view.getId() == android.R.id.content) {
+ break;
+ }
+
+ position[0] -= view.getScrollX();
+ position[1] -= view.getScrollY();
+
+ position[0] += view.getLeft();
+ position[1] += view.getTop();
+
+ viewParent = view.getParent();
+ }
+
+ location[0] = (int) (position[0] + 0.5f);
+ location[1] = (int) (position[1] + 0.5f);
+ }
+
+ /**
+ * Look for a ancestor view with the given id. If this view has the given
+ * id, return this view.
+ *
+ * @param view the view to start with
+ * @param id The id to search for.
+ * @return The view that has the given id in the hierarchy or null
+ */
+ public static View getAncestor(View view, int id) {
+ if (view.getId() == id) {
+ return view;
+ }
+
+ ViewParent viewParent = view.getParent();
+ while (viewParent instanceof View) {
+ view = (View) viewParent;
+ if (view.getId() == id) {
+ return view;
+ }
+ viewParent = view.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Returns a bitmap showing a screenshot of the view passed in.
+ *
+ * @param v The view to get screenshot
+ * @return The screenshot
+ */
+ public static Bitmap getBitmapFromView(View v) {
+ int width = v.getWidth();
+ int height = v.getHeight();
+ if (width == 0 && height == 0) {
+ width = v.getMeasuredWidth();
+ height = v.getMeasuredHeight();
+ }
+ Bitmap bitmap = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.translate(-v.getScrollX(), -v.getScrollY());
+ v.draw(canvas);
+ return bitmap;
+ }
+
+ /**
+ * Remove view from its parent
+ *
+ * @param view the view to remove
+ */
+ public static void removeFromParent(View view) {
+ ViewParent vp = view.getParent();
+ if (vp instanceof ViewGroup)
+ ((ViewGroup) vp).removeView(view);
+ }
+
+ /**
+ * Method that removes the support for HardwareAcceleration from a
+ * {@link android.view.View}.
+ *
+ * Check AOSP notice:
+ *
+ *
+ * 'ComposeShader can only contain shaders of different types (a BitmapShader and a
+ * LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
+ * application is affected by any of these missing features or limitations, you can turn
+ * off hardware acceleration for just the affected portion of your application by calling
+ * setLayerType(View.LAYER_TYPE_SOFTWARE, null).'
+ *
+ *
+ * @param v
+ * The view
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static void removeHardwareAccelerationSupport(View v) {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
+ if (v.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
+ v.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ }
+ }
+
+ public static void measureView(View v, int width, int height) {
+ int widthMeasureSpec;
+ int heightMeasureSpec;
+ if (width == ViewGroup.LayoutParams.WRAP_CONTENT)
+ widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
+ View.MeasureSpec.UNSPECIFIED);
+ else
+ widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(width, 0),
+ View.MeasureSpec.EXACTLY);
+ if (height == ViewGroup.LayoutParams.WRAP_CONTENT)
+ heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
+ View.MeasureSpec.UNSPECIFIED);
+ else
+ heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(height, 0),
+ View.MeasureSpec.EXACTLY);
+
+ v.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Determine if the supplied view is under the given point in the
+ * parent view's coordinate system.
+ *
+ * @param view Child view of the parent to hit test
+ * @param x X position to test in the parent's coordinate system
+ * @param y Y position to test in the parent's coordinate system
+ * @return true if the supplied view is under the given point, false otherwise
+ */
+ public static boolean isViewUnder(View view, int x, int y) {
+ if (view == null || view.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ return x >= view.getLeft() &&
+ x < view.getRight() &&
+ y >= view.getTop() &&
+ y < view.getBottom();
+ }
+
+ /**
+ * Set the enabled state of this view.
+ *
+ * @param v the view to set the enabled state
+ * @param visibility One of GONE, INVISIBLE, or GONE.
+ */
+ public static void setVisibility(View v, int visibility) {
+ if (visibility != v.getVisibility()) {
+ v.setVisibility(visibility);
+ }
+ }
+
+ public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
+
+ public static final int MEASURED_STATE_MASK = 0xff000000;
+
+ /**
+ * Utility to reconcile a desired size and state, with constraints imposed
+ * by a MeasureSpec. Will take the desired size, unless a different size
+ * is imposed by the constraints. The returned value is a compound integer,
+ * with the resolved size in the MEASURED_SIZE_MASK bits and
+ * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
+ * size is smaller than the size the view wants to be.
+ *
+ * @param size How big the view wants to be
+ * @param measureSpec Constraints imposed by the parent
+ * @return Size information bit mask as defined by
+ */
+ public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
+ int result = size;
+ int specMode = View.MeasureSpec.getMode(measureSpec);
+ int specSize = View.MeasureSpec.getSize(measureSpec);
+ switch (specMode) {
+ case View.MeasureSpec.UNSPECIFIED:
+ result = size;
+ break;
+ case View.MeasureSpec.AT_MOST:
+ if (specSize < size) {
+ result = specSize | MEASURED_STATE_TOO_SMALL;
+ } else {
+ result = size;
+ }
+ break;
+ case View.MeasureSpec.EXACTLY:
+ result = specSize;
+ break;
+ }
+ return result | (childMeasuredState&MEASURED_STATE_MASK);
+ }
+
+}
diff --git a/app/src/main/java/com/hippo/widget/FixedAspectImageView.java b/app/src/main/java/com/hippo/widget/FixedAspectImageView.java
new file mode 100644
index 000000000..2d78f6190
--- /dev/null
+++ b/app/src/main/java/com/hippo/widget/FixedAspectImageView.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2014-2015 Hippo Seven
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hippo.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.hippo.ehviewer.R;
+import com.hippo.util.ViewUtils;
+
+/**
+ * aspect is width / height
+ *
+ * @author Hippo
+ *
+ */
+
+public class FixedAspectImageView extends ImageView {
+
+ private static final String TAG = FixedAspectImageView.class.getSimpleName();
+
+ private static final int[] ATTRS = new int[] {
+ android.R.attr.adjustViewBounds,
+ android.R.attr.maxWidth,
+ android.R.attr.maxHeight
+ };
+
+ private int mMaxWidth = Integer.MAX_VALUE;
+ private int mMaxHeight = Integer.MAX_VALUE;
+ private boolean mAdjustViewBounds = false;
+
+ private float mAspect = -1f;
+
+ public FixedAspectImageView(Context context) {
+ super(context);
+ }
+
+ public FixedAspectImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FixedAspectImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a;
+
+ // Make sure we get value from xml
+ a = context.obtainStyledAttributes(attrs, ATTRS, defStyle, 0);
+ setAdjustViewBounds(a.getBoolean(0, false));
+ setMaxWidth(a.getDimensionPixelSize(1, Integer.MAX_VALUE));
+ setMaxHeight(a.getDimensionPixelSize(2, Integer.MAX_VALUE));
+ a.recycle();
+
+ a = context.obtainStyledAttributes(
+ attrs, R.styleable.FixedAspectImageView, defStyle, 0);
+ setAspect(a.getFloat(R.styleable.FixedAspectImageView_aspect, -1f));
+ a.recycle();
+ }
+
+ @Override
+ public void setMaxWidth(int maxWidth) {
+ super.setMaxWidth(maxWidth);
+ mMaxWidth = maxWidth;
+ }
+
+ @Override
+ public void setMaxHeight(int maxHeight) {
+ super.setMaxHeight(maxHeight);
+ mMaxHeight = maxHeight;
+ }
+
+ @Override
+ public void setAdjustViewBounds(boolean adjustViewBounds) {
+ super.setAdjustViewBounds(adjustViewBounds);
+ mAdjustViewBounds = adjustViewBounds;
+ }
+
+ /**
+ * Enable aspect will set AdjustViewBounds true.
+ * Any negative float to disable it,
+ * disable Aspect will not disable AdjustViewBounds.
+ *
+ * @param ratio width/height
+ */
+ public void setAspect(float ratio) {
+ if (ratio > 0) {
+ mAspect = ratio;
+ setAdjustViewBounds(true);
+ } else {
+ mAspect = -1f;
+ }
+ requestLayout();
+ }
+
+ private int resolveAdjustedSize(int desiredSize, int maxSize,
+ int measureSpec) {
+ int result = desiredSize;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+ switch (specMode) {
+ case MeasureSpec.UNSPECIFIED:
+ /* Parent says we can be as big as we want. Just don't be larger
+ * than max size imposed on ourselves.
+ */
+ result = Math.min(desiredSize, maxSize);
+ break;
+ case MeasureSpec.AT_MOST:
+ // Parent says we can be as big as we want, up to specSize.
+ // Don't be larger than specSize, and don't be larger than
+ // the max size imposed on ourselves.
+ result = Math.min(Math.min(desiredSize, specSize), maxSize);
+ break;
+ case MeasureSpec.EXACTLY:
+ // No choice. Do what we are told.
+ result = specSize;
+ break;
+ }
+ return result;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int w;
+ int h;
+
+ // Desired aspect ratio of the view's contents (not including padding)
+ float desiredAspect = 0.0f;
+
+ // We are allowed to change the view's width
+ boolean resizeWidth = false;
+
+ // We are allowed to change the view's height
+ boolean resizeHeight = false;
+
+ final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ Drawable drawable = getDrawable();
+ if (drawable == null) {
+ // If no drawable, its intrinsic size is 0.
+ w = h = 0;
+
+ if (mAdjustViewBounds && mAspect > 0.0f) {
+ resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
+ resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
+ desiredAspect = mAspect;
+ }
+ } else {
+ w = drawable.getIntrinsicWidth();
+ h = drawable.getIntrinsicHeight();
+ if (w <= 0) w = 1;
+ if (h <= 0) h = 1;
+
+ // We are supposed to adjust view bounds to match the aspect
+ // ratio of our drawable. See if that is possible.
+ if (mAdjustViewBounds) {
+ resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
+ resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
+
+ desiredAspect = mAspect <= 0.0f ? ((float) w / (float) h) : mAspect;
+ }
+ }
+
+ int pleft = getPaddingLeft();
+ int pright = getPaddingRight();
+ int ptop = getPaddingTop();
+ int pbottom = getPaddingBottom();
+
+ int widthSize;
+ int heightSize;
+
+ if (resizeWidth || resizeHeight) {
+ // If we get here, it means we want to resize to match the
+ // drawables aspect ratio, and we have the freedom to change at
+ // least one dimension.
+
+ // Get the max possible width given our constraints
+ widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);
+
+ // Get the max possible height given our constraints
+ heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
+
+ if (desiredAspect != 0.0f) {
+ // See what our actual aspect ratio is
+ float actualAspect = (float)(widthSize - pleft - pright) /
+ (heightSize - ptop - pbottom);
+
+ if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
+ boolean done = false;
+
+ // Try adjusting width to be proportional to height
+ if (resizeWidth) {
+ int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
+ pleft + pright;
+
+ // Allow the width to outgrow its original estimate if height is fixed.
+ if (!resizeHeight) {
+ widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
+ }
+
+ if (newWidth <= widthSize) {
+ widthSize = newWidth;
+ done = true;
+ }
+ }
+
+ // Try adjusting height to be proportional to width
+ if (!done && resizeHeight) {
+ int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
+ ptop + pbottom;
+
+ // Allow the height to outgrow its original estimate if width is fixed.
+ if (!resizeWidth) {
+ heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
+ heightMeasureSpec);
+ }
+
+ if (newHeight <= heightSize) {
+ heightSize = newHeight;
+ }
+ }
+ }
+ }
+ } else {
+ // We are either don't want to preserve the drawables aspect ratio,
+ // or we are not allowed to change view dimensions. Just measure in
+ // the normal way.
+ w += pleft + pright;
+ h += ptop + pbottom;
+
+ w = Math.max(w, getSuggestedMinimumWidth());
+ h = Math.max(h, getSuggestedMinimumHeight());
+
+ widthSize = ViewUtils.resolveSizeAndState(w, widthMeasureSpec, 0);
+ heightSize = ViewUtils.resolveSizeAndState(h, heightMeasureSpec, 0);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ }
+}
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index 96a442e5b..cdff1c684 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 359047dfa..27ba08261 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 71c6d760f..df61d54cc 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 20a47a046..cf14a2f27 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d668712cf
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..a5dc02b9a
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8987d8e18..04a7db132 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,5 @@
+
EhViewer
+