diff --git a/app/build.gradle b/app/build.gradle index 7a8dc7bc0..33810660f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,7 +23,7 @@ android { sourceSets { main { - java.srcDirs = ['src/main/java', 'src/main/java-gen'] + java.srcDirs += 'src/main/java-gen' // Disable default NdkCompile task jni.srcDirs = [] // Set new jni lib dir diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java b/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java index 50d552be1..52ad36f20 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser; @@ -33,6 +34,8 @@ import com.hippo.scene.StageActivity; public class EhUrlOpener { + private static final String TAG = EhUrlOpener.class.getSimpleName(); + public static boolean openUrl(Activity activity, String url) { if (TextUtils.isEmpty(url)) { return false; @@ -79,6 +82,8 @@ public class EhUrlOpener { return true; } + Log.i(TAG, "Can't parse url: " + url); + return false; } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java index 730418596..1848f30bc 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java @@ -16,7 +16,6 @@ package com.hippo.ehviewer.client.parser; -import android.os.Environment; import android.util.Log; import com.hippo.ehviewer.client.EhUtils; @@ -270,7 +269,7 @@ public class GalleryDetailParser { if (largePreviewSet.size() == 0) { Log.d("TAG", "largePreviewSet.size() == 0"); try { - OutputStream os = new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "empty.txt")); + OutputStream os = new FileOutputStream(new File("/data/data/com.hippo.ehviewer/cache/empty.txt")); os.write(body.getBytes("utf-8")); os.close(); } catch (Exception e) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index 977f5e88b..d13ed5a99 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -19,31 +19,178 @@ package com.hippo.ehviewer.ui; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.view.MotionEvent; import com.hippo.ehviewer.R; +import com.hippo.ehviewer.ui.gl.GalleryPageView; +import com.hippo.ehviewer.ui.gl.GalleryView; import com.hippo.gl.view.GLRootView; -import com.hippo.gl.view.GLView; +import com.hippo.util.Dpad; public class GalleryActivity extends AppCompatActivity { + private GalleryView mGalleryView; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gallery); - GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); - - GLView contentPane = new GLView(); - - GLView glView = new GLView(); - contentPane.addComponent(glView); - - - glRootView.setContentPane(contentPane); - - - - + mGalleryView = new GalleryView(this, new InvalidPageIterator(), null); + glRootView.setContentPane(mGalleryView); } + + @Override + protected void onDestroy() { + super.onDestroy(); + mGalleryView = null; + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + switch (Dpad.getDirectionPressed(event)) { + case Dpad.LEFT: + // TODO do something + return true; + case Dpad.UP: + // TODO do something + return true; + case Dpad.RIGHT: + // TODO do something + return true; + case Dpad.DOWN: + // TODO do something + return true; + case Dpad.CENTER: + // TODO do something + return true; + } + + return super.onGenericMotionEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Check volume + if (false) { // TODO Setting.getVolumePage() + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + // TODO do something + return true; + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + // TODO do something + return true; + } + } + + // Check keyboard and Dpad + switch (keyCode) { + case KeyEvent.KEYCODE_PAGE_UP: + // TODO do something + return true; + case KeyEvent.KEYCODE_PAGE_DOWN: + // TODO do something + return true; + case KeyEvent.KEYCODE_DPAD_LEFT: + // TODO do something + return true; + case KeyEvent.KEYCODE_DPAD_UP: + // TODO do something + return true; + case KeyEvent.KEYCODE_DPAD_RIGHT: + // TODO do something + return true; + case KeyEvent.KEYCODE_DPAD_DOWN: + // TODO do something + return true; + case KeyEvent.KEYCODE_DPAD_CENTER: + // TODO do something + return true; + case KeyEvent.KEYCODE_SPACE: + // TODO do something + return true; + case KeyEvent.KEYCODE_MENU: + // TODO do something + return true; + } + + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + // Check volume + if (false) { // TODO Setting.getVolumePage() + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || + keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + return true; + } + } + + // Check keyboard and Dpad + if (keyCode == KeyEvent.KEYCODE_PAGE_UP || + keyCode == KeyEvent.KEYCODE_PAGE_DOWN || + keyCode == KeyEvent.KEYCODE_DPAD_LEFT || + keyCode == KeyEvent.KEYCODE_DPAD_UP || + keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || + keyCode == KeyEvent.KEYCODE_DPAD_DOWN || + keyCode == KeyEvent.KEYCODE_DPAD_CENTER || + keyCode == KeyEvent.KEYCODE_SPACE || + keyCode == KeyEvent.KEYCODE_MENU) { + return true; + } + + return super.onKeyUp(keyCode, event); + } + + + private class InvalidPageIterator implements GalleryView.PageIterator { + + @Override + public void mark() { + + } + + @Override + public void reset() { + + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public void next() { + + } + + @Override + public void previous() { + + } + + @Override + public void bind(GalleryPageView view) { + + } + + @Override + public void unbind(GalleryPageView view) { + + } + } + } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index 534540fbb..4065d6419 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -26,6 +26,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.NavigationView; import android.support.v4.widget.DrawerLayout; +import android.util.Log; import android.view.Gravity; import android.view.MenuItem; import android.widget.Toast; @@ -90,6 +91,8 @@ public final class MainActivity extends StageActivity @Override protected void onNewIntent(Intent intent) { + Log.d("TAG", "onNewIntent"); + super.onNewIntent(intent); if (!handleIntent(intent) && intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { Toast.makeText(this, R.string.error_cannot_parse_the_url, Toast.LENGTH_SHORT).show(); @@ -97,6 +100,9 @@ public final class MainActivity extends StageActivity } private void onInit() { + Log.d("TAG", "onInit"); + + Intent intent = getIntent(); if (!handleIntent(intent)) { if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { @@ -125,6 +131,8 @@ public final class MainActivity extends StageActivity } private void onRestore(Bundle savedInstanceState) { + Log.d("TAG", "onRestore"); + mNavCheckedItem = savedInstanceState.getInt(KEY_NAV_CHECKED_ITEM); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java new file mode 100644 index 000000000..ac9eb3512 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java @@ -0,0 +1,189 @@ +/* + * Copyright 2016 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.ui.gl; + +import android.content.Context; +import android.hardware.SensorManager; +import android.view.ViewConfiguration; +import android.view.animation.Interpolator; + +import com.hippo.gl.anim.Animation; + +class Fling extends Animation { + + private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); + private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) + private static final float START_TENSION = 0.5f; + private static final float END_TENSION = 1.0f; + private static final float P1 = START_TENSION * INFLEXION; + private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION); + private static final float FLING_FRICTION = ViewConfiguration.getScrollFriction(); + + private static final int NB_SAMPLES = 100; + private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; + private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; + + private float mPhysicalCoeff; + + //private GalleryPageView mPage; + private int mDx; + private int mDy; + private int mLastX; + private int mLastY; + private int[] mTemp = new int[2]; + + static { + float x_min = 0.0f; + float y_min = 0.0f; + for (int i = 0; i < NB_SAMPLES; i++) { + final float alpha = (float) i / NB_SAMPLES; + + float x_max = 1.0f; + float x, tx, coef; + while (true) { + x = x_min + (x_max - x_min) / 2.0f; + coef = 3.0f * x * (1.0f - x); + tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x; + if (Math.abs(tx - alpha) < 1E-5) break; + if (tx > alpha) x_max = x; + else x_min = x; + } + SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x; + + float y_max = 1.0f; + float y, dy; + while (true) { + y = y_min + (y_max - y_min) / 2.0f; + coef = 3.0f * y * (1.0f - y); + dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y; + if (Math.abs(dy - alpha) < 1E-5) break; + if (dy > alpha) y_max = y; + else y_min = y; + } + SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y; + } + SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; + } + + private static final Interpolator FLING_INTERPOLATOR = new Interpolator() { + @Override + public float getInterpolation(float input) { + final int index = (int) (NB_SAMPLES * input); + float distanceCoef = 1.f; + float velocityCoef = 0.f; + if (index < NB_SAMPLES) { + final float t_inf = (float) index / NB_SAMPLES; + final float t_sup = (float) (index + 1) / NB_SAMPLES; + final float d_inf = SPLINE_POSITION[index]; + final float d_sup = SPLINE_POSITION[index + 1]; + velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); + distanceCoef = d_inf + (input - t_inf) * velocityCoef; + } + return distanceCoef; + } + }; + + public Fling(Context context) { + final float ppi = context.getResources().getDisplayMetrics().density * 160.0f; + mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2) + * 39.37f // inch/meter + * ppi + * 0.84f; // look and feel tuning + } + + private double getSplineDeceleration(int velocity) { + return Math.log(INFLEXION * Math.abs(velocity) / (FLING_FRICTION * mPhysicalCoeff)); + } + + /* Returns the duration, expressed in milliseconds */ + private int getSplineFlingDuration(int velocity) { + final double l = getSplineDeceleration(velocity); + final double decelMinusOne = DECELERATION_RATE - 1.0; + return (int) (1000.0 * Math.exp(l / decelMinusOne)); + } + + private double getSplineFlingDistance(int velocity) { + final double l = getSplineDeceleration(velocity); + final double decelMinusOne = DECELERATION_RATE - 1.0; + return FLING_FRICTION * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l); + } + + /** + * Modifies mDuration to the duration it takes to get from start to newFinal using the + * spline interpolation. The previous duration was needed to get to oldFinal. + **/ + private int adjustDuration(int start, int oldFinal, int newFinal, int duration) { + final int oldDistance = oldFinal - start; + final int newDistance = newFinal - start; + final float x = Math.abs((float) newDistance / oldDistance); + final int index = (int) (NB_SAMPLES * x); + if (index < NB_SAMPLES) { + final float x_inf = (float) index / NB_SAMPLES; + final float x_sup = (float) (index + 1) / NB_SAMPLES; + final float t_inf = SPLINE_TIME[index]; + final float t_sup = SPLINE_TIME[index + 1]; + final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf); + duration *= timeCoef; + } + return duration; + } + + public void startFling(GalleryPageView page, int velocityX, int minX, int maxX, + int velocityY, int minY, int maxY) { + //mPage = page; + mDx = (int) (getSplineFlingDistance(velocityX) * Math.signum(velocityX)); + mDy = (int) (getSplineFlingDistance(velocityY) * Math.signum(velocityY)); + mLastX = 0; + mLastY = 0; + int durationX = getSplineFlingDuration(velocityX); + int durationY = getSplineFlingDuration(velocityY); + + if (mDx < minX) { + durationX = adjustDuration(0, mDx, minX, durationX); + mDx = minX; + } + if (mDx > maxX) { + durationX = adjustDuration(0, mDx, maxX, durationX); + mDx = maxX; + } + if (mDy < minY) { + durationY = adjustDuration(0, mDy, minY, durationY); + mDy = minY; + } + if (mDy > maxY) { + durationY = adjustDuration(0, mDy, maxY, durationY); + mDy = maxY; + } + + setDuration(Math.max(durationX, durationY)); + start(); + // TODO invalidate(); + } + + @Override + protected void onCalculate(float progress) { + int x = (int) (mDx * progress); + int y = (int) (mDy * progress); + int offsetX = x - mLastX; + int offsetY = y - mLastY; + if (offsetX != 0 || offsetY != 0) { + // TODO mPage.scroll(-offsetX, -offsetY, mTemp); + } + mLastX = x; + mLastY = y; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java index 6333b5701..59f58e8cd 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java @@ -16,109 +16,68 @@ package com.hippo.ehviewer.ui.gl; -import android.graphics.Rect; +import android.content.Context; -import com.hippo.gl.view.GLView; -import com.hippo.gl.widget.FrameLayout; -import com.hippo.gl.widget.ScaledImageView; +import com.hippo.gl.glrenderer.TextTexture; +import com.hippo.gl.view.Gravity; +import com.hippo.gl.widget.GLFrameLayout; +import com.hippo.gl.widget.GLLinearLayout; +import com.hippo.gl.widget.GLProgressView; +import com.hippo.gl.widget.GLTextView; +import com.hippo.gl.widget.GLTextureView; +import com.hippo.yorozuya.LayoutUtils; -public class GalleryPageView extends FrameLayout { +public class GalleryPageView extends GLFrameLayout { - public ScaledImageView mImageView; - //public LinearLayout mLinearLayout; - //public TextView mIndexView; - //public ProgressView mProgressView; + public ImageView mImage; + public GLTextView mPage; + public GLTextureView mError; + public GLProgressView mProgress; - private Rect mSeen = new Rect(); + // TODO + private static TextTexture sPageTextTexture; - //private static TextTexture sTextTexture; + public GalleryPageView(Context context) { + // Add image + mImage = new ImageView(); + GravityLayoutParams glp = new GravityLayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + addComponent(mImage, glp); - //public static void setTextTexture(TextTexture textTexture) { - // sTextTexture = textTexture; - //} + // Add other panel + GLLinearLayout ll = new GLLinearLayout(); + ll.setOrientation(GLLinearLayout.VERTICAL); + glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + glp.gravity = Gravity.CENTER; + addComponent(ll, glp); - public GalleryPageView() { - mImageView = new ScaledImageView(); - //mLinearLayout = new LinearLayout(); - //mLinearLayout.setInterval(LayoutUtils.dp2pix(context, 24)); // 12dp - //mIndexView = new TextView(sTextTexture); - //mProgressView = new ProgressView(); - // mProgressView.setMinimumWidth(LayoutUtils.dp2pix(context, 56)); // 56dp - //mProgressView.setMinimumHeight(LayoutUtils.dp2pix(context, 56)); // 56dp - - /* - GravityLayoutParams lp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, + // Add page + mPage = new GLTextView(); + mPage.setTextTexture(sPageTextTexture); + GLLinearLayout.LayoutParams lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_HORIZONTAL; - mLinearLayout.addComponent(mIndexView, lp); - //lp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, - // LayoutParams.WRAP_CONTENT); - //lp.gravity = Gravity.CENTER_HORIZONTAL; - //mLinearLayout.addComponent(mProgressView, lp); + ll.addComponent(mPage, lp); - - - lp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, + // Add error + mError = new GLTextureView(); + lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER; + lp.gravity = Gravity.CENTER_HORIZONTAL; + ll.addComponent(mError, lp); - addComponent(mLinearLayout, lp); - */ - - GLView.GravityLayoutParams lp = new GLView.GravityLayoutParams(GLView.LayoutParams.MATCH_PARENT, - GLView.LayoutParams.MATCH_PARENT); - addComponent(mImageView, lp); + // Add progress + mProgress = new GLProgressView(); + mProgress.setMinimumWidth(LayoutUtils.dp2pix(context, 56)); // 56dp + mProgress.setMinimumHeight(LayoutUtils.dp2pix(context, 56)); // 56dp + lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + ll.addComponent(mProgress, lp); } - private void updateSeen() { - Rect bounds = bounds(); - Rect parentBounds = mParent.bounds(); - mSeen.set(bounds); - if (!mSeen.intersect(0, 0, parentBounds.width(), parentBounds.height())) { - mSeen.set(0, 0, 0, 0); - } else { - mSeen.offset(-bounds.left, -bounds.top); - } - mImageView.setSeen(mSeen); - } - - @Override - public void layout(int left, int top, int right, int bottom) { - super.layout(left, top, right, bottom); - updateSeen(); - } - - public ScaledImageView getImageView() { - return mImageView; - } - - public void setAnimateState(int position) { - mImageView.setAnimateState(position); - mImageView.setAnimateState(position); - } - - public boolean isLoaded() { - return mImageView.isLoaded(); - } - - public void setScaleOffset(@GalleryView.Scale int scale, - @GalleryView.StartPosition int startPosition, float lastScale) { - mImageView.setScaleOffset(scale, startPosition, lastScale); - } - - public void scroll(int dx, int dy, int[] remain) { - mImageView.scroll(dx, dy, remain); - } - - public void scale(float focusX, float focusY, float scale) { - mImageView.scale(focusX, focusY, scale); - } - - public float getFitScale() { - return mImageView.getFitScale(); - } - - public float getScale() { - return mImageView.getScale(); + public ImageView getImageView() { + return mImage; } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java index fbd47fc4b..5aaea4c74 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java @@ -18,32 +18,26 @@ package com.hippo.ehviewer.ui.gl; import android.content.Context; import android.graphics.Rect; -import android.graphics.RectF; -import android.hardware.SensorManager; import android.support.annotation.IntDef; -import android.util.SparseArray; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.view.MotionEvent; -import android.view.ViewConfiguration; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; -import com.hippo.gl.anim.Animation; +import com.hippo.ehviewer.R; import com.hippo.gl.glrenderer.GLCanvas; -import com.hippo.gl.glrenderer.ImageTexture; import com.hippo.gl.util.GalleryUtils; import com.hippo.gl.view.AnimationTime; import com.hippo.gl.view.GLRoot; import com.hippo.gl.view.GLView; -import com.hippo.gl.widget.EdgeView; -import com.hippo.gl.widget.ScaledImageView; -import com.hippo.yorozuya.IntArray; +import com.hippo.gl.widget.GLEdgeView; +import com.hippo.gl.widget.GLProgressView; import com.hippo.yorozuya.LayoutUtils; -import com.hippo.yorozuya.MathUtils; +import com.hippo.yorozuya.ResourcesUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; import java.util.Stack; public class GalleryView extends GLView implements GestureRecognizer.Listener { @@ -69,6 +63,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { private static final int MAX_SETTLE_DURATION = 600; // ms private static final int BACKGROUND_COLOR = 0xff212121; private static final int INTERVAL_IN_DP = 24; + private static final int PROGRESS_SIZE_IN_DP = 48; private static final int PAGE_STATE_UNKNOWN_SEEN = 0; private static final int PAGE_STATE_SEEN = 1; @@ -90,117 +85,35 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public static final int LAYOUT_MODE_RIGHT_TO_LEFT = 1; public static final int LAYOUT_MODE_TOP_TO_BOTTOM = 2; - private static final Interpolator SMOOTH_SCROLLER_INTERPOLATOR = new Interpolator() { - @Override - public float getInterpolation(float t) { - t -= 1.0f; - return t * t * t * t * t + 1.0f; - } - }; - private static final Interpolator SMOOTH_SCALER_INTERPOLATOR = new OvershootInterpolator(0.7f); - private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); - private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) - private static final float START_TENSION = 0.5f; - private static final float END_TENSION = 1.0f; - private static final float P1 = START_TENSION * INFLEXION; - private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION); - private static final float FLING_FRICTION = ViewConfiguration.getScrollFriction(); - - private static final int NB_SAMPLES = 100; - private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; - private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; - - static { - float x_min = 0.0f; - float y_min = 0.0f; - for (int i = 0; i < NB_SAMPLES; i++) { - final float alpha = (float) i / NB_SAMPLES; - - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x; - if (Math.abs(tx - alpha) < 1E-5) break; - if (tx > alpha) x_max = x; - else x_min = x; - } - SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x; - - float y_max = 1.0f; - float y, dy; - while (true) { - y = y_min + (y_max - y_min) / 2.0f; - coef = 3.0f * y * (1.0f - y); - dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y; - if (Math.abs(dy - alpha) < 1E-5) break; - if (dy > alpha) y_max = y; - else y_min = y; - } - SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y; - } - SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; - } - - private static final Interpolator FLING_INTERPOLATOR = new Interpolator() { - @Override - public float getInterpolation(float input) { - final int index = (int) (NB_SAMPLES * input); - float distanceCoef = 1.f; - float velocityCoef = 0.f; - if (index < NB_SAMPLES) { - final float t_inf = (float) index / NB_SAMPLES; - final float t_sup = (float) (index + 1) / NB_SAMPLES; - final float d_inf = SPLINE_POSITION[index]; - final float d_sup = SPLINE_POSITION[index + 1]; - velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); - distanceCoef = d_inf + (input - t_inf) * velocityCoef; - } - return distanceCoef; - } - }; - private static final float[] LEFT_AREA = {0.0f, 0.0f, 1.0f / 3.0f, 1f}; private static final float[] RIGHT_AREA = {2.0f / 3.0f, 0.0f, 1.0f, 1f}; private static final float[] CENTER_AREA = {1.0f / 3.0f, 2.0f / 5.0f, 2.0f / 3.0f, 3.0f / 5.0f}; - private boolean mEnableRequestLayout = true; - + private Context mContext; private GestureRecognizer mGestureRecognizer; - private Adapter mAdapter; + private LayoutManager mPagerLayoutManager; + private LayoutManager mLayoutManager; - private int mFirstShownIndex = 0; - private int mLayoutOffset = 0; + private Recycler mRecycler; + private GLEdgeView mEdgeView; + private GLProgressView mProgressCache; - private SparseArray mAttachedPage = new SparseArray<>(); - private List mInvalidAttachedComponent = new ArrayList<>(); - private IntArray mMissedPageInfo = new IntArray(5 * 3); - private List mUsedPage = new ArrayList<>(); - private SparseArray mPageMap = new SparseArray<>(); + private int mProgressColor; - private EdgeView mEdgeView; + private boolean mEnableRequestLayout = true; private boolean mScale = false; private boolean mScroll = false; private boolean mFirstScroll = false; private boolean mTouched = false; - private boolean mCanScrollBetweenPagesHMode; - private Rect mLeftArea = new Rect(); private Rect mRightArea = new Rect(); private Rect mCenterArea = new Rect(); - private int mDeltaX; - private int mDeltaY; - private int[] mScrollRemain = new int[2]; - - private boolean mHaveChangePage = false; - @LayoutMode private int mLayoutMode = LAYOUT_MODE_RIGHT_TO_LEFT; @LayoutMode @@ -210,32 +123,25 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { private int mScaleMode = SCALE_FIT; @StartPosition private int mStartPosition = START_POSITION_TOP_LEFT; - private float mLastScale = 1f; - - public int mInterval; - - private Fling mFling; - private SmoothScroller mSmoothScroller; - private SmoothScaler mSmoothScaler; - private Recycler mRecycler; private boolean mRequestFill = false; private ActionListener mListener; - public GalleryView(Context context, int firstShownIndex, ActionListener listener) { - mFirstShownIndex = firstShownIndex; + public GalleryView(Context context, PageIterator iterator, ActionListener listener) { + mContext = context; mListener = listener; - mEdgeView = new EdgeView(context); + mEdgeView = new GLEdgeView(context); mGestureRecognizer = new GestureRecognizer(context, this); - mSmoothScroller = new SmoothScroller(); - mSmoothScroller.setInterpolator(SMOOTH_SCROLLER_INTERPOLATOR); - mSmoothScaler = new SmoothScaler(); - mSmoothScaler.setInterpolator(SMOOTH_SCALER_INTERPOLATOR); - mFling = new Fling(context); - mFling.setInterpolator(FLING_INTERPOLATOR); mRecycler = new Recycler(); - mInterval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); + + mProgressColor = ResourcesUtils.getAttrColor(context, R.attr.colorPrimary); + + int interval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); + int progressSize = LayoutUtils.dp2pix(context, PROGRESS_SIZE_IN_DP); + mPagerLayoutManager = new PagerLayoutManager(this, interval, progressSize); + mPagerLayoutManager.onAttach(iterator); + mLayoutManager = mPagerLayoutManager; setBackgroundColor(BACKGROUND_COLOR); } @@ -271,27 +177,11 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } */ - private void resetPageMoving() { - mSmoothScroller.forceStop(); - mSmoothScaler.forceStop(); - mFling.forceStop(); - mLayoutOffset = 0; - mHaveChangePage = false; - } - - public void setAdapter(Adapter adapter) { - mAdapter = adapter; - } - - public int getPages() { - return mAdapter.getPages(); - } - public void superRequestLayout() { super.requestLayout(); } - private void requestFill() { + public void requestFill() { if (GalleryUtils.isRenderThread()) { fill(); } else { @@ -300,100 +190,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } } - public void scrollToPrevious() { - scrollToPage(mFirstShownIndex - 1); - } - - public void scrollToNext() { - scrollToPage(mFirstShownIndex + 1); - } - - public void scrollToLeft() { - switch (mLastLayoutMode) { - case LAYOUT_MODE_LEFT_TO_RIGHT: - scrollToPrevious(); - break; - case LAYOUT_MODE_RIGHT_TO_LEFT: - scrollToNext(); - break; - case LAYOUT_MODE_TOP_TO_BOTTOM: - // TODO What about top to bottom - break; - } - } - - public void scrollToRight() { - switch (mLastLayoutMode) { - case LAYOUT_MODE_LEFT_TO_RIGHT: - scrollToNext(); - break; - case LAYOUT_MODE_RIGHT_TO_LEFT: - scrollToPrevious(); - break; - case LAYOUT_MODE_TOP_TO_BOTTOM: - // TODO What about top to bottom - break; - } - } - - public void scrollToPage(int page) { - scrollToPageInternal(page, false); - } - - private void scrollToPageInternal(int page, boolean internal) { - if (mAdapter == null || mAdapter.getPages() <= 0) { - return; - } - - if (page < 0) { - switch (mLastLayoutMode) { - case LAYOUT_MODE_LEFT_TO_RIGHT: - mEdgeView.onPull(getWidth() * 2, EdgeView.LEFT); - break; - case LAYOUT_MODE_RIGHT_TO_LEFT: - mEdgeView.onPull(getWidth() * 2, EdgeView.RIGHT); - break; - case LAYOUT_MODE_TOP_TO_BOTTOM: - // TODO What about top to bottom - break; - } - return; - } - - if (page >= mAdapter.getPages()) { - switch (mLastLayoutMode) { - case LAYOUT_MODE_LEFT_TO_RIGHT: - mEdgeView.onPull(getWidth() * 2, EdgeView.RIGHT); - break; - case LAYOUT_MODE_RIGHT_TO_LEFT: - mEdgeView.onPull(getWidth() * 2, EdgeView.LEFT); - break; - case LAYOUT_MODE_TOP_TO_BOTTOM: - // TODO What about top to bottom - break; - } - return; - } - - mSmoothScroller.forceStop(); - mSmoothScaler.forceStop(); - mFling.forceStop(); - - mFirstShownIndex = page; - mLayoutOffset = 0; - requestFill(); - - mListener.onScrollToPage(page, internal); - } - - public void smoothScrollToPage(int page) { - mSmoothScroller.forceStop(); - mSmoothScaler.forceStop(); - mFling.forceStop(); - - // TODO - } - @Override protected boolean dispatchTouchEvent(MotionEvent event) { // Do not pass event to component, so handle event here @@ -408,41 +204,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { @Override public boolean onSingleTapConfirmed(float x, float y) { - // TODO Let outside decide action - if ((mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT || mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT) && mLayoutOffset == 0) { - if (mLeftArea.contains((int) x, (int) y)) { - if (mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT) { - if (mFirstShownIndex > 0) { - scrollToPageInternal(mFirstShownIndex - 1, true); - } else { - mEdgeView.onPull(getWidth() * 2, (int) y, EdgeView.LEFT); - } - } else { - if (mFirstShownIndex < mAdapter.getPages() - 1) { - scrollToPageInternal(mFirstShownIndex + 1, true); - } else { - mEdgeView.onPull(getWidth() * 2, (int) y, EdgeView.LEFT); - } - } - } else if (mRightArea.contains((int) x, (int) y)) { - if (mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT) { - if (mFirstShownIndex < mAdapter.getPages() - 1) { - scrollToPageInternal(mFirstShownIndex + 1, true); - } else { - mEdgeView.onPull(getWidth() * 2, (int) y, EdgeView.RIGHT); - } - } else { - if (mFirstShownIndex > 0) { - scrollToPageInternal(mFirstShownIndex - 1, true); - } else { - mEdgeView.onPull(getWidth() * 2, (int) y, EdgeView.RIGHT); - } - } - } else if (mCenterArea.contains((int) x, (int) y)) { - mListener.onTapCenter(); - } - } - return true; } @@ -456,216 +217,18 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { if (mScale) { return false; } - - // TODO Let outside decide action - if ((mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT || mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT) && - mLayoutOffset == 0) { - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page != null && page.isLoaded()) { - float scale = page.getScale(); - float fitScale = page.getFitScale(); - float centerScale = (1f + fitScale) / 2; - float targetScale; - if ((fitScale > 1f && scale > centerScale) || - (fitScale < 1f && scale < centerScale)) { - targetScale = 1f; - } else { - targetScale = fitScale; - } - mSmoothScaler.startSmoothScaler(page, x, y, scale, targetScale, 500); // TODO - } - } - return true; } - private int doScrollBetweenPagesHMode(int dx) { - boolean reverse = mLastLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT; - int remain; - int width = getWidth(); - int newLayoutOffset = mLayoutOffset + dx; - if (!mHaveChangePage) { - if (mLayoutOffset == 0) { - if (dx > mInterval) { - mHaveChangePage = true; - mFirstShownIndex += reverse ? 1 : -1; - mLayoutOffset = -width; - remain = dx - mInterval; - } else if (dx < -mInterval) { - mHaveChangePage = true; - mFirstShownIndex += reverse ? -1 : 1; - mLayoutOffset = width; - remain = dx + mInterval; - } else { - mLayoutOffset = newLayoutOffset; - remain = 0; - } - } else if (mLayoutOffset > 0) { - if (newLayoutOffset > mInterval) { - mHaveChangePage = true; - mFirstShownIndex += reverse ? 1 : -1; - mLayoutOffset = -width; - remain = newLayoutOffset - mInterval; - } else if (newLayoutOffset < 0) { - mLayoutOffset = 0; - remain = newLayoutOffset; - } else { - mLayoutOffset = newLayoutOffset; - remain = 0; - } - } else { // mLayoutOffset < 0 - if (newLayoutOffset < -mInterval) { - mHaveChangePage = true; - mFirstShownIndex += reverse ? -1 : 1; - mLayoutOffset = width; - remain = newLayoutOffset + mInterval; - } else if (mLayoutOffset + dx > 0) { - mLayoutOffset = 0; - remain = newLayoutOffset; - } else { - mLayoutOffset = newLayoutOffset; - remain = 0; - } - } - - // Fix mHaveChangePage - if (mLayoutOffset < mInterval && mLayoutOffset > -mInterval) { - mHaveChangePage = false; - } - } else { - if (mLayoutOffset >= 0) { - if (newLayoutOffset > width) { - mHaveChangePage = false; - mFirstShownIndex += reverse ? 1 : -1; - mLayoutOffset = -mInterval; - remain = newLayoutOffset - width; - } else if (newLayoutOffset < mInterval) { - mHaveChangePage = false; - mLayoutOffset = mInterval; - remain = newLayoutOffset - mInterval; - } else { - mLayoutOffset = newLayoutOffset; - remain = 0; - } - } else { // mLayoutOffset < 0 - if (newLayoutOffset < -width) { - mHaveChangePage = false; - mFirstShownIndex += reverse ? -1 : 1; - mLayoutOffset = mInterval; - remain = newLayoutOffset + width; - } else if (newLayoutOffset > -mInterval) { - mHaveChangePage = false; - mLayoutOffset = -mInterval; - remain = newLayoutOffset + mInterval; - } else { - mLayoutOffset = newLayoutOffset; - remain = 0; - } - } - } - - return remain; + GLEdgeView getEdgeView() { + return mEdgeView; } - private int scrollBetweenPagesHMode(int dx) { - int remain; - if (mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT) { - if (mFirstShownIndex == 0 && mLayoutOffset - dx > 0) { // Start - doScrollBetweenPagesHMode(-mLayoutOffset); - remain = dx - mLayoutOffset; - } else if (mFirstShownIndex == mAdapter.getPages() - 1 && mLayoutOffset - dx < 0) { // end - doScrollBetweenPagesHMode(-mLayoutOffset); - remain = dx - mLayoutOffset; - } else { - remain = -doScrollBetweenPagesHMode(-dx); - } - } else if (mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT) { - if (mFirstShownIndex == 0 && mLayoutOffset - dx < 0) { // Start - doScrollBetweenPagesHMode(-mLayoutOffset); - remain = dx - mLayoutOffset; - } else if (mFirstShownIndex == mAdapter.getPages() - 1 && mLayoutOffset - dx > 0) { // end - doScrollBetweenPagesHMode(-mLayoutOffset); - remain = dx - mLayoutOffset; - } else { - remain = -doScrollBetweenPagesHMode(-dx); - } - } else { - throw new IllegalStateException("mLayoutMode can only be " + - "LAYOUT_MODE_LEFT_TO_RIGHT or LAYOUT_MODE_LEFT_TO_RIGHT"); - } - - return remain; - } - - - private void overScrollEdgeForHMode(float x, float y, int dx, int dy) { - if (dx == 0) { - if (mDeltaX != 0) { - mDeltaX = 0; - mEdgeView.onRelease(EdgeView.LEFT); - mEdgeView.onRelease(EdgeView.RIGHT); - } - } else if (dx < 0) { - if (mDeltaX > 0) { - mDeltaX = 0; - mEdgeView.onRelease(EdgeView.LEFT); - mEdgeView.onRelease(EdgeView.RIGHT); - } - mDeltaX += dx; - mEdgeView.onPull(-mDeltaX, y, EdgeView.LEFT); - } else { // dx > 0 - if (mDeltaX < 0) { - mDeltaX = 0; - mEdgeView.onRelease(EdgeView.LEFT); - mEdgeView.onRelease(EdgeView.RIGHT); - } - mDeltaX += dx; - mEdgeView.onPull(mDeltaX, y, EdgeView.RIGHT); - } - - // Only over scroll top bottom edge when image full cover vertically - if (mLayoutOffset != 0) { - return; - } - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page == null) { - return; - } - ScaledImageView imageView = page.getImageView(); - if (imageView == null) { - return; - } - ImageTexture imageTexture = imageView.getImageTexture(); - if (imageTexture == null) { - return; - } - if (imageView.getScale() * imageTexture.getHeight() <= getHeight()) { - return; - } - if (dy == 0) { - if (mDeltaY != 0) { - mDeltaY = 0; - mEdgeView.onRelease(EdgeView.TOP); - mEdgeView.onRelease(EdgeView.BOTTOM); - } - } else if (dy < 0) { - if (mDeltaY > 0) { - mDeltaY = 0; - mEdgeView.onRelease(EdgeView.TOP); - mEdgeView.onRelease(EdgeView.BOTTOM); - } - mDeltaY += dy; - mEdgeView.onPull(-mDeltaY, x, EdgeView.TOP); - } else { - if (mDeltaY < 0) { - mDeltaY = 0; - mEdgeView.onRelease(EdgeView.TOP); - mEdgeView.onRelease(EdgeView.BOTTOM); - } - mDeltaY += dy; - mEdgeView.onPull(mDeltaY, x, EdgeView.BOTTOM); - } + public boolean isFirstScroll() { + boolean firstScroll = mFirstScroll; + mFirstScroll = false; + return firstScroll; } @Override @@ -675,94 +238,14 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } mScroll = true; - if (mAdapter == null || mAdapter.getPages() <= 0) { - return true; - } - - if (mFirstScroll) { - mFirstScroll = false; - mCanScrollBetweenPagesHMode = Math.abs(dx) > Math.abs(dy) * 1.5; - } - - int dxI = (int) dx; - int dyI = (int) dy; - switch (mLayoutMode) { - case LAYOUT_MODE_LEFT_TO_RIGHT: - case LAYOUT_MODE_RIGHT_TO_LEFT: { - boolean canImageScroll = true; - boolean needFill = false; - boolean reverse = mLastLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT; - int remainX = dxI; - int remainY = dyI; - while (remainX != 0 || remainY != 0) { - if (mLayoutOffset != 0 || !canImageScroll) { - if (remainX == 0 || (mFirstShownIndex == 0 && mLayoutOffset == 0 && - (reverse ? -remainX < 0 : -remainX > 0)) || - (mFirstShownIndex == mAdapter.getPages() - 1 && mLayoutOffset == 0 && - (reverse ? -remainX > 0 : -remainX < 0))) { - // On edge - overScrollEdgeForHMode(x, y, remainX, remainY); - remainX = 0; - remainY = 0; - } else if (mCanScrollBetweenPagesHMode) { - remainX = scrollBetweenPagesHMode(remainX); - canImageScroll = true; - needFill = true; - - - } else { - remainX = 0; - remainY = 0; - } - } else { - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page != null) { - page.scroll(remainX, remainY, mScrollRemain); - remainX = mScrollRemain[0]; - remainY = mScrollRemain[1]; - } - canImageScroll = false; - } - } - - if (needFill) { - requestFill(); - } - - break; - } - - case LAYOUT_MODE_TOP_TO_BOTTOM: { - // TODO What about top to bottom - break; - } - } + mLayoutManager.onScroll(dx, dy, totalX, totalY, x, y); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - // Only over scroll top bottom edge when image full cover vertically - if (mLayoutOffset != 0) { - return false; - } - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page == null) { - return false; - } - ScaledImageView imageView = page.getImageView(); - if (imageView == null) { - return false; - } - ImageTexture imageTexture = imageView.getImageTexture(); - if (imageTexture == null) { - return false; - } - - RectF target = imageView.getTargetRect(); - mFling.startFling(page, (int) velocityX, getWidth() - (int) target.right, (int) -target.left, - (int) velocityY, getHeight() - (int) target.bottom, (int) -target.top); + mLayoutManager.onFling(e1, e2, velocityX, velocityY); return true; } @@ -775,9 +258,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { if (mLastLayoutMode != LAYOUT_MODE_LEFT_TO_RIGHT && mLastLayoutMode != LAYOUT_MODE_RIGHT_TO_LEFT) { return false; } - if (mLayoutOffset != 0) { - return false; - } return !mScroll; } @@ -788,57 +268,28 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } mScale = true; - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page != null && page.isLoaded()) { - page.scale(focusX, focusY, scale); - mLastScale = page.getScale(); - } + mLayoutManager.onScale(focusX, focusY, scale); return true; } @Override public void onScaleEnd() { - } @Override public void onDown(float x, float y) { - mSmoothScroller.forceStop(); - mSmoothScaler.forceStop(); - mFling.forceStop(); mTouched = true; mScale = false; mScroll = false; mFirstScroll = true; - mDeltaX = 0; - mDeltaY = 0; + mLayoutManager.onDown(); } @Override public void onUp() { mTouched = false; - mDeltaX = 0; - mDeltaY = 0; - mEdgeView.onRelease(); - - if ((mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT || mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT)) { - if (mLayoutOffset != 0) { - // Scroll - final float pageDelta = 7 * (float) Math.abs(mLayoutOffset) / (getWidth() + mInterval); - int duration = (int) ((pageDelta + 1) * 100); - duration = Math.min(duration, MAX_SETTLE_DURATION); - mSmoothScroller.startSmoothScroll(mLayoutOffset, 0, duration); - } else { - // Update AnimateState - GalleryPageView page = mPageMap.get(mFirstShownIndex); - if (page != null) { - page.setAnimateState(ScaledImageView.ANIMATE_STATE_RUN); - } - } - } else if (mLayoutMode == LAYOUT_MODE_TOP_TO_BOTTOM) { - // TODO - } + mLayoutManager.onUp(); } @Override @@ -865,15 +316,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { mEdgeView.layout(left, top, right, bottom); - //TODO set all seen UNKNOWN_SEEN in order to setScaleAndOffset - //if (mLastLayoutMode != mMode) { - // mLastLayoutMode = mMode; - // for () { - - // } - //} - - fill(); if (changeSize) { @@ -888,248 +330,20 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } } - public GalleryPageView getPage(int index) { - return mPageMap.get(index); - } - - private void unbindGalleryPage(GalleryPageView view) { - LayoutParams lp = (LayoutParams) view.getLayoutParams(); - if (lp.index != NO_POSITION) { - mAdapter.unbindPage(view, lp.index); - } - } - - private void attachePage(GalleryPageView page, int index) { - mPageMap.put(index, page); - } - - // Set lp.index NO_POSITION - private void detachPage(GalleryPageView page) { - LayoutParams lp = (LayoutParams) page.getLayoutParams(); - int index = lp.index; - lp.index = NO_POSITION; - if (index != NO_POSITION) { - mPageMap.remove(index); - } - } - - - private void fillX(int start, int end, int startIndex, boolean reverse) { - for (int i = 0, n = getComponentCount(); i < n; i++) { - GLView component = getComponent(i); - if (component instanceof GalleryPageView) { - mAttachedPage.put(((LayoutParams) component.getLayoutParams()).index, - (GalleryPageView) component); - } else { - mInvalidAttachedComponent.add(component); - } - } - - // Remove invalid view - int n = mInvalidAttachedComponent.size(); - while (n-- > 0) { - removeComponentAt(n); - } - mInvalidAttachedComponent.clear(); - - // Fill - int width = getWidth(); - int height = getHeight(); - int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); - int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - int offset = width + mInterval; - if (reverse) { - offset = -offset; - } - int pages = mAdapter.getPages(); - for (int spec = start, i = startIndex; reverse ? spec > end : spec < end && i < pages; i++) { - GalleryPageView page = mAttachedPage.get(i); - if (page != null) { - mAttachedPage.remove(i); - page.measure(widthSpec, heightSpec); - if (reverse) { - page.layout(spec - width, 0, spec, height); - } else { - page.layout(spec, 0, spec + width, height); - } - } else { - // Add index, left, top, right, bottom - mMissedPageInfo.add(i); - if (reverse) { - mMissedPageInfo.add(spec - width); - mMissedPageInfo.add(0); - mMissedPageInfo.add(spec); - mMissedPageInfo.add(height); - } else { - mMissedPageInfo.add(spec); - mMissedPageInfo.add(0); - mMissedPageInfo.add(spec + width); - mMissedPageInfo.add(height); - } - } - spec += offset; - } - - // Fill missed page - int count = mAttachedPage.size(); - n = mMissedPageInfo.size(); - for (int i = 0; i < n; i += 5) { - int index = mMissedPageInfo.get(i); - GalleryPageView page; - if (i < count) { - page = mAttachedPage.valueAt(i); - // Unbind - unbindGalleryPage(page); - detachPage(page); - mUsedPage.add(page); - } else { - page = mRecycler.obtain(); - addComponent(page); - } - // Bind - ((LayoutParams) page.getLayoutParams()).index = index; - ((LayoutParams) page.getLayoutParams()).seen = PAGE_STATE_UNKNOWN_SEEN; - attachePage(page, index); - // Update index in LayoutParams - mAdapter.bindPage(page, index); - page.measure(widthSpec, heightSpec); - page.layout(mMissedPageInfo.get(i + 1), mMissedPageInfo.get(i + 2), - mMissedPageInfo.get(i + 3), mMissedPageInfo.get(i + 4)); - - } - mMissedPageInfo.clear(); - - // Clear unuse page - for (int i = 0; i < count; i++) { - GalleryPageView page = mAttachedPage.valueAt(i); - if (mUsedPage.contains(page)) { - continue; - } - unbindGalleryPage(page); - detachPage(page); - removeComponent(page); - mRecycler.release(page); - } - mAttachedPage.clear(); - mUsedPage.clear(); - - for (int i = 0; i < getComponentCount(); i++) { - // Update seen - GalleryPageView page = (GalleryPageView) getComponent(i); - LayoutParams lp = ((LayoutParams) page.getLayoutParams()); - int oldSeen = lp.seen; - int seen = page.bounds().intersects(0, 0, width, height) ? PAGE_STATE_SEEN : PAGE_STATE_UNSEEN; - lp.seen = seen; - if (oldSeen == PAGE_STATE_UNKNOWN_SEEN || (oldSeen == PAGE_STATE_SEEN && seen == PAGE_STATE_UNSEEN)) { - page.setScaleOffset(mScaleMode, mStartPosition, mLastScale); - } - - // Update AnimateState - if (seen == PAGE_STATE_UNSEEN) { - page.setAnimateState(ScaledImageView.ANIMATE_STATE_STOP); - } else if (!mTouched && mLayoutOffset == 0 && mFirstShownIndex == lp.index) { - page.setAnimateState(ScaledImageView.ANIMATE_STATE_RUN); - } else { - //page.setAnimateState(ScaledImageView.ANIMATE_STATE_DEFALUE); - } - } - } - - private void fillY(int start, int end, int startIndex, boolean reverse) { - } - private void fill() { // Must be in render thread GalleryUtils.assertInRenderThread(); // Disable request layout mEnableRequestLayout = false; - - if (mAdapter == null) { - // Remove all component and unbind - int i = getComponentCount(); - while (i-- > 0) { - GLView component = getComponent(i); - removeComponentAt(i); - - // Remove from map, unbind gallery page, add to recycler - if (component instanceof GalleryPageView) { - GalleryPageView page = (GalleryPageView) component; - unbindGalleryPage(page); - detachPage(page); - mRecycler.release(page); - } - } - } else if (mAdapter.getPages() == 0) { - // Remove all component and unbind - int i = getComponentCount(); - while (i-- > 0) { - GLView component = getComponent(i); - removeComponentAt(i); - - // Remove from map, unbind gallery page, add to recycler - if (component instanceof GalleryPageView) { - GalleryPageView page = (GalleryPageView) component; - unbindGalleryPage(page); - detachPage(page); - mRecycler.release(page); - } - } - // TODO add - - } else if (mLayoutMode == LAYOUT_MODE_LEFT_TO_RIGHT || mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT) { - int width = getWidth(); - int start; - int end; - int startIndex; - boolean reverse = mLayoutMode == LAYOUT_MODE_RIGHT_TO_LEFT; - - if (reverse) { - if (mFirstShownIndex == 0) { - start = width; - startIndex = 0; - } else { - startIndex = mFirstShownIndex - 1; - start = width * 2 + mInterval; - } - if (mFirstShownIndex == mAdapter.getPages() - 1) { - end = 0; - } else { - end = -width - mInterval; - } - } else { - if (mFirstShownIndex == 0) { - start = 0; - startIndex = 0; - } else { - startIndex = mFirstShownIndex - 1; - start = -width - mInterval; - } - if (mFirstShownIndex == mAdapter.getPages() - 1) { - end = width; - } else { - end = width * 2 + mInterval; - } - } - - // Set offset - start += mLayoutOffset; - end += mLayoutOffset; - - fillX(start, end, startIndex, reverse); - } else { // mLayoutMode == LAYOUT_MODE_TOP_TO_BOTTOM - - } - + mLayoutManager.onFill(); mEnableRequestLayout = true; } @Override public void onRender(GLCanvas canvas) { long time = AnimationTime.get(); - boolean invalidate = mSmoothScroller.calculate(time); - invalidate |= mSmoothScaler.calculate(time); - invalidate |= mFling.calculate(time); + boolean invalidate = mLayoutManager.onUpdateAnimation(time); if (invalidate) { invalidate(); } @@ -1143,51 +357,91 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { mEdgeView.onRender(canvas); } - @Override - protected boolean checkLayoutParams(GLView.LayoutParams p) { - return p instanceof LayoutParams; + public GalleryPageView obtainPage() { + GalleryPageView page = mRecycler.obtain(); + if (page == null) { + page = new GalleryPageView(mContext); + } + return page; } - @Override - protected GLView.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + public void releasePage(GalleryPageView page) { + mRecycler.release(page); } - @Override - protected GLView.LayoutParams generateLayoutParams(GLView.LayoutParams p) { - return p == null ? generateDefaultLayoutParams() : new LayoutParams(p); + /** + * Indeterminate GLProgressView + */ + public GLProgressView obtainProgress() { + GLProgressView progress; + if (mProgressCache != null) { + progress = mProgressCache; + mProgressCache = null; + } else { + progress = new GLProgressView(); + progress.setColor(mProgressColor); + progress.setBgColor(BACKGROUND_COLOR); + progress.setIndeterminate(true); + } + return progress; } - public static class LayoutParams extends GLView.LayoutParams { + /** + * @param progress Indeterminate GLProgressView + */ + public void releaseProgress(GLProgressView progress) { + mProgressCache = progress; + } - private int index = NO_POSITION; - @PageState - private int seen = PAGE_STATE_UNKNOWN_SEEN; + public interface PageIterator { - public LayoutParams(GLView.LayoutParams source) { - super(source); + void mark(); + + void reset(); + + boolean hasNext(); + + boolean hasPrevious(); + + boolean isValid(); + + void next(); + + void previous(); + + void bind(GalleryPageView view); + + void unbind(GalleryPageView view); + } + + public static abstract class LayoutManager { + + protected GalleryView mGalleryView; + + public LayoutManager(@NonNull GalleryView galleryView) { + mGalleryView = galleryView; } - public LayoutParams(int width, int height) { - super(width, height); - } - } + public abstract void onAttach(PageIterator iterator); - public static abstract class Adapter { + public abstract PageIterator onDetach(); - public abstract int getPages(); + public abstract void onFill(); - public abstract GalleryPageView createPage(); + public abstract void onDown(); - public abstract void bindPage(GalleryPageView view, int index); + public abstract void onUp(); - public abstract void unbindPage(GalleryPageView view, int index); + public abstract void onScroll(float dx, float dy, float totalX, float totalY, float x, float y); - public void notifySizeChange() { - // TODO - } + public abstract void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); + + public abstract void onScale(float focusX, float focusY, float scale); + + public abstract boolean onUpdateAnimation(long time); } + /* class SmoothScaler extends Animation { private GalleryPageView mPage; @@ -1220,167 +474,32 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } } } + */ - class SmoothScroller extends Animation { + private static class Recycler { - private int mDx; - private int mDy; - private int mLastX; - private int mLastY; - - public void startSmoothScroll(int dx, int dy, int duration) { - mDx = dx; - mDy = dy; - mLastX = 0; - mLastY = 0; - setDuration(duration); - start(); - invalidate(); - } - - @Override - protected void onCalculate(float progress) { - int x = (int) (mDx * progress); - int y = (int) (mDy * progress); - int offsetX = x - mLastX; - while (offsetX != 0) { - int oldOffsetX = offsetX; - offsetX = scrollBetweenPagesHMode(offsetX); - // Avoid loop infinitely - if (offsetX == oldOffsetX) { - break; - } else { - mRequestFill = true; - } - } - mLastX = x; - mLastY = y; - } - } - - class Fling extends Animation { - - private float mPhysicalCoeff; - - private GalleryPageView mPage; - private int mDx; - private int mDy; - private int mLastX; - private int mLastY; - private int[] mTemp = new int[2]; - - public Fling(Context context) { - final float ppi = context.getResources().getDisplayMetrics().density * 160.0f; - mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2) - * 39.37f // inch/meter - * ppi - * 0.84f; // look and feel tuning - } - - private double getSplineDeceleration(int velocity) { - return Math.log(INFLEXION * Math.abs(velocity) / (FLING_FRICTION * mPhysicalCoeff)); - } - - /* Returns the duration, expressed in milliseconds */ - private int getSplineFlingDuration(int velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return (int) (1000.0 * Math.exp(l / decelMinusOne)); - } - - private double getSplineFlingDistance(int velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return FLING_FRICTION * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l); - } - - /** - * Modifies mDuration to the duration it takes to get from start to newFinal using the - * spline interpolation. The previous duration was needed to get to oldFinal. - **/ - private int adjustDuration(int start, int oldFinal, int newFinal, int duration) { - final int oldDistance = oldFinal - start; - final int newDistance = newFinal - start; - final float x = Math.abs((float) newDistance / oldDistance); - final int index = (int) (NB_SAMPLES * x); - if (index < NB_SAMPLES) { - final float x_inf = (float) index / NB_SAMPLES; - final float x_sup = (float) (index + 1) / NB_SAMPLES; - final float t_inf = SPLINE_TIME[index]; - final float t_sup = SPLINE_TIME[index + 1]; - final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf); - duration *= timeCoef; - } - return duration; - } - - public void startFling(GalleryPageView page, int velocityX, int minX, int maxX, - int velocityY, int minY, int maxY) { - mPage = page; - mDx = (int) (getSplineFlingDistance(velocityX) * Math.signum(velocityX)); - mDy = (int) (getSplineFlingDistance(velocityY) * Math.signum(velocityY)); - mLastX = 0; - mLastY = 0; - int durationX = getSplineFlingDuration(velocityX); - int durationY = getSplineFlingDuration(velocityY); - - if (mDx < minX) { - durationX = adjustDuration(0, mDx, minX, durationX); - mDx = minX; - } - if (mDx > maxX) { - durationX = adjustDuration(0, mDx, maxX, durationX); - mDx = maxX; - } - if (mDy < minY) { - durationY = adjustDuration(0, mDy, minY, durationY); - mDy = minY; - } - if (mDy > maxY) { - durationY = adjustDuration(0, mDy, maxY, durationY); - mDy = maxY; - } - - setDuration(Math.max(durationX, durationY)); - start(); - invalidate(); - } - - @Override - protected void onCalculate(float progress) { - int x = (int) (mDx * progress); - int y = (int) (mDy * progress); - int offsetX = x - mLastX; - int offsetY = y - mLastY; - if (offsetX != 0 || offsetY != 0) { - mPage.scroll(-offsetX, -offsetY, mTemp); - } - mLastX = x; - mLastY = y; - } - } - - class Recycler { + private static final int MAX_SIZE = 3; private int mSize = 0; private Stack mStack = new Stack<>(); + @Nullable private GalleryPageView obtain() { if (mSize != 0) { mSize--; return mStack.pop(); } else { - return mAdapter.createPage(); + return null; } } - public void release(GalleryPageView page) { + public void release(@Nullable GalleryPageView page) { if (page == null) { return; } - if (mSize < 3) { // 3 is max size + if (mSize < MAX_SIZE) { mSize++; mStack.push(page); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java index ade840b65..312560b72 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java @@ -16,30 +16,380 @@ package com.hippo.ehviewer.ui.gl; +import android.graphics.Rect; import android.graphics.RectF; +import android.support.annotation.IntDef; import com.hippo.gl.glrenderer.GLCanvas; import com.hippo.gl.glrenderer.Texture; import com.hippo.gl.view.GLView; +import com.hippo.yorozuya.MathUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; public class ImageView extends GLView { + @IntDef({SCALE_ORIGIN, SCALE_FIT_WIDTH, SCALE_FIT_HEIGHT, SCALE_FIT, SCALE_FIXED}) + @Retention(RetentionPolicy.SOURCE) + public @interface Scale {} + + @IntDef({START_POSITION_TOP_LEFT, START_POSITION_TOP_RIGHT, START_POSITION_BOTTOM_LEFT, + START_POSITION_BOTTOM_RIGHT, START_POSITION_CENTER}) + @Retention(RetentionPolicy.SOURCE) + public @interface StartPosition {} + + // TODO adjust scale max and min accroding to image size and screen size + private static final float SCALE_MIN = 1 / 10.0f; + private static final float SCALE_MAX = 10.0f; + + public static final int SCALE_ORIGIN = 0; + public static final int SCALE_FIT_WIDTH = 1; + public static final int SCALE_FIT_HEIGHT = 2; + public static final int SCALE_FIT = 3; + public static final int SCALE_FIXED = 4; + + public static final int START_POSITION_TOP_LEFT = 0; + public static final int START_POSITION_TOP_RIGHT = 1; + public static final int START_POSITION_BOTTOM_LEFT = 2; + public static final int START_POSITION_BOTTOM_RIGHT = 3; + public static final int START_POSITION_CENTER = 4; + private Texture mTexture; private RectF mDst = new RectF(); - private RectF mSrcActual = new RectF(); private RectF mDstActual = new RectF(); + private Rect mValidRect = new Rect(); + + private int mScaleMode = SCALE_FIT; + private int mStartPosition = START_POSITION_TOP_RIGHT; + private float mScaleValue = 1.0f; + + private float mScale = 1.0f; + + private boolean mScaleOffsetDirty = true; + private boolean mPositionInRootDirty = true; + + @Override + protected int getSuggestedMinimumWidth() { + return Math.max(super.getSuggestedMinimumWidth(), + mTexture == null ? 0 : mTexture.getWidth()); + } + + @Override + protected int getSuggestedMinimumHeight() { + return Math.max(super.getSuggestedMinimumHeight(), + mTexture == null ? 0 : mTexture.getHeight()); + } + + @Override + protected void onSizeChanged(int newW, int newH, int oldW, int oldH) { + mScaleOffsetDirty = true; + mPositionInRootDirty = true; + } + + @Override + protected void onPositionInRootChanged(int x, int y, int oldX, int oldY) { + mPositionInRootDirty = true; + } + + public void setTexture(Texture texture) { + mTexture = texture; + mScaleOffsetDirty = true; + mPositionInRootDirty = true; + } + + /** + * If target is shorter then screen, make it in screen center. If target is + * longer then parent, make sure target fill parent over + */ + private void adjustPosition() { + RectF dst = mDst; + int screenWidth = getWidth(); + int screenHeight = getHeight(); + float targetWidth = dst.width(); + float targetHeight = dst.height(); + + if (targetWidth > screenWidth) { + float fixXOffset = dst.left; + if (fixXOffset > 0) { + dst.left -= fixXOffset; + dst.right -= fixXOffset; + } else if ((fixXOffset = screenWidth - dst.right) > 0) { + dst.left += fixXOffset; + dst.right += fixXOffset; + } + } else { + float left = (screenWidth - targetWidth) / 2; + dst.offsetTo(left, dst.top); + } + if (targetHeight > screenHeight) { + float fixYOffset = dst.top; + if (fixYOffset > 0) { + dst.top -= fixYOffset; + dst.bottom -= fixYOffset; + } else if ((fixYOffset = screenHeight - dst.bottom) > 0) { + dst.top += fixYOffset; + dst.bottom += fixYOffset; + } + } else { + float top = (screenHeight - targetHeight) / 2; + dst.offsetTo(dst.left, top); + } + } + + public void setScaleOffset(@Scale int scaleMode, @StartPosition int startPosition, float scaleValue) { + mScaleMode = scaleMode; + mStartPosition = startPosition; + mScaleValue = scaleValue; + + int screenWidth = getWidth(); + int screenHeight = getHeight(); + + if (mTexture == null || screenWidth == 0 || screenHeight == 0) { + mScaleOffsetDirty = true; + return; + } + + int textureWidth = mTexture.getWidth(); + int textureHeight = mTexture.getHeight(); + // Avoid zero and negative + if (textureWidth <= 0) { + textureWidth = 1; + } + if (textureHeight <= 0) { + textureHeight = 1; + } + + // Set scale + float targetWidth; + float targetHeight; + switch (scaleMode) { + case SCALE_ORIGIN: + mScale = 1.0f; + targetWidth = textureWidth; + targetHeight = textureHeight; + break; + case SCALE_FIT_WIDTH: + mScale = (float) screenWidth / textureWidth; + targetWidth = screenWidth; + targetHeight = textureHeight * mScale; + break; + case SCALE_FIT_HEIGHT: + mScale = (float) screenHeight / textureHeight; + targetWidth = textureWidth * mScale; + targetHeight = screenHeight; + break; + case SCALE_FIT: + float scaleX = (float) screenWidth / textureWidth; + float scaleY = (float) screenHeight / textureHeight; + if (scaleX < scaleY) { + mScale = scaleX; + targetWidth = screenWidth; + targetHeight = textureHeight * scaleX; + } else { + mScale = scaleY; + targetWidth = textureWidth * scaleY; + targetHeight = screenHeight; + break; + } + break; + case SCALE_FIXED: + default: + mScale = scaleValue; + targetWidth = textureWidth * scaleValue; + targetHeight = textureHeight * scaleValue; + break; + } + + // adjust scale, not too big, not too small + if (mScale < SCALE_MIN) { + mScale = SCALE_MIN; + targetWidth = textureWidth * SCALE_MIN; + targetHeight = textureHeight * SCALE_MIN; + } else if (mScale > SCALE_MAX) { + mScale = SCALE_MAX; + targetWidth = textureWidth * SCALE_MAX; + targetHeight = textureHeight * SCALE_MAX; + } + + // Set mDst.left and mDst.right + RectF dst = mDst; + switch (startPosition) { + case START_POSITION_TOP_LEFT: + dst.left = 0; + dst.top = 0; + break; + case START_POSITION_TOP_RIGHT: + dst.left = screenWidth - targetWidth; + dst.top = 0; + break; + case START_POSITION_BOTTOM_LEFT: + dst.left = 0; + dst.top = screenHeight - targetHeight; + break; + case START_POSITION_BOTTOM_RIGHT: + dst.left = screenWidth - targetWidth; + dst.top = screenHeight - targetHeight; + break; + case START_POSITION_CENTER: + default: + dst.left = (screenWidth - targetWidth) / 2; + dst.top = (screenHeight - targetHeight) / 2; + break; + } + + // Set mDst.right and mDst.bottom + dst.right = dst.left + targetWidth; + dst.bottom = dst.top + targetHeight; + + // adjust position + adjustPosition(); + + mScaleOffsetDirty = false; + mPositionInRootDirty = true; + } + + + public void scroll(int dx, int dy, int[] remain) { + // Only work after layout + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + } + if (mScaleOffsetDirty) { + remain[0] = dx; + remain[1] = dy; + return; + } + + RectF dst = mDst; + int screenWidth = getWidth(); + int screenHeight = getHeight(); + float targetWidth = dst.width(); + float targetHeight = dst.height(); + + if (targetWidth > screenWidth) { + dst.left -= dx; + dst.right -= dx; + + float fixXOffset = dst.left; + if (fixXOffset > 0) { + dst.left -= fixXOffset; + dst.right -= fixXOffset; + remain[0] = -(int) fixXOffset; + } else if ((fixXOffset = screenWidth - dst.right) > 0) { + dst.left += fixXOffset; + dst.right += fixXOffset; + remain[0] = (int) fixXOffset; + } else { + remain[0] = 0; + } + } else { + remain[0] = dx; + } + if (targetHeight > screenHeight) { + dst.top -= dy; + dst.bottom -= dy; + + float fixYOffset = dst.top; + if (fixYOffset > 0) { + dst.top -= fixYOffset; + dst.bottom -= fixYOffset; + remain[1] = -(int) fixYOffset; + } else if ((fixYOffset = screenHeight - dst.bottom) > 0) { + dst.top += fixYOffset; + dst.bottom += fixYOffset; + remain[1] = (int) fixYOffset; + } else { + remain[1] = 0; + } + } else { + remain[1] = dy; + } + + if (dx != remain[0] || dy != remain[1]) { + mPositionInRootDirty = true; + invalidate(); + } + } + + public void scale(float focusX, float focusY, float scale) { + // Only work after layout + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + } + if (mScaleOffsetDirty) { + return; + } + + if ((mScale == SCALE_MAX && scale >= 1.0f) || (mScale == SCALE_MIN && scale < 1.0f)) { + return; + } + + float newScale = mScale * scale; + newScale = MathUtils.clamp(newScale, SCALE_MIN, SCALE_MAX); + mScale = newScale; + RectF dst = mDst; + float left = (focusX - ((focusX - dst.left) * scale)); + float top = (focusY - ((focusY - dst.top) * scale)); + dst.set(left, top, + (left + (mTexture.getWidth() * newScale)), + (top + (mTexture.getHeight() * newScale))); + + // adjust position + adjustPosition(); + + mPositionInRootDirty = true; + invalidate(); + } + + private void applyPositionInRoot() { + int width = mTexture.getWidth(); + int height = mTexture.getHeight(); + RectF dst = mDst; + RectF dstActual = mDstActual; + RectF srcActual = mSrcActual; + + dstActual.set(dst); + getValidRect(mValidRect); + if (dstActual.intersect(mValidRect.left, mValidRect.top, mValidRect.right, mValidRect.bottom)) { + srcActual.left = MathUtils.lerp(0, width, + MathUtils.delerp(dst.left, dst.right, dstActual.left)); + srcActual.right = MathUtils.lerp(0, width, + MathUtils.delerp(dst.left, dst.right, dstActual.right)); + srcActual.top = MathUtils.lerp(0, height, + MathUtils.delerp(dst.top, dst.bottom, dstActual.top)); + srcActual.bottom = MathUtils.lerp(0, height, + MathUtils.delerp(dst.top, dst.bottom, dstActual.bottom)); + } else { + // Can't be seen, set src and dst empty + srcActual.setEmpty(); + dstActual.setEmpty(); + } + } @Override public void onRender(GLCanvas canvas) { - if (mTexture != null) { + Texture texture = mTexture; + if (texture == null) { + return; + } + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + } + if (mPositionInRootDirty) { + mPositionInRootDirty = false; + applyPositionInRoot(); + } else { + mSrcActual.set(0, 0, texture.getWidth(), texture.getHeight()); + mDstActual.set(mDst); + } - - - mTexture.draw(canvas, mSrcActual, mDstActual); + if (!mSrcActual.isEmpty()) { + texture.draw(canvas, mSrcActual, mDstActual); } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java new file mode 100644 index 000000000..00d79b2e8 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java @@ -0,0 +1,516 @@ +/* + * Copyright 2016 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.ui.gl; + +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.MotionEvent; +import android.view.animation.Interpolator; + +import com.hippo.gl.anim.Animation; +import com.hippo.gl.view.GLView; +import com.hippo.gl.widget.GLEdgeView; +import com.hippo.gl.widget.GLProgressView; +import com.hippo.yorozuya.AssertUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class PagerLayoutManager extends GalleryView.LayoutManager { + + private static final String TAG = PagerLayoutManager.class.getSimpleName(); + + @IntDef({MODE_LEFT_TO_RIGHT, MODE_RIGHT_TO_LEFT}) + @Retention(RetentionPolicy.SOURCE) + private @interface Mode {} + + private static final int MODE_LEFT_TO_RIGHT = 0; + private static final int MODE_RIGHT_TO_LEFT = 1; + + private static final Interpolator SMOOTH_SCROLLER_INTERPOLATOR = new Interpolator() { + @Override + public float getInterpolation(float t) { + t -= 1.0f; + return t * t * t * t * t + 1.0f; + } + }; + + private GalleryView.PageIterator mIterator; + + private GLProgressView mProgress; + private GalleryPageView mPrevious; + private GalleryPageView mCurrent; + private GalleryPageView mNext; + + @Mode + private int mMode = MODE_RIGHT_TO_LEFT; + private int mOffset; + private int mInterval; + private int mProgressSpec; + + private int[] mScrollRemain = new int[2]; + + private boolean mCanScrollBetweenPages = false; + + private int mDeltaX; + private int mDeltaY; + + public PagerLayoutManager(@NonNull GalleryView galleryView, int interval, int progressSize) { + super(galleryView); + mInterval = interval; + mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(progressSize, GLView.MeasureSpec.EXACTLY); + } + + public void setMode(@Mode int mode) { + // TODO + if (mIterator != null) { + + } else { + + } + } + + @Override + public void onAttach(GalleryView.PageIterator iterator) { + AssertUtils.assertEquals("The PagerLayoutManager is attached", mIterator, null); + mIterator = iterator; + } + + private void removeProgress() { + if (mProgress != null) { + mGalleryView.removeComponent(mProgress); + mProgress = null; + } + } + + private void removePage(@NonNull GalleryPageView page) { + mGalleryView.removeComponent(page); + mIterator.unbind(page); + mGalleryView.releasePage(page); + } + + private void removeAllPages() { + // Remove gallery view + if (mPrevious != null) { + removePage(mPrevious); + mPrevious = null; + } + if (mCurrent != null) { + removePage(mCurrent); + mCurrent = null; + } + if (mNext != null) { + removePage(mNext); + mNext = null; + } + } + + @Override + public GalleryView.PageIterator onDetach() { + AssertUtils.assertNotEquals("The PagerLayoutManager is not attached", mIterator, null); + + removeAllPages(); + + // Clear iterator + GalleryView.PageIterator iterator = mIterator; + mIterator = null; + + return iterator; + } + + private GalleryPageView getLeftPage() { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + return mPrevious; + default: + case MODE_RIGHT_TO_LEFT: + return mNext; + } + } + + private void setLeftPage(GalleryPageView page) { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + mPrevious = page; + break; + default: + case MODE_RIGHT_TO_LEFT: + mNext = page; + break; + } + } + + private GalleryPageView getRightPage() { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + return mNext; + default: + case MODE_RIGHT_TO_LEFT: + return mPrevious; + } + } + + private void setRightPage(GalleryPageView page) { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + mNext = page; + break; + default: + case MODE_RIGHT_TO_LEFT: + mPrevious = page; + break; + } + } + + @Override + public void onFill() { + GalleryView.PageIterator iterator = mIterator; + GalleryView galleryView = mGalleryView; + AssertUtils.assertNotEquals("The PagerLayoutManager is not attached", iterator, null); + + int width = galleryView.getWidth(); + int height = galleryView.getHeight(); + + if (!iterator.isValid()) { + // Remove all pages + removeAllPages(); + + // Ensure progress + if (mProgress == null) { + mProgress = galleryView.obtainProgress(); + galleryView.addComponent(mProgress); + } + + // Measure and layout progress + mProgress.measure(mProgressSpec, mProgressSpec); + int progressWidth = mProgress.getMeasuredWidth(); + int progressHeight = mProgress.getMeasuredHeight(); + int progressLeft = width / 2 - progressWidth / 2; + int progressTop = height / 2 - progressHeight / 2; + mProgress.layout(progressLeft, progressTop, progressLeft + progressWidth, progressTop + progressHeight); + } else { + // Remove progress + removeProgress(); + + // Ensure pages + if (mCurrent == null) { + mCurrent = galleryView.obtainPage(); + iterator.bind(mCurrent); + galleryView.addComponent(mCurrent); + } + if (mPrevious == null && iterator.hasPrevious()) { + iterator.mark(); + iterator.previous(); + mPrevious = galleryView.obtainPage(); + iterator.bind(mPrevious); + galleryView.addComponent(mPrevious); + iterator.reset(); + } + if (mNext == null && iterator.hasNext()) { + iterator.mark(); + iterator.next(); + mNext = galleryView.obtainPage(); + iterator.bind(mNext); + galleryView.addComponent(mNext); + iterator.reset(); + } + + // Measure and layout pages + int offset = mOffset; + int widthSpec = GLView.MeasureSpec.makeMeasureSpec(width, GLView.MeasureSpec.EXACTLY); + int heightSpec = GLView.MeasureSpec.makeMeasureSpec(height, GLView.MeasureSpec.EXACTLY); + if (mCurrent != null) { + mCurrent.measure(widthSpec, heightSpec); + mCurrent.layout(offset, 0, width + offset, height); + } + GalleryPageView leftPage = getLeftPage(); + if (leftPage != null) { + leftPage.measure(widthSpec, heightSpec); + leftPage.layout(-mInterval - width + offset, 0, -mInterval + offset, height); + } + GalleryPageView rightPage = getRightPage(); + if (rightPage != null) { + rightPage.measure(widthSpec, heightSpec); + rightPage.layout(width + mInterval + offset, 0, width + mInterval + width + offset, height); + } + } + } + + @Override + public void onDown() { + mDeltaX = 0; + mDeltaY = 0; + } + + @Override + public void onUp() { + mGalleryView.getEdgeView().onRelease(); + } + + private void pagePrevious() { + GalleryView.PageIterator iterator = mIterator; + if (!iterator.hasPrevious()) { + return; + } + iterator.previous(); + + if (mNext != null) { + removePage(mNext); + } + mNext = mCurrent; + mCurrent = mPrevious; + mPrevious = null; + + if (iterator.hasPrevious()) { + iterator.mark(); + iterator.previous(); + mPrevious = mGalleryView.obtainPage(); + iterator.bind(mPrevious); + mGalleryView.addComponent(mPrevious); + iterator.reset(); + } + } + + private void pageNext() { + GalleryView.PageIterator iterator = mIterator; + if (!iterator.hasNext()) { + return; + } + iterator.next(); + + if (mPrevious != null) { + removePage(mPrevious); + } + mPrevious = mCurrent; + mCurrent = mNext; + mNext = null; + + if (iterator.hasNext()) { + iterator.mark(); + iterator.next(); + mPrevious = mGalleryView.obtainPage(); + iterator.bind(mPrevious); + mGalleryView.addComponent(mPrevious); + iterator.reset(); + } + } + + private void pageLeft() { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + pagePrevious(); + break; + default: + case MODE_RIGHT_TO_LEFT: + pageNext(); + break; + } + } + + private void pageRight() { + switch (mMode) { + case MODE_LEFT_TO_RIGHT: + pageNext(); + break; + default: + case MODE_RIGHT_TO_LEFT: + pagePrevious(); + break; + } + } + + private int scrollBetweenPages(int dx) { + GalleryPageView leftPage = getLeftPage(); + GalleryPageView rightPage = getRightPage(); + int width = mGalleryView.getWidth(); + + int remain; + if (dx < 0) { // Try to show left + if (leftPage == null) { + return dx; + } + + if (dx > mOffset - width - mInterval) { + remain = 0; + mOffset -= dx; + } else { + // Go to left page + pageLeft(); + remain = dx + width + mInterval - mOffset; + mOffset = 0; + } + } else { // Try to show right + if (rightPage == null) { + return dx; + } + + if (dx < width + mInterval + mOffset) { + remain = 0; + mOffset -= dx; + } else { + // Go to right page + pageRight(); + remain = dx - width - mInterval - mOffset; + mOffset = 0; + } + } + + return remain; + } + + public void overScrollEdge(float x, float y, int dx, int dy) { + GLEdgeView edgeView = mGalleryView.getEdgeView(); + + mDeltaX += dx; + mDeltaY += dy; + + if (mDeltaX < 0) { + edgeView.onPull(-mDeltaX, y, GLEdgeView.LEFT); + if (!edgeView.isFinished(GLEdgeView.RIGHT)) { + edgeView.onRelease(GLEdgeView.RIGHT); + } + } else if (mDeltaX > 0) { + edgeView.onPull(mDeltaX, y, GLEdgeView.RIGHT); + if (!edgeView.isFinished(GLEdgeView.LEFT)) { + edgeView.onRelease(GLEdgeView.LEFT); + } + } + + // TODO What about top and bottom + } + + @Override + public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) { + if (mCurrent == null) { + return; + } + + boolean needFill = false; + boolean canImageScroll = true; + int remainX = (int) dx; + int remainY = (int) dy; + + if (mGalleryView.isFirstScroll()) { + mCanScrollBetweenPages = Math.abs(dx) > Math.abs(dy) * 1.5; + } + + while (remainX != 0 || remainY != 0) { + if (mOffset == 0 && canImageScroll) { + ImageView image = mCurrent.getImageView(); + image.scroll(remainX, remainY, mScrollRemain); + remainX = mScrollRemain[0]; + remainY = mScrollRemain[1]; + canImageScroll = false; + mDeltaX = 0; + mDeltaY = 0; + } else if (remainX == 0 || + (getLeftPage() == null && mOffset == 0 && remainX < 0) || + (getRightPage() == null && mOffset == 0 && remainX > 0)) { + // On edge + overScrollEdge(x, y, remainX, remainY); + remainX = 0; + remainY = 0; + } else if (mCanScrollBetweenPages) { + remainX = scrollBetweenPages(remainX); + canImageScroll = true; + needFill = true; + mDeltaX = 0; + mDeltaY = 0; + } else { + Log.w(TAG, "Something wrong?"); + remainX = 0; + remainY = 0; + mDeltaX = 0; + mDeltaY = 0; + } + } + + if (needFill) { + mGalleryView.requestFill(); + } + } + + @Override + public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (mCurrent == null) { + return; + } + + + + } + + @Override + public void onScale(float focusX, float focusY, float scale) { + if (mCurrent == null) { + return; + } + + + + } + + @Override + public boolean onUpdateAnimation(long time) { + return false; + } + + + + + + + + + class SmoothScroller extends Animation { + + private int mDx; + private int mDy; + private int mLastX; + private int mLastY; + + public void startSmoothScroll(int dx, int dy, int duration) { + mDx = dx; + mDy = dy; + mLastX = 0; + mLastY = 0; + setDuration(duration); + start(); + // TODO invalidate(); + } + + @Override + protected void onCalculate(float progress) { + int x = (int) (mDx * progress); + int y = (int) (mDy * progress); + int offsetX = x - mLastX; + while (offsetX != 0) { + int oldOffsetX = offsetX; + // TODO offsetX = scrollBetweenPagesHMode(offsetX); + // Avoid loop infinitely + if (offsetX == oldOffsetX) { + break; + } else { + // TODO mRequestFill = true; + } + } + mLastX = x; + mLastY = y; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java new file mode 100644 index 000000000..47ec331c8 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016 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.ui.gl; + +import android.support.annotation.NonNull; +import android.view.MotionEvent; + +public class ScrollLayoutManager extends GalleryView.LayoutManager { + + public ScrollLayoutManager(@NonNull GalleryView galleryView) { + super(galleryView); + } + + @Override + public void onAttach(GalleryView.PageIterator iterator) { + + } + + @Override + public GalleryView.PageIterator onDetach() { + return null; + } + + @Override + public void onFill() { + + } + + @Override + public void onDown() { + + } + + @Override + public void onUp() { + + } + + @Override + public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) { + + } + + @Override + public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + + } + + @Override + public void onScale(float focusX, float focusY, float scale) { + + } + + @Override + public boolean onUpdateAnimation(long time) { + return false; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index 37aac3415..7e421cb76 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -20,6 +20,7 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; @@ -45,8 +46,8 @@ import android.widget.TextView; import android.widget.Toast; import com.hippo.ehviewer.EhApplication; -import com.hippo.ehviewer.UrlOpener; import com.hippo.ehviewer.R; +import com.hippo.ehviewer.UrlOpener; import com.hippo.ehviewer.client.EhCacheKeyFactory; import com.hippo.ehviewer.client.EhClient; import com.hippo.ehviewer.client.EhRequest; @@ -59,6 +60,7 @@ import com.hippo.ehviewer.client.data.GalleryTagGroup; import com.hippo.ehviewer.client.data.LargePreviewSet; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.parser.RateGalleryParser; +import com.hippo.ehviewer.ui.GalleryActivity; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; import com.hippo.scene.SceneFragment; @@ -863,6 +865,11 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene ListUrlBuilder lub = new ListUrlBuilder(); lub.setCategory(category); GalleryListScene.startScene(this, lub); + } else if (mDownload == v) { + // TODO + } else if (mRead == v) { + Intent intent = new Intent(getActivity(), GalleryActivity.class); + startActivity(intent); } else if (mInfo == v) { Bundle args = new Bundle(); args.putParcelable(GalleryInfoScene.KEY_GALLERY_DETAIL, mGalleryDetail); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java index de1ebf0fc..32a11ab65 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java @@ -16,7 +16,6 @@ package com.hippo.ehviewer.ui.scene; -import android.content.Context; import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; @@ -29,7 +28,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -42,6 +40,7 @@ import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhRequest; import com.hippo.rippleold.RippleSalon; import com.hippo.scene.Announcer; +import com.hippo.util.ActivityHelper; import com.hippo.util.ExceptionUtils; public final class LoginScene extends BaseScene implements EditText.OnEditorActionListener, @@ -171,12 +170,7 @@ public final class LoginScene extends BaseScene implements EditText.OnEditorActi mPasswordLayout.setError(null); } - // Hide ime keyboard - View view = getActivity().getCurrentFocus(); - if (view != null) { - InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } + ActivityHelper.hideSoftInput(getActivity()); mProgress.setAlpha(0.0f); mProgress.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/hippo/gl/view/GLView.java b/app/src/main/java/com/hippo/gl/view/GLView.java index 3e1e8cba9..58f427499 100644 --- a/app/src/main/java/com/hippo/gl/view/GLView.java +++ b/app/src/main/java/com/hippo/gl/view/GLView.java @@ -631,9 +631,18 @@ public class GLView implements TouchOwner { } private boolean setBounds(int left, int top, int right, int bottom) { - boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left) - || (bottom - top) != (mBounds.bottom - mBounds.top); - mBounds.set(left, top, right, bottom); + Rect bounds = mBounds; + int oldW = bounds.right - bounds.left; + int oldH = bounds.bottom - bounds.top; + int newW = right - left; + int newH = bottom - top; + + boolean sizeChanged = oldW != newW || oldH != newH; + bounds.set(left, top, right, bottom); + + if (sizeChanged) { + onSizeChanged(newW, newH, oldW, oldH); + } // Update position in root int[] position = mPosition; @@ -657,6 +666,9 @@ public class GLView implements TouchOwner { return sizeChanged; } + protected void onSizeChanged(int newW, int newH, int oldW, int oldH) { + } + protected void onPositionInRootChanged(int x, int y, int oldX, int oldY) { } diff --git a/app/src/main/java/com/hippo/gl/widget/EdgeEffect.java b/app/src/main/java/com/hippo/gl/widget/GLEdgeEffect.java similarity index 99% rename from app/src/main/java/com/hippo/gl/widget/EdgeEffect.java rename to app/src/main/java/com/hippo/gl/widget/GLEdgeEffect.java index f9559d616..a8a9fb098 100644 --- a/app/src/main/java/com/hippo/gl/widget/EdgeEffect.java +++ b/app/src/main/java/com/hippo/gl/widget/GLEdgeEffect.java @@ -26,7 +26,7 @@ import com.hippo.gl.glrenderer.GLCanvas; /** * Material design edge effect */ -public class EdgeEffect { +public class GLEdgeEffect { // Time it will take the effect to fully recede in ms private static final int RECEDE_TIME = 600; @@ -91,7 +91,7 @@ public class EdgeEffect { /** * Construct a new EdgeEffect with a theme appropriate for the provided context. */ - public EdgeEffect(int color) { + public GLEdgeEffect(int color) { mColor = color; mInterpolator = new DecelerateInterpolator(); } diff --git a/app/src/main/java/com/hippo/gl/widget/EdgeView.java b/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java similarity index 90% rename from app/src/main/java/com/hippo/gl/widget/EdgeView.java rename to app/src/main/java/com/hippo/gl/widget/GLEdgeView.java index b4f93c984..d56dcf27e 100644 --- a/app/src/main/java/com/hippo/gl/widget/EdgeView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java @@ -17,18 +17,18 @@ package com.hippo.gl.widget; import android.content.Context; -import android.content.res.TypedArray; import android.opengl.Matrix; import android.support.annotation.IntDef; import com.hippo.ehviewer.R; import com.hippo.gl.glrenderer.GLCanvas; import com.hippo.gl.view.GLView; +import com.hippo.yorozuya.ResourcesUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -public final class EdgeView extends GLView { +public final class GLEdgeView extends GLView { @IntDef({TOP, LEFT, BOTTOM, RIGHT}) @Retention(RetentionPolicy.SOURCE) @@ -50,18 +50,14 @@ public final class EdgeView extends GLView { private static final int BOTTOM_M = BOTTOM * 16; private static final int RIGHT_M = RIGHT * 16; - private EdgeEffect[] mEffect = new EdgeEffect[4]; + private GLEdgeEffect[] mEffect = new GLEdgeEffect[4]; private float[] mMatrix = new float[4 * 16]; - public EdgeView(Context context) { - final TypedArray a = context.obtainStyledAttributes( - R.styleable.Theme); - final int themeColor = a.getColor( - R.styleable.Theme_colorPrimary, 0xff666666); - a.recycle(); + public GLEdgeView(Context context) { + final int themeColor = ResourcesUtils.getAttrColor(context, R.attr.colorPrimary); int color = (themeColor & 0xffffff) | 0x33000000; for (int i = 0; i < 4; i++) { - mEffect[i] = new EdgeEffect(color); + mEffect[i] = new GLEdgeEffect(color); } } @@ -101,7 +97,7 @@ public final class EdgeView extends GLView { @Override public void onRender(GLCanvas canvas) { - super.render(canvas); + super.onRender(canvas); boolean more = false; for (int i = 0; i < 4; i++) { canvas.save(GLCanvas.SAVE_FLAG_MATRIX); @@ -146,7 +142,7 @@ public final class EdgeView extends GLView { } public void onRelease(@Direction int direction) { - EdgeEffect edgeEffect = mEffect[direction]; + GLEdgeEffect edgeEffect = mEffect[direction]; edgeEffect.onRelease(); if (!edgeEffect.isFinished()) { invalidate(); @@ -162,4 +158,8 @@ public final class EdgeView extends GLView { invalidate(); } } + + public boolean isFinished(int direction) { + return mEffect[direction].isFinished(); + } } diff --git a/app/src/main/java/com/hippo/gl/widget/FrameLayout.java b/app/src/main/java/com/hippo/gl/widget/GLFrameLayout.java similarity index 98% rename from app/src/main/java/com/hippo/gl/widget/FrameLayout.java rename to app/src/main/java/com/hippo/gl/widget/GLFrameLayout.java index e5ddde695..202cdfa04 100644 --- a/app/src/main/java/com/hippo/gl/widget/FrameLayout.java +++ b/app/src/main/java/com/hippo/gl/widget/GLFrameLayout.java @@ -19,7 +19,7 @@ package com.hippo.gl.widget; import com.hippo.gl.view.GLView; import com.hippo.gl.view.Gravity; -public class FrameLayout extends GLView { +public class GLFrameLayout extends GLView { @Override protected void onMeasure(int widthSpec, int heightSpec) { diff --git a/app/src/main/java/com/hippo/gl/widget/GLImageView.java b/app/src/main/java/com/hippo/gl/widget/GLImageView.java deleted file mode 100644 index 5f64b6031..000000000 --- a/app/src/main/java/com/hippo/gl/widget/GLImageView.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2016 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.gl.widget; - -import android.graphics.Rect; -import android.graphics.RectF; -import android.support.annotation.IntDef; - -import com.hippo.gl.glrenderer.GLCanvas; -import com.hippo.gl.glrenderer.Texture; -import com.hippo.gl.view.GLView; -import com.hippo.yorozuya.MathUtils; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -public class GLImageView extends GLView { - - @IntDef({SCALE_ORIGIN, SCALE_FIT_WIDTH, SCALE_FIT_HEIGHT, SCALE_FIT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Scale {} - - @IntDef({START_POSITION_TOP_LEFT, START_POSITION_TOP_RIGHT, START_POSITION_BOTTOM_LEFT, - START_POSITION_BOTTOM_RIGHT, START_POSITION_CENTER}) - @Retention(RetentionPolicy.SOURCE) - public @interface StartPosition {} - - public static final int SCALE_ORIGIN = 0; - public static final int SCALE_FIT_WIDTH = 1; - public static final int SCALE_FIT_HEIGHT = 2; - public static final int SCALE_FIT = 3; - - public static final int START_POSITION_TOP_LEFT = 0; - public static final int START_POSITION_TOP_RIGHT = 1; - public static final int START_POSITION_BOTTOM_LEFT = 2; - public static final int START_POSITION_BOTTOM_RIGHT = 3; - public static final int START_POSITION_CENTER = 4; - - private Texture mTexture; - - private RectF mDst = new RectF(); - private RectF mSrcActual = new RectF(); - private RectF mDstActual = new RectF(); - private Rect mValidRect = new Rect(); - - private boolean mPositionInRootDirty = true; - - private void applyPositionInRoot() { - int width = mTexture.getWidth(); - int height = mTexture.getHeight(); - RectF dst = mDst; - RectF dstActual = mDstActual; - RectF srcActual = mSrcActual; - - dstActual.set(dst); - getValidRect(mValidRect); - if (dstActual.intersect(mValidRect.left, mValidRect.top, mValidRect.right, mValidRect.bottom)) { - srcActual.left = MathUtils.lerp(0, width, - MathUtils.delerp(dst.left, dst.right, dstActual.left)); - srcActual.right = MathUtils.lerp(0, width, - MathUtils.delerp(dst.left, dst.right, dstActual.right)); - srcActual.top = MathUtils.lerp(0, height, - MathUtils.delerp(dst.top, dst.bottom, dstActual.top)); - srcActual.bottom = MathUtils.lerp(0, height, - MathUtils.delerp(dst.top, dst.bottom, dstActual.bottom)); - } else { - // Can't be seen, set src and dst empty - srcActual.setEmpty(); - dstActual.setEmpty(); - } - } - - @Override - protected void onPositionInRootChanged(int x, int y, int oldX, int oldY) { - mPositionInRootDirty = true; - } - - @Override - public void onRender(GLCanvas canvas) { - Texture texture = mTexture; - if (texture == null) { - return; - } - - - - - - - if (mPositionInRootDirty) { - mPositionInRootDirty = false; - applyPositionInRoot(); - } else { - mSrcActual.set(0, 0, texture.getWidth(), texture.getHeight()); - mDstActual.set(mDst); - } - - texture.draw(canvas, mSrcActual, mDstActual); - } -} diff --git a/app/src/main/java/com/hippo/gl/widget/LinearLayout.java b/app/src/main/java/com/hippo/gl/widget/GLLinearLayout.java similarity index 99% rename from app/src/main/java/com/hippo/gl/widget/LinearLayout.java rename to app/src/main/java/com/hippo/gl/widget/GLLinearLayout.java index 9201be4a6..d3920c7ca 100644 --- a/app/src/main/java/com/hippo/gl/widget/LinearLayout.java +++ b/app/src/main/java/com/hippo/gl/widget/GLLinearLayout.java @@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; -public class LinearLayout extends GLView { +public class GLLinearLayout extends GLView { @IntDef({HORIZONTAL, VERTICAL}) @Retention(RetentionPolicy.SOURCE) diff --git a/app/src/main/java/com/hippo/gl/widget/GLProgressView.java b/app/src/main/java/com/hippo/gl/widget/GLProgressView.java new file mode 100644 index 000000000..ca74997b4 --- /dev/null +++ b/app/src/main/java/com/hippo/gl/widget/GLProgressView.java @@ -0,0 +1,242 @@ +/* + * Copyright 2016 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.gl.widget; + +import android.graphics.Color; +import android.graphics.Path; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import com.hippo.anani.PathInterpolator; +import com.hippo.gl.anim.Animation; +import com.hippo.gl.anim.FloatAnimation; +import com.hippo.gl.glrenderer.GLCanvas; +import com.hippo.gl.glrenderer.GLPaint; +import com.hippo.gl.view.AnimationTime; +import com.hippo.gl.view.GLView; + +import java.util.ArrayList; +import java.util.List; + +public class GLProgressView extends GLView { + + private static final Interpolator TRIM_START_INTERPOLATOR; + private static final Interpolator TRIM_END_INTERPOLATOR; + private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); + + static { + Path trimStartPath = new Path(); + trimStartPath.moveTo(0.0f, 0.0f); + trimStartPath.lineTo(0.5f, 0.0f); + trimStartPath.cubicTo(0.7f, 0.0f, 0.6f, 1f, 1f, 1f); + TRIM_START_INTERPOLATOR = PathInterpolator.build(trimStartPath); + + Path trimEndPath = new Path(); + trimEndPath.moveTo(0.0f, 0.0f); + trimEndPath.cubicTo(0.2f, 0.0f, 0.1f, 1f, 0.5f, 1f); + trimEndPath.lineTo(1f, 1f); + TRIM_END_INTERPOLATOR = PathInterpolator.build(trimEndPath); + } + + private GLPaint mGLPaint; + + private float mCx; + private float mCy; + private float mRadiusX; + private float mRadiusY; + + private float mTrimStart = 0.0f; + private float mTrimEnd = 0.0f; + private float mTrimOffset = 0.0f; + private float mTrimRotation = 0.0f; + + private boolean mIndeterminate = false; + + private List mAnimations; + + public GLProgressView() { + mGLPaint = new GLPaint(); + mGLPaint.setColor(Color.WHITE); + mGLPaint.setBackgroundColor(Color.BLACK); + mAnimations = new ArrayList<>(); + + setupAnimations(); + } + + public void setupAnimations() { + FloatAnimation trimStart = new FloatAnimation() { + @Override + protected void onCalculate(float progress) { + super.onCalculate(progress); + mTrimStart = get(); + } + }; + trimStart.setRange(0.0f, 0.75f); + trimStart.setDuration(1333L); + trimStart.setInterpolator(TRIM_START_INTERPOLATOR); + trimStart.setRepeatCount(Animation.INFINITE); + + FloatAnimation trimEnd = new FloatAnimation() { + @Override + protected void onCalculate(float progress) { + super.onCalculate(progress); + mTrimEnd = get(); + } + }; + trimEnd.setRange(0.0f, 0.75f); + trimEnd.setDuration(1333L); + trimEnd.setInterpolator(TRIM_END_INTERPOLATOR); + trimEnd.setRepeatCount(Animation.INFINITE); + + FloatAnimation trimOffset = new FloatAnimation() { + @Override + protected void onCalculate(float progress) { + super.onCalculate(progress); + mTrimOffset = get(); + } + }; + trimOffset.setRange(0.0f, 0.25f); + trimOffset.setDuration(1333L); + trimOffset.setInterpolator(LINEAR_INTERPOLATOR); + trimOffset.setRepeatCount(Animation.INFINITE); + + FloatAnimation trimRotation = new FloatAnimation() { + @Override + protected void onCalculate(float progress) { + super.onCalculate(progress); + mTrimRotation = get(); + } + }; + trimRotation.setRange(0.0f, 720.0f); + trimRotation.setDuration(6665L); + trimRotation.setInterpolator(LINEAR_INTERPOLATOR); + trimRotation.setRepeatCount(Animation.INFINITE); + + mAnimations.add(trimStart); + mAnimations.add(trimEnd); + mAnimations.add(trimOffset); + mAnimations.add(trimRotation); + } + + private void startAnimations() { + List animations = mAnimations; + for (int i = 0, n = animations.size(); i < n; i++) { + animations.get(i).forceReset(); + } + } + + private void stopAnimations() { + List animations = mAnimations; + for (int i = 0, n = animations.size(); i < n; i++) { + animations.get(i).forceStop(); + } + } + + @Override + protected void onLayout(boolean changeSize, int left, int top, int right, + int bottom) { + super.onLayout(changeSize, left, top, right, bottom); + + int width = right - left; + int height = bottom - top; + mGLPaint.setLineWidth(Math.min(width, height) / 12.0f); + mCx = width / 2; + mCy = height / 2; + mRadiusX = width / 48.0f * 19.0f; + mRadiusY = height / 48.0f * 19.0f; + } + + public void setColor(int color) { + mGLPaint.setColor(color); + invalidate(); + } + + public void setBgColor(int color) { + mGLPaint.setBackgroundColor(color); + invalidate(); + } + + public void setIndeterminate(boolean indeterminate) { + if (mIndeterminate != indeterminate) { + mIndeterminate = indeterminate; + if (indeterminate) { + startAnimations(); + } else { + stopAnimations(); + } + invalidate(); + } + } + + public boolean isIndeterminate() { + return mIndeterminate; + } + + public void setProgress(float progress) { + if (!mIndeterminate) { + mTrimStart = 0.0f; + mTrimEnd = progress; + mTrimOffset = 0.0f; + mTrimRotation = 0.0f; + invalidate(); + } + } + + @Override + public void onRender(GLCanvas canvas) { + update(); + + int width = getWidth(); + int height = getHeight(); + int cx = width / 2; + int cy = height / 2; + + float startAngle = (mTrimStart + mTrimOffset) * 360.0f - 90; + float sweepAngle = Math.max(12.0f, (mTrimEnd - mTrimStart) * 360.0f); + float rotation = mTrimRotation + startAngle; + + canvas.save(); + + canvas.translate(cx, cy); + canvas.rotate(rotation, 0, 0, 1); + if (rotation % 180 != 0) { + canvas.translate(-cy, -cx); + } else { + canvas.translate(-cx, -cy); + } + + canvas.drawArc(mCx, mCy, mRadiusX, mRadiusY, sweepAngle, mGLPaint); + + canvas.restore(); + } + + private void update() { + boolean invalidate = false; + + if (mIndeterminate) { + long currentTime = AnimationTime.get(); + List animations = mAnimations; + for (int i = 0, n = animations.size(); i < n; i++) { + invalidate |= animations.get(i).calculate(currentTime); + } + } + + if (invalidate) { + invalidate(); + } + } +} diff --git a/app/src/main/java/com/hippo/gl/widget/TextView.java b/app/src/main/java/com/hippo/gl/widget/GLTextView.java similarity index 79% rename from app/src/main/java/com/hippo/gl/widget/TextView.java rename to app/src/main/java/com/hippo/gl/widget/GLTextView.java index e9535d2c1..e1ca0c6c7 100644 --- a/app/src/main/java/com/hippo/gl/widget/TextView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLTextView.java @@ -17,6 +17,7 @@ package com.hippo.gl.widget; import android.graphics.Rect; +import android.text.TextUtils; import com.hippo.gl.glrenderer.GLCanvas; import com.hippo.gl.glrenderer.TextTexture; @@ -24,7 +25,7 @@ import com.hippo.gl.view.GLView; import com.hippo.gl.view.Gravity; import com.hippo.yorozuya.ArrayUtils; -public class TextView extends GLView { +public class GLTextView extends GLView { TextTexture mTextTexture; @@ -33,26 +34,35 @@ public class TextView extends GLView { private int mGravity = Gravity.NO_GRAVITY; - public TextView(TextTexture textTexture) { + private void generateIndexes() { + if (mTextTexture == null || TextUtils.isEmpty(mText)) { + mIndexes = ArrayUtils.EMPTY_INT_ARRAY; + } else { + mIndexes = mTextTexture.getTextIndexes(mText); + } + } + + public void setTextTexture(TextTexture textTexture) { + if (mTextTexture == textTexture) { + return; + } mTextTexture = textTexture; + + generateIndexes(); + requestLayout(); } public void setText(String text) { if (text == null) { text = ""; } - if (!text.equals(mText)) { - int oldMinimumWidth = getSuggestedMinimumWidth(); - - mText = text; - mIndexes = mTextTexture.getTextIndexes(text); - - if (getSuggestedMinimumWidth() != oldMinimumWidth) { - requestLayout(); - } else { - invalidate(); - } + if (text.equals(mText)) { + return; } + mText = text; + + generateIndexes(); + requestLayout(); } public void setGravity(int gravity) { @@ -82,12 +92,6 @@ public class TextView extends GLView { } } - @Override - protected void onMeasure(int widthSpec, int heightSpec) { - setMeasuredSize(getDefaultSize(getSuggestedMinimumWidth(), widthSpec), - getDefaultSize(getSuggestedMinimumHeight(), heightSpec)); - } - @Override public void onRender(GLCanvas canvas) { Rect paddings = getPaddings(); diff --git a/app/src/main/java/com/hippo/gl/widget/GLTextureView.java b/app/src/main/java/com/hippo/gl/widget/GLTextureView.java new file mode 100644 index 000000000..954007b1c --- /dev/null +++ b/app/src/main/java/com/hippo/gl/widget/GLTextureView.java @@ -0,0 +1,73 @@ +/* + * Copyright 2016 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.gl.widget; + +import android.graphics.RectF; + +import com.hippo.gl.glrenderer.GLCanvas; +import com.hippo.gl.glrenderer.Texture; +import com.hippo.gl.view.GLView; + +public class GLTextureView extends GLView { + + private RectF mSrc = new RectF(); + private RectF mDst = new RectF(); + + private Texture mTexture; + + public void setTexture(Texture texture) { + mTexture = texture; + if (texture != null) { + mSrc.set(0.0f, 0.0f, texture.getWidth(), texture.getHeight()); + } else { + mSrc.setEmpty(); + } + } + + @Override + protected int getSuggestedMinimumWidth() { + return mTexture == null ? super.getSuggestedMinimumWidth() : mTexture.getWidth() + mPaddings.width(); + } + + @Override + protected int getSuggestedMinimumHeight() { + return mTexture == null ? super.getSuggestedMinimumWidth() : mTexture.getHeight() + mPaddings.height(); + } + + @Override + public void onRender(GLCanvas canvas) { + if (mTexture == null || mSrc.isEmpty()) { + return; + } + + mDst.set(mPaddings.left, mPaddings.top, getWidth() - mPaddings.right, getHeight() - mPaddings.bottom); + if (mDst.isEmpty()) { + return; + } + + mTexture.draw(canvas, mSrc, mDst); + } + + + + + + + + + +} diff --git a/app/src/main/java/com/hippo/gl/widget/ImageView.java b/app/src/main/java/com/hippo/gl/widget/ImageView.java deleted file mode 100644 index a96b1e2a3..000000000 --- a/app/src/main/java/com/hippo/gl/widget/ImageView.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016 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.gl.widget; - -import android.graphics.Color; - -import com.hippo.gl.glrenderer.GLCanvas; -import com.hippo.gl.glrenderer.ImageTexture; -import com.hippo.gl.image.Image; -import com.hippo.gl.view.GLView; - -public class ImageView extends GLView implements ImageTexture.Callback { - - private ImageTexture mImageTexture; - - public ImageView(ImageTexture imageTexture) { - mImageTexture = imageTexture; - imageTexture.setCallback(this); - setBackgroundColor(Color.WHITE); - } - - @Override - public void onRender(GLCanvas canvas) { - mImageTexture.draw(canvas, 0, 0); - } - - @Override - public void invalidateImage(Image image) { - invalidate(); - } -} diff --git a/app/src/main/java/com/hippo/gl/widget/ScaledImageView.java b/app/src/main/java/com/hippo/gl/widget/ScaledImageView.java deleted file mode 100644 index 78bb77115..000000000 --- a/app/src/main/java/com/hippo/gl/widget/ScaledImageView.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2016 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.gl.widget; - -import android.graphics.Rect; -import android.graphics.RectF; - -import com.hippo.ehviewer.ui.gl.GalleryView; -import com.hippo.gl.glrenderer.GLCanvas; -import com.hippo.gl.glrenderer.ImageTexture; -import com.hippo.gl.image.Image; -import com.hippo.gl.view.GLView; -import com.hippo.yorozuya.MathUtils; - -public class ScaledImageView extends GLView implements ImageTexture.Callback { - - // TODO adjust scale max and min accroding to image size and screen size - private static final float SCALE_MIN = 1 / 10.0f; - private static final float SCALE_MAX = 10.0f; - - public static final int ANIMATE_STATE_RUN = 0; - public static final int ANIMATE_STATE_STOP = 1; - - private int mAnimateState = ANIMATE_STATE_RUN; - - private ImageTexture mImageTexture; - - // Corresponding source is [0, 0, mTiledTexture.getWidth(), mTiledTexture.getHeight()] - private RectF mTarget = new RectF(); - private RectF mSeen = new RectF(); - private RectF mSourceAfterSeen = new RectF(); - private RectF mTargetAfterSeen = new RectF(); - private boolean mScaleOffsetDirty = true; - private boolean mSeenDirty = true; - - @GalleryView.Scale - private int mScaleMode; - @GalleryView.StartPosition - private int mStartPosition; - private float mLastScale; - private float mScale = 1f; - - public void setAnimateState(int animateState) { - - - - if (mAnimateState != animateState) { - mAnimateState = animateState; - - if (mImageTexture != null) { - mImageTexture.setPause(mAnimateState == ANIMATE_STATE_STOP); - } - } - } - - public void setImageTexture(ImageTexture imageTexture) { - ImageTexture oldImageTexture = mImageTexture; - if (oldImageTexture != null) { - oldImageTexture.setCallback(null); - } - - mImageTexture = imageTexture; - setScaleOffset(mScaleMode, mStartPosition, mLastScale); - - if (mImageTexture != null) { - mImageTexture.setCallback(this); - mImageTexture.setPause(mAnimateState == ANIMATE_STATE_STOP); - } - - invalidate(); - } - - public ImageTexture getImageTexture() { - return mImageTexture; - } - - @Override - protected int getSuggestedMinimumWidth() { - return Math.max(super.getSuggestedMinimumWidth(), - mImageTexture == null ? 0 : mImageTexture.getWidth()); - } - - @Override - protected int getSuggestedMinimumHeight() { - return Math.max(super.getSuggestedMinimumHeight(), - mImageTexture == null ? 0 : mImageTexture.getHeight()); - } - - /** - * If target is shorter then screen, make it in screen center. If target is - * longer then parent, make sure target fill parent over - */ - private void adjustPosition() { - RectF target = mTarget; - int screenWidth = getWidth(); - int screenHeight = getHeight(); - float targetWidth = target.width(); - float targetHeight = target.height(); - - if (targetWidth > screenWidth) { - float fixXOffset = target.left; - if (fixXOffset > 0) { - target.left -= fixXOffset; - target.right -= fixXOffset; - } else if ((fixXOffset = screenWidth - target.right) > 0) { - target.left += fixXOffset; - target.right += fixXOffset; - } - } else { - float left = (screenWidth - targetWidth) / 2; - target.offsetTo(left, target.top); - } - if (targetHeight > screenHeight) { - float fixYOffset = target.top; - if (fixYOffset > 0) { - target.top -= fixYOffset; - target.bottom -= fixYOffset; - } else if ((fixYOffset = screenHeight - target.bottom) > 0) { - target.top += fixYOffset; - target.bottom += fixYOffset; - } - } else { - float top = (screenHeight - targetHeight) / 2; - target.offsetTo(target.left, top); - } - } - - public void setScaleOffset(@GalleryView.Scale int scale, - @GalleryView.StartPosition int startPosition, float lastScale) { - mScaleMode = scale; - mStartPosition = startPosition; - mLastScale = lastScale; - - int screenWidth = getWidth(); - int screenHeight = getHeight(); - - if (mImageTexture == null || screenWidth == 0 || screenHeight == 0) { - mScaleOffsetDirty = true; - return; - } - - int imageWidth = mImageTexture.getWidth(); - int imageHeight = mImageTexture.getHeight(); - - // Set scale - float targetWidth; - float targetHeight; - switch (scale) { - case GalleryView.SCALE_ORIGIN: - mScale = 1.0f; - targetWidth = imageWidth; - targetHeight = imageHeight; - break; - case GalleryView.SCALE_FIT_WIDTH: - mScale = (float) screenWidth / imageWidth; - targetWidth = screenWidth; - targetHeight = imageHeight * mScale; - break; - case GalleryView.SCALE_FIT_HEIGHT: - mScale = (float) screenHeight / imageHeight; - targetWidth = imageWidth * mScale; - targetHeight = screenHeight; - break; - case GalleryView.SCALE_FIT: - float scaleX = (float) screenWidth / imageWidth; - float scaleY = (float) screenHeight / imageHeight; - if (scaleX < scaleY) { - mScale = scaleX; - targetWidth = screenWidth; - targetHeight = imageHeight * scaleX; - } else { - mScale = scaleY; - targetWidth = imageWidth * scaleY; - targetHeight = screenHeight; - break; - } - break; - case GalleryView.SCALE_FIXED: - default: - mScale = lastScale; - targetWidth = imageWidth * lastScale; - targetHeight = imageHeight * lastScale; - break; - } - - // adjust scale, not too big, not too small - if (mScale < SCALE_MIN) { - mScale = SCALE_MIN; - targetWidth = imageWidth * SCALE_MIN; - targetHeight = imageHeight * SCALE_MIN; - } else if (mScale > SCALE_MAX) { - mScale = SCALE_MAX; - targetWidth = imageWidth * SCALE_MAX; - targetHeight = imageHeight * SCALE_MAX; - } - - RectF target = mTarget; - // set start position - switch (startPosition) { - case GalleryView.START_POSITION_TOP_LEFT: - target.left = 0; - target.top = 0; - break; - case GalleryView.START_POSITION_TOP_RIGHT: - target.left = screenWidth - targetWidth; - target.top = 0; - break; - case GalleryView.START_POSITION_BOTTOM_LEFT: - target.left = 0; - target.top = screenHeight - targetHeight; - break; - case GalleryView.START_POSITION_BOTTOM_RIGHT: - target.left = screenWidth - targetWidth; - target.top = screenHeight - targetHeight; - break; - case GalleryView.START_POSITION_CENTER: - default: - target.left = (screenWidth - targetWidth) / 2; - target.top = (screenHeight - targetHeight) / 2; - break; - } - - target.right = target.left + targetWidth; - target.bottom = target.top + targetHeight; - - // adjust position - adjustPosition(); - - mScaleOffsetDirty = false; - mSeenDirty = true; - } - - public void scroll(int dx, int dy, int[] remain) { - if (mImageTexture == null) { - remain[0] = dx; - remain[1] = dy; - return; - } - - RectF target = mTarget; - int screenWidth = getWidth(); - int screenHeight = getHeight(); - float targetWidth = target.width(); - float targetHeight = target.height(); - - if (targetWidth > screenWidth) { - target.left -= dx; - target.right -= dx; - - float fixXOffset = target.left; - if (fixXOffset > 0) { - target.left -= fixXOffset; - target.right -= fixXOffset; - remain[0] = -(int) fixXOffset; - } else if ((fixXOffset = screenWidth - target.right) > 0) { - target.left += fixXOffset; - target.right += fixXOffset; - remain[0] = (int) fixXOffset; - } else { - remain[0] = 0; - } - } else { - remain[0] = dx; - } - if (targetHeight > screenHeight) { - target.top -= dy; - target.bottom -= dy; - - float fixYOffset = target.top; - if (fixYOffset > 0) { - target.top -= fixYOffset; - target.bottom -= fixYOffset; - remain[1] = -(int) fixYOffset; - } else if ((fixYOffset = screenHeight - target.bottom) > 0) { - target.top += fixYOffset; - target.bottom += fixYOffset; - remain[1] = (int) fixYOffset; - } else { - remain[1] = 0; - } - } else { - remain[1] = dy; - } - - if (dx != remain[0] || dy != remain[1]) { - mSeenDirty = true; - invalidate(); - } - } - - public void scale(float focusX, float focusY, float scale) { - if (mImageTexture == null) { - return; - } - - if ((mScale == SCALE_MAX && scale > 1f) || (mScale == SCALE_MIN && scale < 1f)) { - return; - } - - float newScale = mScale * scale; - newScale = MathUtils.clamp(newScale, SCALE_MIN, SCALE_MAX); - mScale = newScale; - RectF target = mTarget; - float left = (focusX - ((focusX - target.left) * scale)); - float top = (focusY - ((focusY - target.top) * scale)); - target.set(left, top, - (left + (mImageTexture.getWidth() * newScale)), - (top + (mImageTexture.getHeight() * newScale))); - - // adjust position - adjustPosition(); - - mSeenDirty = true; - invalidate(); - } - - public RectF getTargetRect() { - return mTarget; - } - - public float getScale() { - return mScale; - } - - public float getFitScale() { - int screenWidth = getWidth(); - int screenHeight = getHeight(); - - if (mImageTexture == null || screenWidth == 0 || screenHeight == 0) { - return 1f; - } - - int imageWidth = mImageTexture.getWidth(); - int imageHeight = mImageTexture.getHeight(); - - float scaleX = (float) screenWidth / imageWidth; - float scaleY = (float) screenHeight / imageHeight; - if (scaleX < scaleY) { - return scaleX; - } else { - return scaleY; - } - } - - public boolean isLoaded() { - return mImageTexture != null; - } - - public void setSeen(Rect seen) { - mSeen.set(seen); - mSeenDirty = true; - } - - public void applySeen() { - int width = mImageTexture.getWidth(); - int height = mImageTexture.getHeight(); - RectF target = mTarget; - RectF targetAfterSeen = mTargetAfterSeen; - RectF sourceAfterSeen = mSourceAfterSeen; - - targetAfterSeen.set(target); - if (targetAfterSeen.intersect(mSeen)) { - sourceAfterSeen.left = MathUtils.lerp(0, width, - MathUtils.delerp(target.left, target.right, targetAfterSeen.left)); - sourceAfterSeen.right = MathUtils.lerp(0, width, - MathUtils.delerp(target.left, target.right, targetAfterSeen.right)); - sourceAfterSeen.top = MathUtils.lerp(0, height, - MathUtils.delerp(target.top, target.bottom, targetAfterSeen.top)); - sourceAfterSeen.bottom = MathUtils.lerp(0, height, - MathUtils.delerp(target.top, target.bottom, targetAfterSeen.bottom)); - } else { - // Can't be seen - sourceAfterSeen.set(0f, 0f, 0f, 0f); - } - } - - @Override - public void onRender(GLCanvas canvas) { - if (mImageTexture != null) { - - if (mScaleOffsetDirty) { - setScaleOffset(mScaleMode, mStartPosition, mLastScale); - } - if (mSeenDirty) { - applySeen(); - mSeenDirty = false; - } - // Draw - if (!mSourceAfterSeen.isEmpty()) { - mImageTexture.draw(canvas, mSourceAfterSeen, mTargetAfterSeen); - } - } - } - - @Override - public void invalidateImage(Image image) { - invalidate(); - } -} diff --git a/app/src/main/java/com/hippo/gl/widget/XmlResourceView.java b/app/src/main/java/com/hippo/gl/widget/XmlResourceView.java deleted file mode 100644 index bd3e1abf9..000000000 --- a/app/src/main/java/com/hippo/gl/widget/XmlResourceView.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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.gl.widget; - -import com.hippo.gl.glrenderer.GLCanvas; -import com.hippo.gl.glrenderer.XmlResourceTexture; -import com.hippo.gl.view.GLView; - -public class XmlResourceView extends GLView { - - private XmlResourceTexture mTexture; - - public void setTexture(XmlResourceTexture texture) { - mTexture = texture; - - if (texture != null) { - int width = getWidth(); - int height = getHeight(); - if (width > 0 && height > 0) { - texture.setBound(width, height); - invalidate(); - } - } - } - - @Override - protected void onLayout(boolean changeSize, int left, int top, int right, int bottom) { - super.onLayout(changeSize, left, top, right, bottom); - - if (changeSize && mTexture != null) { - mTexture.setBound(right - left, bottom - top); - } - } - - @Override - public void onRender(GLCanvas canvas) { - super.onRender(canvas); - - if (mTexture != null) { - canvas.drawTexture(mTexture, 0, 0, getWidth(), getHeight()); - } - } -} diff --git a/app/src/main/java/com/hippo/okhttp/ChromeRequestBuilder.java b/app/src/main/java/com/hippo/okhttp/ChromeRequestBuilder.java index e63302720..83d06ff11 100644 --- a/app/src/main/java/com/hippo/okhttp/ChromeRequestBuilder.java +++ b/app/src/main/java/com/hippo/okhttp/ChromeRequestBuilder.java @@ -24,7 +24,7 @@ import okhttp3.Request; public class ChromeRequestBuilder extends Request.Builder { private static final String CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"; + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36"; public ChromeRequestBuilder(String url) { URL u; diff --git a/app/src/main/java/com/hippo/util/Dpad.java b/app/src/main/java/com/hippo/util/Dpad.java new file mode 100644 index 000000000..ce01ba894 --- /dev/null +++ b/app/src/main/java/com/hippo/util/Dpad.java @@ -0,0 +1,94 @@ +/* + * Copyright 2016 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.util; + +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; + +// From http://developer.android.com/training/game-controllers/controller-input.html#dpad +public class Dpad { + + public static final int INVALID = -1; + public static final int UP = 0; + public static final int LEFT = 1; + public static final int RIGHT = 2; + public static final int DOWN = 3; + public static final int CENTER = 4; + + public static int getDirectionPressed(InputEvent event) { + if (!isDpadDevice(event)) { + return INVALID; + } + + int directionPressed = INVALID; + + // If the input event is a MotionEvent, check its hat axis values. + if (event instanceof MotionEvent) { + + // Use the hat axis value to find the D-pad direction + MotionEvent motionEvent = (MotionEvent) event; + float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X); + float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y); + + // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad + // LEFT and RIGHT direction accordingly. + if (Float.compare(xaxis, -1.0f) == 0) { + directionPressed = Dpad.LEFT; + } else if (Float.compare(xaxis, 1.0f) == 0) { + directionPressed = Dpad.RIGHT; + } + // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad + // UP and DOWN direction accordingly. + else if (Float.compare(yaxis, -1.0f) == 0) { + directionPressed = Dpad.UP; + } else if (Float.compare(yaxis, 1.0f) == 0) { + directionPressed = Dpad.DOWN; + } + } + + // If the input event is a KeyEvent, check its key code. + else if (event instanceof KeyEvent) { + + // Use the key code to find the D-pad direction. + KeyEvent keyEvent = (KeyEvent) event; + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) { + directionPressed = Dpad.LEFT; + } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) { + directionPressed = Dpad.RIGHT; + } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) { + directionPressed = Dpad.UP; + } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { + directionPressed = Dpad.DOWN; + } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) { + directionPressed = Dpad.CENTER; + } + } + return directionPressed; + } + + public static boolean isDpadDevice(InputEvent event) { + // Check that input comes from a device with directional pads. + if ((event.getSource() & InputDevice.SOURCE_DPAD) + != InputDevice.SOURCE_DPAD) { + return true; + } else { + return false; + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 050ce6850..4ea733423 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 18 22:31:05 CST 2016 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +#Mon Jan 18 22:31:05 CST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/yorozuya b/yorozuya index 41fa892da..c1b5f4cb2 160000 --- a/yorozuya +++ b/yorozuya @@ -1 +1 @@ -Subproject commit 41fa892da2a1b3cc7af21c7c29b958ac726b66dc +Subproject commit c1b5f4cb2e86282165843f0c180a5e07ac16cedf