/*******************************************************************************
* Copyright 2009 Robot Media SL
*
* 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 net.robotmedia.acv.ui.widget;
import java.io.File;
import java.util.HashMap;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation.AnimationListener;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageSwitcher;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.VideoView;
import android.widget.ViewSwitcher;
import net.androidcomics.acv.R;
import net.robotmedia.acv.Constants;
import net.robotmedia.acv.comic.ACVComic;
import net.robotmedia.acv.comic.Comic;
import net.robotmedia.acv.logic.PreferencesController;
import net.robotmedia.acv.logic.ServiceManager;
import net.robotmedia.acv.ui.widget.SuperImageView.LayoutMeasures;
import net.robotmedia.acv.utils.IntentUtils;
import net.robotmedia.acv.utils.MathUtils;
public class ComicView extends RelativeLayout implements OnCompletionListener, OnErrorListener {
protected void initializeWithResources(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.comic_view, this);
animationFadeIn = R.anim.fade_in;
animationFadeOut = R.anim.fade_out;
animationKeep = R.anim.keep;
animationPushDownIn = R.anim.push_down_in;
animationPushDownOut = R.anim.push_down_out;
animationPushLeftIn = R.anim.push_left_in;
animationPushLeftOut = R.anim.push_left_out;
animationPushRightIn = R.anim.push_right_in;
animationPushRightOut = R.anim.push_right_out;
animationPushUpIn = R.anim.push_up_in;
animationPushUpOut = R.anim.push_up_out;
messageButton = (Button) findViewById(R.id.message_button);
mSwitcher = (ImageSwitcher) findViewById(R.id.switcher);
mVideoView = (VideoView) findViewById(R.id.video_view);
stringScreenProgressMessage = R.string.dialog_page_progress_text;
stringScreenProgressTitle = R.string.dialog_page_progress_title;
stringUnavailableText = R.string.dialog_unavailable_text;
stringUnavailableTitle = R.string.dialog_unavailable_title;
}
protected boolean isLeftToRight() {
return new PreferencesController(getContext()).isLeftToRight();
}
private class SwitchImageTask extends AsyncTask<Object, Object, Drawable> {
private boolean forward;
private boolean sequential;
private ProgressDialog progressDialog;
final Runnable mNotifyLoadScreenRunning = new Runnable() {
public void run() {
if (progressDialogRequired && !mDestroyed) {
progressDialog = new ProgressDialog(getContext());
progressDialog.setTitle(stringScreenProgressTitle);
progressDialog.setIcon(android.R.drawable.ic_menu_info_details);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
if (SwitchImageTask.this.isImageSwitch()) {
progressDialog.setMessage(getContext().getString(stringScreenProgressMessage).replace("@number", String.valueOf(imageIndex + 1)));
} else {
// TODO: Localize text
progressDialog.setMessage("Loading...");
}
progressDialog.show();
}
}
};
private final static int IMAGE_SWITCH = -1;
private int imageIndex;
private int frameIndex = IMAGE_SWITCH; // Screen change by default
boolean progressDialogRequired;
private int getFrameIndex() {
if (this.isImageSwitch()) { // Screen change
return sequential && !forward ? comic.getFramesSize(position) - 1 : 0;
} else { // Frame change with transition
return frameIndex;
}
}
private boolean isImageSwitch() {
return frameIndex == IMAGE_SWITCH;
}
private void postScreenChangedActions() {
showScreenNumber();
prepareMessageButton();
prepareCaption();
boolean stopAnimation = true;
if (comic instanceof ACVComic) {
final ACVComic acv = (ACVComic) comic;
if (mustConsiderFrames()) {
stopAnimation = false; // The frame will stop the animation if necessary
final int frameIndex = this.getFrameIndex();
setFrame(frameIndex, forward, false);
} else {
stopAnimation = true;
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
current.getLetterbox().hide(false); // In case we come from a screen with frames
}
if (forward) {
final boolean autoplay = ((ACVComic) comic).isAutoplay(position);
if (autoplay || mAutoplay) {
stopAnimation = false;
long duration = ((ACVComic) comic).getDuration(position);
startAnimating();
mAutoplayRunnable = new Runnable() {
@Override
public void run() {
if (position + 1 < comic.getLength()) {
moveForwardAfterVideo = true;
setPosition(position + 1, true, true);
}
}
};
ComicView.this.postDelayed(mAutoplayRunnable, duration);
}
}
if (acv.hasVibration(position)) {
vibrate(stopAnimation);
stopAnimation = false; // The vibration or frame will stop the animation
}
File sound = acv.getSound(position);
if (sound != null) {
playSound(sound);
}
}
if (stopAnimation && !mVideoPlaying) {
stopAnimating();
}
if (mListener != null) {
mListener.onScreenChanged(position);
}
}
@Override
protected Drawable doInBackground(Object... params) {
System.gc();
try {
if (comic instanceof ACVComic) {
final ACVComic acv = (ACVComic) comic;
if (acv.getVideoFile(imageIndex) != null) {
if (forward && imageIndex + 1 < comic.getLength()) {
return comic.getScreen(imageIndex + 1);
}
if (!forward && imageIndex > 0) {
return comic.getScreen(imageIndex - 1);
}
} else {
return acv.getScreenWithContents(mHTMLRenderer, imageIndex);
}
}
return comic.getScreen(imageIndex);
} catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(Drawable result) {
if (result != null && result instanceof BitmapDrawable) {
Log.d("Image width", String.valueOf(((BitmapDrawable) result).getBitmap().getWidth()));
Log.d("Image height", String.valueOf(((BitmapDrawable) result).getBitmap().getHeight()));
}
loadingPage = false;
progressDialogRequired = false;
if (progressDialog != null) progressDialog.dismiss();
// if (!viewer.destroyed) {
if (result != null) {
previousPosition = position;
position = imageIndex;
framePosition = -1;
if (comic instanceof ACVComic) {
File videoFile = ((ACVComic) comic).getVideoFile(position);
if (videoFile != null) {
mVideoView.setVisibility(View.VISIBLE);
mSwitcher.setVisibility(View.INVISIBLE);
mVideoView.setVideoPath(videoFile.getAbsolutePath());
mVideoView.start();
mVideoView.requestFocus();
mSwitcher.setInAnimation(null);
mSwitcher.setOutAnimation(null);
mVideoPlaying = true;
startAnimating();
}
}
final ComicFrame current = (ComicFrame) mSwitcher.getNextView();
current.getImage().setImageDrawable(result);
mSwitcher.showNext();
nextImageUp = !nextImageUp; // HACK
Integer backgroundColor = comic.getBackgroundColor(position);
if (backgroundColor == null) {
backgroundColor = Color.BLACK;
}
setBackgroundColor(backgroundColor);
mSwitcher.setBackgroundColor(backgroundColor);
// TODO: Check if it's necessary to set both background colors
scale(current.getImage());
Animation animation = mSwitcher.getInAnimation();
if (animation == null) {
animation = mSwitcher.getOutAnimation();
};
if (animation != null) {
if (mustConsiderFrames()) { // Animate to the first frame
final ACVComic acv = (ACVComic) comic;
final int frameIndex = this.getFrameIndex();
final int width = current.getImage().getOriginalWidth();
final int height = current.getImage().getOriginalHeight();
final Rect frame = acv.rectForSize(position, frameIndex, width, height);
final Integer color = acv.getBackgroundColor(position, frameIndex);
boolean letterbox = color != null;
if (!this.isImageSwitch()) {
ComicView.this.framePosition = frameIndex; // Need to set this because we're bypassing setFrame
}
final Rect newFrame = current.getImage().scrollTo(frame, !letterbox);
final LayoutMeasures imageMeasures = current.getImage().calculateLayout(frame, !letterbox);
current.showContent(acv, position, frameIndex, forward, imageMeasures);
if (letterbox) {
current.getLetterbox().show(newFrame.width(), newFrame.height(), color, 0);
} else {
current.getLetterbox().hide(false);
}
}
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
if (SwitchImageTask.this.isImageSwitch()) {
postScreenChangedActions();
} else {
ComicView.this.postFrameDisplayedActions(forward);
}
}
@Override public void onAnimationRepeat(Animation arg0) {}
@Override public void onAnimationStart(Animation arg0) { startAnimating(); }
});
} else {
postScreenChangedActions();
}
if (isImageSwitch()) {
int cachePosition;
if (previousPosition == position + 1) {
// The next screen is already prepared, so we attempt to prepare the previous screen.
cachePosition = position - 1;
} else {
cachePosition = position + 1;
}
if (preload) {
mPrepareScreenTask = new PrepareScreenTask(comic);
mPrepareScreenTask.execute(cachePosition);
}
}
} else {
if (mListener != null) {
mListener.onScreenLoadFailed();
}
}
}
/**
* Scales the current image based on (in order of priority): the comic scale mode, the zoom of the previous image or the scale mode preference.
* @param current
*/
private void scale(SuperImageView current) {
String scaleMode = comic.getScaleMode();
// Legacy code
if (comic instanceof ACVComic && Constants.SCALE_MODE_NONE_VALUE.equals(scaleMode)) {
int width = current.getOriginalWidth();
int height = current.getOriginalHeight();
if (MathUtils.isEqual(width, 480, 2) && MathUtils.isEqual(height, 320, 2)) {
scaleMode = Constants.SCALE_MODE_BEST_VALUE;
}
}
if (scaleMode == null) {
final ComicFrame previous = (ComicFrame) mSwitcher.getCurrentView();
if (previous != null && !previous.getImage().isScaled() && previous.getImage().getZoomFactor() > 0) { // If a previous screen view exists with a valid zoom factor not obtained by scaling, use it
current.scaleByFactor(previous.getImage().getZoomFactor());
return;
} else { // Scale mode preference
scaleMode = preferences.getString(Constants.SCALE_MODE_KEY, Constants.SCALE_MODE_NONE_VALUE);
}
}
current.scale(scaleMode, false);
}
@Override
protected void onPreExecute() {
System.gc();
loadingPage = true;
progressDialogRequired = true;
ComicView.this.postDelayed(mNotifyLoadScreenRunning, 1000);
final ComicFrame next = (ComicFrame) mSwitcher.getNextView();
if (next != null) next.getImage().recycleBitmap();
if (lowMemoryTransitions) {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
if (position != imageIndex) { // No need to recycle the bitmap if the image is the same.
current.getImage().recycleBitmap();
}
}
}
}
}
private MediaPlayer soundPlayer = null;
private HashMap<File, Integer> soundHistory = new HashMap<File, Integer>();
private synchronized void playSound(final File sound) {
if (soundPlayer == null || !soundPlayer.isPlaying()) {
if (!soundHistory.containsKey(sound)) {
soundHistory.put(sound, 1);
soundPlayer = new MediaPlayer();
if (soundPlayer != null) {
try {
soundPlayer.setDataSource(sound.getAbsolutePath());
soundPlayer.prepare();
soundPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public void setPreload(boolean preload) {
this.preload = preload;
}
public void setLowMemoryTransitions(boolean lowMemoryTransitions) {
this.lowMemoryTransitions = lowMemoryTransitions;
}
private boolean preload = true;
private boolean lowMemoryTransitions = false;
// TODO: Does not belong to this class
private class GetNativeUrlTask extends AsyncTask<String, Object, String> {
@Override
protected String doInBackground(String... params) {
String comicId = params[0];
String result = ServiceManager.getNativeURL(comicId);
return result;
}
protected void onPostExecute (String result) {
messageButton.setEnabled(true);
if (result != null) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(result));
getContext().startActivity(intent);
} else {
showOfferSubscribeDialog();
}
}
@Override
protected void onPreExecute () {
messageButton.setEnabled(false);
}
}
private class PrepareScreenTask extends AsyncTask<Integer, Object, Object> {
private Comic comic;
public PrepareScreenTask(Comic comic) {
this.comic = comic;
}
@Override
protected Object doInBackground(Integer... params) {
try {
comic.prepareScreen(params[0]);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
return null;
}
}
private PrepareScreenTask mPrepareScreenTask;
private SwitchImageTask mImageSwitchTask;
private GetNativeUrlTask mGetNativeUrlTask;
private boolean mAutoplay;
private Comic comic;
private int framePosition;
private boolean isBottomMost;
private boolean isLeftMost;
private boolean isRightMost;
private boolean isTopMost;
private boolean loadingPage = false;
private boolean mAnimating = false;
private boolean mDestroyed = false;
private ComicViewListener mListener;
private boolean moveForwardAfterVideo;
private boolean mVideoPlaying = false;
private boolean nextImageUp; // HACK
private int position;
private SharedPreferences preferences;
private int previousPosition;
private Toast toast = null;
protected int animationFadeIn;
protected int animationFadeOut;
protected int animationKeep;
protected int animationPushDownIn;
protected int animationPushDownOut;
protected int animationPushLeftIn;
protected int animationPushLeftOut;
protected int animationPushRightIn;
protected int animationPushRightOut;
protected int animationPushUpIn;
protected int animationPushUpOut;
protected Button messageButton;
protected ViewSwitcher mSwitcher;
protected VideoView mVideoView;
protected CaptionView mCaption;
protected int stringScreenProgressMessage;
protected int stringScreenProgressTitle;
protected int stringUnavailableText;
protected int stringUnavailableTitle;
protected WebView mHTMLRenderer;
public void setAutoplay(boolean autoplay) {
if (!mAutoplay && autoplay) {
mAutoplay = true;
this.goToCurrent();
} else if (mAutoplay && !autoplay) {
mAutoplay = false;
this.removeCallbacks(mAutoplayRunnable);
this.stopAnimating();
}
}
public ComicView(Context context, AttributeSet attrs) {
super(context, attrs);
nextImageUp = true;
mHTMLRenderer = new WebView(context);
mHTMLRenderer.setVisibility(View.INVISIBLE);
mHTMLRenderer.setBackgroundColor(Color.TRANSPARENT);
mHTMLRenderer.setVerticalScrollBarEnabled(false);
mHTMLRenderer.setHorizontalScrollBarEnabled(false);
this.addView(mHTMLRenderer);
initializeWithResources(context);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
mSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
public View makeView() {
final ComicFrame view = new ComicFrame(getContext());
view.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
// SuperImageView i = new SuperImageView(getContext(), null, attributeScrollViewStyle);
// i.setScaleType(ImageView.ScaleType.CENTER);
// FIXME: Why does the following show the image vertically
// centered? See onLoadPageSuccess for workaround.
// i.setLayoutParams(new
// FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
// ViewGroup.LayoutParams.WRAP_CONTENT));
return view;
}
});
messageButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (comic instanceof ACVComic) {
ACVComic.Message message = ((ACVComic) comic).getMessage(position);
if (message != null) {
if (message.uri.startsWith(ACVComic.COMIC_URI_PREFIX)) {
String comicId = message.uri.substring(ACVComic.COMIC_URI_PREFIX.length());
mGetNativeUrlTask = new GetNativeUrlTask();
mGetNativeUrlTask.execute(comicId);
} else {
IntentUtils.openURI(getContext(), message.uri, message.nonMarketUri);
}
}
}
}
});
mVideoView.setOnCompletionListener(this);
mVideoView.setOnErrorListener(this);
}
public boolean actualSize() {
return fit(Constants.SCALE_MODE_NONE_VALUE);
}
public void destroy() {
this.recycleBitmaps();
mDestroyed = true;
if (mImageSwitchTask != null) {
mImageSwitchTask.cancel(true);
mImageSwitchTask = null;
}
if (mPrepareScreenTask != null) {
mPrepareScreenTask.cancel(true);
mPrepareScreenTask = null;
}
if (mGetNativeUrlTask != null) {
mGetNativeUrlTask.cancel(true);
mGetNativeUrlTask = null;
}
if (soundPlayer != null && soundPlayer.isPlaying()) {
soundPlayer.stop();
}
}
public boolean fitHeight() {
return fit(Constants.SCALE_MODE_HEIGHT_VALUE);
}
public boolean fitScreen() {
return fit(Constants.SCALE_MODE_BEST_VALUE);
}
public boolean fitWidth() {
return fit(Constants.SCALE_MODE_WIDTH_VALUE);
}
public int getFrameIndex() {
return framePosition;
}
public int getIndex() {
return position;
}
public boolean goToScreen(int index) {
if (!isAnimating()) {
if (index < 0 || index >= comic.getLength()) {
index = 0;
}
moveForwardAfterVideo = true;
setPosition(index, index > position, false);
return true;
}
return false;
}
public boolean isBottomMost() {
return isBottomMost;
}
public boolean isLeftMost() {
return isLeftMost;
}
public boolean isLoading() {
return loadingPage;
}
public boolean isMaxZoom() {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
return current.getImage().getZoomFactor() >= Constants.MAX_ZOOM_FACTOR;
}
public boolean isRightMost() {
return isRightMost;
}
public boolean isTopMost() {
return isTopMost;
}
public boolean goToCurrent() {
if (!isAnimating()) {
if (mustConsiderFrames()) {
setFrame(framePosition, true, true);
}
return true;
} else {
return false;
}
}
/**
* Shows the next frame or the next screen if the current screen has no more frames or frames are disabled.
* @return
*/
public boolean next() {
if (!isAnimating()) {
if (mustConsiderFrames()) {
if (framePosition >= comic.getFramesSize(position)-1) { // Last frame
return nextScreen();
} else { // Any other frame
setFrame(framePosition + 1, true, true);
return true;
}
} else {
return nextScreen();
}
}
return false;
}
public boolean nextScreen() {
if (!isAnimating()) {
return forceNextScreen();
}
return false;
}
public void onCompletion(MediaPlayer mp) {
mVideoPlaying = false;
mSwitcher.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.INVISIBLE);
if (moveForwardAfterVideo) {
forceNextScreen();
} else {
forcePreviousScreen();
}
}
public boolean onError(MediaPlayer mp, int what, int extra) {
mVideoPlaying = false;
stopAnimating();
mVideoView.setVisibility(View.INVISIBLE);
mSwitcher.setVisibility(View.VISIBLE);
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
current.getImage().abortScrollerAnimation();
return false;
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
super.onTouchEvent(motionEvent);
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
isLeftMost = current.getImage().isLeftMost();
isRightMost = current.getImage().isRightMost();
isTopMost = current.getImage().isTopMost();
isBottomMost = current.getImage().isBottomMost();
}
}
// TODO: Abort scroller animation here?
return false;
}
/**
* Shows the previous frame or the previous screen if the current has no previous frames or frames are disabled.
* @return
*/
public boolean previous() {
if (!isAnimating()) {
if (mustConsiderFrames()) {
if (framePosition <= 0) { // First frame
return previousScreen();
} else { // Any other frame
setFrame(framePosition - 1, false, true);
return true;
}
} else {
return previousScreen();
}
}
return false;
}
public boolean previousScreen() {
if (!isAnimating()) {
return this.forcePreviousScreen();
}
return false;
}
public void recycleBitmaps() {
final ComicFrame next = (ComicFrame) mSwitcher.getNextView();
if (next != null) next.getImage().recycleBitmap();
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) current.getImage().recycleBitmap();
}
public boolean scroll(int distanceX, int distanceY) {
if (!isAnimating()) {
ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
current.getLetterbox().hide(true);
current.removeContent();
current.getImage().safeScrollBy(distanceX, distanceY);
return true;
}
return false;
}
public void scroll(MotionEvent event) {
if (!isAnimating()) {
ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (!current.getImage().isSmallerThanRootView()) {
current.getLetterbox().hide(true);
current.removeContent();
current.getImage().smoothScroll(event);
}
}
}
public void setComic(Comic comic) {
this.comic = comic;
position = -1; // TODO: Why -1?
framePosition = 0;
messageButton.setVisibility(GONE);
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
current.getLetterbox().hide(false); // TODO: Is this necessary?
}
}
public void setListener(ComicViewListener listener) {
this.mListener = listener;
}
public Point toImagePoint(Point viewPoint) {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
return current.getImage().toImagePoint(viewPoint);
}
public Rect getOriginalSize() {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
return current.getImage().getOriginalSize();
}
// TODO: Combine in a single zoom public method
public boolean zoom(int increment, Point viewPoint) {
if (!isAnimating()) {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
current.getImage().zoom(increment, viewPoint);
current.getLetterbox().hide(true);
current.removeContent();
return true;
}
}
return false;
}
public boolean zoom(float factor, Point imagePoint) {
if (!isAnimating()) {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
current.getLetterbox().hide(true);
current.removeContent();
current.getImage().zoom(factor, imagePoint);
return true;
}
}
return false;
}
private boolean fit(String scaleMode) {
if (!isAnimating()) {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
Editor editor = preferences.edit();
editor.putString(Constants.SCALE_MODE_KEY, scaleMode);
editor.commit();
current.getImage().scale(scaleMode, true);
return true;
}
}
return false;
}
private boolean forceNextScreen() {
int newPosition = position + 1;
if (newPosition >= comic.getLength()) { // Load next comic
return false;
} else {
moveForwardAfterVideo = true;
setPosition(position + 1, true, true);
return true;
}
}
private boolean forcePreviousScreen() {
int newPosition = position - 1;
if (newPosition < 0) { // Load next comic
return false;
} else {
moveForwardAfterVideo = false;
setPosition(position - 1, false, true);
return true;
}
}
private boolean isAnimating() {
if (mAnimating) {
return true;
} else {
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
if (current != null) {
return current.getImage().isAnimating();
}
}
return false;
}
/**
* Determines if frames must be considered for the current screen.
* @return
*/
private boolean mustConsiderFrames() {
return comic.hasFrames(position);
}
private Runnable mAutoplayRunnable;
private void postFrameDisplayedActions(boolean forward) {
final ACVComic acv = (ACVComic) comic; // If it has frames it's an ACV comic
final boolean vibrate = acv.isVibrate(position, framePosition);
final boolean autoplay;
if (forward) {
autoplay = acv.isAutoplay(position, framePosition);
if (autoplay || mAutoplay) {
long duration = acv.getDuration(position, framePosition);
startAnimating();
mAutoplayRunnable = new Runnable() {
@Override
public void run() {
if (framePosition >= comic.getFramesSize(position) - 1) {
if (position + 1 < comic.getLength()) {
moveForwardAfterVideo = true;
setPosition(position + 1, true, true);
}
} else {
setFrame(framePosition + 1, true, true);
}
}
};
this.postDelayed(mAutoplayRunnable, duration);
}
} else {
autoplay = false;
}
if (vibrate) {
vibrate(!autoplay);
}
if (!vibrate && !autoplay) {
stopAnimating();
}
File sound = acv.getSound(position, framePosition);
if (sound != null) {
playSound(sound);
}
}
private void prepareMessageButton() {
if (comic instanceof ACVComic) {
ACVComic.Message message = ((ACVComic) comic).getMessage(position);
if (message != null) {
messageButton.setEnabled(true);
messageButton.setText(message.text);
messageButton.setVisibility(View.VISIBLE);
} else {
messageButton.setVisibility(View.GONE);
}
} else {
messageButton.setVisibility(View.GONE);
}
}
private void prepareCaption() {
if (mCaption != null) {
if (comic instanceof ACVComic) {
final String caption = ((ACVComic) comic).getDescription(position);
if (caption != null) {
mCaption.setVisibility(View.VISIBLE);
mCaption.setCaption(caption);
mCaption.showCaption();
} else {
mCaption.setVisibility(View.GONE);
}
} else {
mCaption.setVisibility(View.GONE);
}
}
}
private void setFrame(final int framePosition, final boolean forward, final boolean sequential) {
this.framePosition = framePosition;
if (comic instanceof ACVComic) {
final ACVComic acv = (ACVComic) comic;
final ComicFrame current = (ComicFrame) mSwitcher.getCurrentView();
final int width = current.getImage().getOriginalWidth();
final int height = current.getImage().getOriginalHeight();
final Rect frame = acv.rectForSize(position, framePosition, width, height);
final Integer color = acv.getBackgroundColor(position, framePosition);
final boolean letterbox = color != null;
final long transitionDuration;
final int transitionIndex = forward ? framePosition : framePosition + 1;
if (!sequential) {
transitionDuration = 0;
} else {
transitionDuration = acv.getTransitionDuration(position, transitionIndex);
}
final String transitionString = acv.getTransition(position, transitionIndex);
if (Constants.TRANSITION_MODE_TRANSLATE_VALUE.equalsIgnoreCase(transitionString) || transitionDuration == 0) {
// Scroll to frame
final Rect newFrame;
final LayoutMeasures imageMeasures = current.getImage().calculateLayout(frame, !letterbox);
if (transitionDuration > 0) {
startAnimating();
current.getImage().setCSVListener(new SuperImageViewListener() {
@Override
public void onAnimationEnd(SuperImageView view) {
current.showContent(acv, position, framePosition, forward, imageMeasures);
postFrameDisplayedActions(forward);
view.setCSVListener(null);
}
});
current.removeContent();
newFrame = current.getImage().animateTo(frame, !letterbox, transitionDuration);
} else {
newFrame = current.getImage().scrollTo(frame, !letterbox);
current.showContent(acv, position, framePosition, forward, imageMeasures);
}
if (letterbox) {
current.getLetterbox().show(newFrame.width(), newFrame.height(), color, transitionDuration);
} else {
current.getLetterbox().hide(true);
}
if (transitionDuration == 0) {
this.postFrameDisplayedActions(forward);
}
} else { // Switch to frame
setTransition(forward, transitionString, transitionDuration);
mImageSwitchTask = new SwitchImageTask();
mImageSwitchTask.forward = forward;
mImageSwitchTask.sequential = sequential;
mImageSwitchTask.imageIndex = position;
mImageSwitchTask.frameIndex = framePosition;
mImageSwitchTask.execute();
}
}
}
private void startChangeScreenTask(final int value, final boolean forward, final boolean sequential) {
mImageSwitchTask = new SwitchImageTask();
mImageSwitchTask.forward = forward;
mImageSwitchTask.sequential = sequential;
mImageSwitchTask.imageIndex = value;
mImageSwitchTask.execute();
}
private void setPosition(final int value, final boolean forward, final boolean sequential) {
if (!loadingPage) {
if (sequential) {
setSequentialTransition(value, forward);
} else {
mSwitcher.setInAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in));
mSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out));
}
if (lowMemoryTransitions) {
final Integer backgroundColor = comic.getBackgroundColor(value);
final Animation animation;
final Animation inAnimation = mSwitcher.getInAnimation();
final Animation outAnimation = mSwitcher.getOutAnimation();
if (inAnimation != null) {
animation = inAnimation;
inAnimation.setDuration(inAnimation.getDuration() / 2);
} else {
animation = outAnimation;
}
if (outAnimation != null) {
outAnimation.setDuration(outAnimation.getDuration() / 2);
}
if (animation != null) {
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation a) {
a.setAnimationListener(null);
ComicView.this.stopAnimating();
startChangeScreenTask(value, forward, sequential);
}
@Override public void onAnimationRepeat(Animation arg0) {}
@Override public void onAnimationStart(Animation arg0) {}
});
this.startAnimating();
final ComicFrame current = (ComicFrame) mSwitcher.getNextView();
current.getImage().setImageDrawable(new ColorDrawable(backgroundColor));
mSwitcher.showNext();
} else {
startChangeScreenTask(value, forward, sequential);
}
} else {
startChangeScreenTask(value, forward, sequential);
}
}
}
private void setSequentialTransition(int index, boolean forward) {
String transitionMode = null;
final long transitionDuration;
if (comic instanceof ACVComic) {
transitionMode = ((ACVComic) comic).getTransition(forward ? index : index + 1);
transitionDuration = ((ACVComic) comic).getTransitionDuration(forward ? index : index + 1);
} else {
transitionDuration = -1;
}
if (transitionMode == null) {
transitionMode = preferences.getString(Constants.TRANSITION_MODE_KEY, Constants.TRANSITION_MODE_NONE_VALUE);
}
setTransition(forward, transitionMode, transitionDuration);
}
private void setTransition(boolean forward, String transitionString, long duration) {
final Animation inAnimation;
final Animation outAnimation;
if (Constants.TRANSITION_MODE_FADE_VALUE.equals(transitionString)) {
if (nextImageUp) {
inAnimation = AnimationUtils.loadAnimation(getContext(), animationFadeIn);
outAnimation = AnimationUtils.loadAnimation(getContext(), animationKeep);
} else {
inAnimation = AnimationUtils.loadAnimation(getContext(), animationKeep);
outAnimation = AnimationUtils.loadAnimation(getContext(), animationFadeOut);
}
} else if (Constants.TRANSITION_MODE_NONE_VALUE.equals(transitionString)) {
inAnimation = null;
outAnimation = null;
} else if (Constants.TRANSITION_MODE_PUSH_UP_VALUE.equals(transitionString)){
inAnimation = AnimationUtils.loadAnimation(getContext(), forward ? animationPushUpIn : animationPushDownIn);
outAnimation = AnimationUtils.loadAnimation(getContext(), forward ? animationPushUpOut : animationPushDownOut);
} else if (Constants.TRANSITION_MODE_PUSH_DOWN_VALUE.equals(transitionString)){
inAnimation = AnimationUtils.loadAnimation(getContext(), forward ? animationPushDownIn : animationPushUpIn);
outAnimation = AnimationUtils.loadAnimation(getContext(), forward ? animationPushDownOut : animationPushUpOut);
} else {
boolean leftToRight = isLeftToRight();
if ((forward && leftToRight) || (forward && !leftToRight)) {
inAnimation = AnimationUtils.loadAnimation(getContext(), animationPushLeftIn);
outAnimation = AnimationUtils.loadAnimation(getContext(), animationPushLeftOut);
} else {
inAnimation = AnimationUtils.loadAnimation(getContext(), animationPushRightIn);
outAnimation = AnimationUtils.loadAnimation(getContext(), animationPushRightOut);
}
}
if (duration >= 0) {
if (inAnimation != null) {
inAnimation.setDuration(duration);
}
if (outAnimation != null) {
outAnimation.setDuration(duration);
}
}
mSwitcher.setInAnimation(inAnimation);
mSwitcher.setOutAnimation(outAnimation);
}
private void showOfferSubscribeDialog() {
AlertDialog dialog = new AlertDialog.Builder(getContext()).setTitle(
stringUnavailableTitle).setMessage(stringUnavailableText)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
if (mListener != null) {
mListener.onRequestSubscription();
}
}
}).setNegativeButton(android.R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
}
}).create();
dialog.show();
}
private void showScreenNumber() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean showNumber = preferences.getBoolean(Constants.SHOW_NUMBER_KEY, false);
if (showNumber) {
if (toast != null) {
toast.cancel();
}
String message;
if (previousPosition == -1) {
String path = comic.getPath();
File file = new File(path);
message = file.getName();
} else {
message = String.valueOf(1 + position);
}
toast = Toast.makeText(getContext(), message, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.BOTTOM|Gravity.RIGHT, 0, 0);
toast.show();
}
}
private void startAnimating() {
mAnimating = true;
if (mListener != null) {
mListener.onAnimationStart(this);
}
}
private void stopAnimating() {
mAnimating = false;
if (mListener != null) {
mListener.onAnimationEnd(this);
}
}
private void vibrate(final boolean stopAnimationWhenDone) {
final Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null) {
vibrator.vibrate(500);
}
final AnimationSet set = new AnimationSet(true);
int fromXDelta = 1;
int fromYDelta = 1;
final int maxDeltaScaled = MathUtils.dipToPixel(getContext(), 30);
for (int i = 0; i < 5; i++) {
int toXDelta = Math.round((float) (Math.random() * maxDeltaScaled) - ((float) maxDeltaScaled / 2f));
int toYDelta = Math.round((float) (Math.random() * maxDeltaScaled) - ((float) maxDeltaScaled / 2f));
TranslateAnimation t = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);
t.setDuration(50);
t.setStartOffset(50 * i);
set.addAnimation(t);
fromXDelta = toXDelta;
fromYDelta = toYDelta;
}
set.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
if (stopAnimationWhenDone)
stopAnimating();
}
@Override
public void onAnimationRepeat(Animation arg0) {
}
@Override
public void onAnimationStart(Animation arg0) {
startAnimating();
}
});
this.startAnimation(set);
}
}