/*******************************************************************************
* 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;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import net.androidcomics.acv.R;
import net.robotmedia.acv.Constants;
import net.robotmedia.acv.adapter.RecentListBaseAdapter;
import net.robotmedia.acv.comic.Comic;
import net.robotmedia.acv.logic.*;
import net.robotmedia.acv.provider.HistoryManager;
import net.robotmedia.acv.ui.settings.mobile.SettingsActivityMobile;
import net.robotmedia.acv.ui.settings.tablet.SettingsActivityTablet;
import net.robotmedia.acv.ui.widget.*;
import net.robotmedia.acv.utils.*;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.*;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.net.Uri;
import android.os.*;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.*;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
public class ComicViewerActivity extends ExtendedActivity implements OnGestureListener, GestureDetector.OnDoubleTapListener, ComicViewListener {
public final static String POSITION_EXTRA = "position";
private class LoadComicTask extends AsyncTask<String, Object, Comic> {
public int initialIndex = 0;
private ProgressDialog progressDialog;
@Override
protected Comic doInBackground(String... params) {
final String path = params[0];
Comic result = Comic.createComic(path);
if (result != null) {
HistoryManager.getInstance(ComicViewerActivity.this).remember(new File(path));
}
return result;
}
protected void onPostExecute (Comic result) {
if (progressDialog != null) progressDialog.dismiss();
if (result != null && !result.isError()) {
comic = result;
trackOpen();
mScreen.setVisibility(View.VISIBLE);
hideRecentItems();
preferencesController.savePreference(Constants.COMIC_PATH_KEY, comic.getPath());
mScreen.setComic(comic);
mScreen.goToScreen(initialIndex);
if(isHoneyComb()) {
new MenuHelper().invalidateOptionsMenu();
}
hideActionBar();
} else {
mScreen.setVisibility(View.GONE);
showRecentItems();
showDialog(Constants.DIALOG_LOAD_ERROR);
}
}
@Override
protected void onPreExecute () {
progressDialog = new ACVDialogFactory(ComicViewerActivity.this).createLoadProgressDialog();
progressDialog.show();
removePreviousComic(true);
}
}
private GestureDetector mGestureDetector;
private ImageButton mCornerTopLeft;
private ImageButton mCornerTopRight;
private ImageButton mCornerBottomLeft;
private ImageButton mCornerBottomRight;
private RelativeLayout mAdsContainer;
private boolean mainMenuActive = false;
protected Comic comic;
protected boolean destroyed = false;
protected ACVDialogFactory dialogFactory;
protected LoadComicTask loadComicTask = null;
protected boolean markCleanExitPending = false;
protected ViewGroup mRecentItems = null;
protected ListView mRecentItemsList = null;
protected RecentListBaseAdapter mRecentItemsListAdapter = null;
protected View mButtonsContainer;
protected View mMain;
protected ComicView mScreen;
protected PreferencesController preferencesController;
protected SharedPreferences preferences;
protected boolean requestedRotation = false;
private String mComicPath; // Used for testing
public String getComicPath() {
return mComicPath;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Constants.SCREEN_BROWSER_CODE && resultCode == RESULT_OK) {
int index = data.getIntExtra(BrowseActivity.POSITION_EXTRA, mScreen.getIndex());
if (isComicLoaded()) mScreen.goToScreen(index);
} else if (requestCode == Constants.SD_BROWSER_CODE && resultCode == RESULT_OK) {
String absolutePath = data.getStringExtra(Constants.COMIC_PATH_KEY);
this.loadComic(absolutePath);
} else if (requestCode == Constants.SETTINGS_CODE) {
boolean sensor = preferences.getBoolean(Constants.AUTO_ROTATE_KEY, false);
if (sensor) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
if (preferencesController.isLeftToRight()) {
if (preferencesController.isUsedForPreviousNext(Constants.TRACKBALL_RIGHT_KEY, Constants.TRACKBALL_LEFT_KEY) ||
preferencesController.isUsedForPreviousNext(Constants.INPUT_FLING_LEFT, Constants.INPUT_FLING_RIGHT) ||
preferencesController.isUsedForPreviousNext(Constants.INPUT_CORNER_BOTTOM_RIGHT, Constants.INPUT_CORNER_BOTTOM_LEFT)) {
showDialog(Constants.DIALOG_FLIP_CONTROLS);
}
} else {
if (preferencesController.isUsedForPreviousNext(Constants.TRACKBALL_LEFT_KEY, Constants.TRACKBALL_RIGHT_KEY) ||
preferencesController.isUsedForPreviousNext(Constants.INPUT_FLING_RIGHT, Constants.INPUT_FLING_LEFT) ||
preferencesController.isUsedForPreviousNext(Constants.INPUT_CORNER_BOTTOM_LEFT, Constants.INPUT_CORNER_BOTTOM_RIGHT)) {
showDialog(Constants.DIALOG_FLIP_CONTROLS);
}
}
this.adjustCornersVisibility(true); // Actions assigned to corners might have changed
adjustBrightness();
adjustLowMemoryMode();
if (isComicLoaded()) mScreen.goToScreen(mScreen.getIndex());
} else if (requestCode == Constants.SUBSCRIBE_CODE) {
switch (resultCode) {
case RESULT_OK:
DialogFactory.showSimpleAlert(this, true, R.string.dialog_subscribe_success_title, R.string.dialog_subscribe_success_text);
break;
case SubscribeActivity.RESULT_ERROR:
DialogFactory.showSimpleAlert(this, false, R.string.dialog_subscribe_error_title, R.string.dialog_subscribe_error_text);
break;
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if(!isHoneyComb()) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
dialogFactory = new ACVDialogFactory(this);
mRecentItems = (ViewGroup) findViewById(R.id.main_recent);
mRecentItemsList = (ListView) findViewById(R.id.main_recent_list);
mRecentItemsList.setEmptyView(findViewById(R.id.main_recent_list_no_items));
mRecentItemsListAdapter = new RecentListBaseAdapter(this, R.layout.list_item_recent);
mRecentItemsListAdapter.setMaxNumItems(2);
mRecentItemsList.setAdapter(mRecentItemsListAdapter);
mRecentItemsList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String path = (String) parent.getItemAtPosition(position);
loadComic(path);
}
});
mGestureDetector = new GestureDetector(this);
preferences = PreferenceManager.getDefaultSharedPreferences(this);
mButtonsContainer = findViewById(R.id.main_buttons_container);
mMain = findViewById(R.id.main_layout);
ImageView logo = (ImageView) findViewById(R.id.main_logo);
logo.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
showMenu();
}});
mScreen = (ComicView) findViewById(R.id.screen);
mScreen.setListener(this);
mCornerTopLeft = (ImageButton) findViewById(R.id.corner_top_left);
mCornerTopRight = (ImageButton) findViewById(R.id.corner_top_right);
mCornerBottomLeft = (ImageButton) findViewById(R.id.corner_bottom_left);
mCornerBottomRight = (ImageButton) findViewById(R.id.corner_bottom_right);
adjustCornersVisibility(true);
preferencesController = new PreferencesController(this);
mAdsContainer = (RelativeLayout) findViewById(R.id.mainAdsContainer);
adjustBrightness();
adjustLowMemoryMode();
// TODO: Shouldn't this be first?
if (startupOrientation(savedInstanceState)) { // If no orientation change was requested
preferencesController.checkCleanExit();
markCleanExitPending = true;
}
String comicPath = null;
if(savedInstanceState != null && savedInstanceState.containsKey(Constants.COMIC_PATH_KEY)) {
comicPath = savedInstanceState.getString(Constants.COMIC_PATH_KEY);
}
if(comicPath == null) {
Intent intent = getIntent();
boolean loaded = false;
if(intent != null) {
loaded = attemptToLoadComicFromViewIntent(intent);
}
if(!loaded) {
showRecentItems();
showAds();
}
} else {
loadComic(comicPath);
}
}
@Override
public void onResume() {
mRecentItemsListAdapter.refresh();
this.showAds();
super.onResume();
}
private void adjustLowMemoryMode() {
boolean lowMemory = preferences.getBoolean(PreferencesController.PREFERENCE_LOW_MEMORY, false);
mScreen.setPreload(!lowMemory);
mScreen.setLowMemoryTransitions(lowMemory);
}
private void adjustBrightness() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
float brightness = (float) preferences.getInt(Constants.BRIGHTNESS_KEY, Math.round(lp.screenBrightness * 100)) / 100f;
if (brightness == 0) { // 0 renders the phone unusable
brightness = 1f/100f;
}
lp.screenBrightness = brightness;
getWindow().setAttributes(lp);
}
private void adjustCornerVisibility(ImageButton corner, String key, String defaultAction, boolean allInvisible) {
final String action = preferences.getString(key, defaultAction);
final boolean visible = !allInvisible && !Constants.ACTION_VALUE_NONE.equals(action);
if (visible) {
corner.setImageResource(R.drawable.corner_button);
} else {
corner.setImageDrawable(null);
}
}
private void adjustCornersVisibility(final boolean visible) {
final boolean allInvisible = !visible || preferences.getBoolean(Constants.PREFERENCE_INVISIBLE_CORNERS, false);
adjustCornerVisibility(mCornerTopLeft, Constants.INPUT_CORNER_TOP_LEFT, Constants.ACTION_VALUE_NONE, allInvisible);
adjustCornerVisibility(mCornerTopRight, Constants.INPUT_CORNER_TOP_RIGHT, Constants.ACTION_VALUE_NONE, allInvisible);
adjustCornerVisibility(mCornerBottomLeft, Constants.INPUT_CORNER_BOTTOM_LEFT, Constants.ACTION_VALUE_PREVIOUS, allInvisible);
adjustCornerVisibility(mCornerBottomRight, Constants.INPUT_CORNER_BOTTOM_RIGHT, Constants.ACTION_VALUE_NEXT, allInvisible);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
public boolean onDoubleTap(MotionEvent e) {
String action = preferences.getString(Constants.INPUT_DOUBLE_TAP, Constants.ACTION_VALUE_ZOOM_IN);
Point p = new Point(Math.round(e.getX()), Math.round(e.getY()));
if (Constants.ACTION_VALUE_ZOOM_IN.equals(action) && isComicLoaded() && mScreen.isMaxZoom()) {
return mScreen.zoom(-1, p);
} else {
return action(Constants.INPUT_DOUBLE_TAP, Constants.ACTION_VALUE_ZOOM_IN, p);
}
}
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
public boolean onDown(MotionEvent e) {
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float arg2, float arg3) {
if (mPinch) return false;
final double angle = MathUtils.getAngle(e1.getX(), e1.getY(), e2.getX(), e2.getY());
final int minFlingDifference = MathUtils.dipToPixel(this, Constants.MIN_FLING_DIFFERENCE_DIP);
final float distance = MathUtils.distance(e1.getX(), e1.getY(), e2.getX(), e2.getY());
boolean comicLoaded = isComicLoaded();
if (distance > minFlingDifference) {
if ((angle < Constants.MAX_FLING_ANGLE || Math.abs(angle - 180) < Constants.MAX_FLING_ANGLE)) {
if (e1.getX() > e2.getX()) { // Fling left
if (!comicLoaded || mScreen.isRightMost()) { return action(Constants.INPUT_FLING_LEFT, Constants.ACTION_VALUE_NEXT); }
} else { // Fling right
if (!comicLoaded || mScreen.isLeftMost()) { return action(Constants.INPUT_FLING_RIGHT, Constants.ACTION_VALUE_PREVIOUS); }
}
} else if (angle - 90 < Constants.MAX_FLING_ANGLE) {
if (e1.getY() > e2.getY()) { // Fling up
if (!comicLoaded || mScreen.isBottomMost()) { return action(Constants.INPUT_FLING_UP, Constants.ACTION_MENU); }
} else { // Fling down
if (!comicLoaded || mScreen.isTopMost()) { return action(Constants.INPUT_FLING_DOWN, Constants.ACTION_VALUE_NONE); }
}
}
}
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
String action = Constants.ACTION_VALUE_NONE;
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
action = preferences.getString(Constants.INPUT_VOLUME_UP, Constants.ACTION_VALUE_PREVIOUS);
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
action = preferences.getString(Constants.INPUT_VOLUME_DOWN, Constants.ACTION_VALUE_NEXT);
}
if (Constants.ACTION_VALUE_NONE.equals(action)) {
return super.onKeyUp(keyCode, event);
} else {
return true;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
final String action = preferences.getString(Constants.INPUT_VOLUME_UP, Constants.ACTION_VALUE_PREVIOUS);
if (Constants.ACTION_VALUE_NONE.equals(action)) {
return super.onKeyDown(keyCode, event);
} else {
action(Constants.INPUT_VOLUME_UP, Constants.ACTION_VALUE_PREVIOUS);
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
final String action = preferences.getString(Constants.INPUT_VOLUME_DOWN, Constants.ACTION_VALUE_NEXT);
if (Constants.ACTION_VALUE_NONE.equals(action)) {
return super.onKeyDown(keyCode, event);
} else {
action(Constants.INPUT_VOLUME_DOWN, Constants.ACTION_VALUE_NEXT);
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
return action(Constants.TRACKBALL_RIGHT_KEY, Constants.ACTION_VALUE_NEXT);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
return action(Constants.TRACKBALL_LEFT_KEY, Constants.ACTION_VALUE_PREVIOUS);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
return action(Constants.TRACKBALL_UP_KEY, Constants.ACTION_VALUE_ZOOM_IN);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
return action(Constants.TRACKBALL_DOWN_KEY, Constants.ACTION_VALUE_ZOOM_OUT);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
return action(Constants.TRACKBALL_CENTER_KEY, Constants.ACTION_VALUE_NEXT);
} else if (keyCode == KeyEvent.KEYCODE_BACK) {
final String action = preferences.getString(Constants.BACK_KEY, Constants.ACTION_VALUE_NONE);
if (Constants.ACTION_VALUE_NONE.equals(action)) {
return super.onKeyDown(keyCode, event);
} else if (Constants.ACTION_VALUE_PREVIOUS.equals(action) && mScreen.getIndex() == 0) {
return super.onKeyDown(keyCode, event);
} else {
action(Constants.BACK_KEY, Constants.ACTION_VALUE_NONE);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
public void onLongPress(MotionEvent arg0) {
if (!mPinch) {
action(Constants.LONG_TAP_KEY, Constants.ACTION_VALUE_SCREEN_BROWSER);
}
}
@Override
public boolean onMenuItemSelected (int featureId, MenuItem item){
if(isHoneyComb()) {
boolean comicLoaded = isComicLoaded();
if(item.hasSubMenu()) {
Menu menu = item.getSubMenu();
switch(featureId) {
case R.id.item_share:
menu.findItem(R.id.item_share_screen).setVisible(comicLoaded);
menu.findItem(R.id.item_set_as).setVisible(comicLoaded);
break;
}
} else {
}
}
return super.onMenuItemSelected(featureId, item);
}
@Override
public void onPanelClosed (int featureId, Menu menu) {
super.onPanelClosed(featureId, menu);
hideActionBarDelayed();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
String actionValue = null;
switch (item.getItemId()) {
case R.id.item_zoom_in:
actionValue = Constants.ACTION_VALUE_ZOOM_IN;
break;
case R.id.item_zoom_out:
actionValue = Constants.ACTION_VALUE_ZOOM_OUT;
break;
case R.id.item_fit_width:
actionValue = Constants.ACTION_VALUE_FIT_WIDTH;
break;
case R.id.item_fit_height:
actionValue = Constants.ACTION_VALUE_FIT_HEIGHT;
break;
case R.id.item_actual_size:
actionValue = Constants.ACTION_VALUE_ACTUAL_SIZE;
break;
case R.id.item_first:
actionValue = Constants.ACTION_VALUE_FIRST;
break;
case R.id.item_previous:
actionValue = Constants.ACTION_VALUE_PREVIOUS;
break;
case R.id.item_next:
actionValue = Constants.ACTION_VALUE_NEXT;
break;
case R.id.item_previous_screen:
actionValue = Constants.ACTION_VALUE_PREVIOUS_SCREEN;
break;
case R.id.item_next_screen:
actionValue = Constants.ACTION_VALUE_NEXT_SCREEN;
break;
case R.id.item_last:
actionValue = Constants.ACTION_VALUE_LAST;
break;
case R.id.item_browse:
actionValue = Constants.ACTION_VALUE_SCREEN_BROWSER;
break;
case R.id.item_rotate:
actionValue = Constants.ACTION_VALUE_ROTATE;
break;
case R.id.item_settings:
actionValue = Constants.ACTION_VALUE_SETTINGS;
break;
case R.id.item_open:
actionValue = Constants.ACTION_VALUE_SD_BROWSER;
break;
case R.id.item_share_app:
actionValue = Constants.ACTION_VALUE_SHARE_APP;
break;
case R.id.item_set_as:
actionValue = Constants.ACTION_SET_AS;
break;
case R.id.item_share_screen:
actionValue = Constants.ACTION_VALUE_SHARE_SCREEN;
break;
case R.id.menu_close:
actionValue = Constants.ACTION_CLOSE;
break;
}
if (actionValue != null) {
return this.actionWithValue(actionValue, Constants.EVENT_VALUE_MENU, null);
} else {
return false;
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(!isHoneyComb() && mScreen.isLoading()) {
return false;
}
boolean comicLoaded = isComicLoaded();
menu.findItem(R.id.item_share_screen).setVisible(comicLoaded);
menu.findItem(R.id.item_set_as).setVisible(comicLoaded);
menu.findItem(R.id.item_navigate).setVisible(comicLoaded);
menu.findItem(R.id.item_zoom).setVisible(comicLoaded);
menu.findItem(R.id.item_browse).setVisible(comicLoaded);
menu.findItem(R.id.item_rotate).setVisible(comicLoaded);
if (comicLoaded) {
boolean considerFrames = comic.hasFrames(mScreen.getIndex());
menu.findItem(R.id.item_next_screen).setVisible(considerFrames);
menu.findItem(R.id.item_previous_screen).setVisible(considerFrames);
}
return true;
}
public void onRequestSubscription() {
startSubscribeActivity();
}
public void onScreenLoadFailed() {
mScreen.setVisibility(View.INVISIBLE);
showRecentItems();
// Remove the comic path in case the comic is defective.
// If the page load failed because of an orientation change, the comic path is saved in the instance state anyway.
preferencesController.savePreference(Constants.COMIC_PATH_KEY, null);
removePreviousComic(true);
// Don't want to show an error if the activity was destroyed
if (!destroyed) {
showDialog(Constants.DIALOG_PAGE_ERROR);
}
}
private boolean mScrolling;
public boolean onScroll(MotionEvent downEvent, MotionEvent dragEvent, float distanceX, float distanceY) {
mScrolling = true;
if (isComicLoaded() && !mPinch) {
return mScreen.scroll(Math.round(distanceX), Math.round(distanceY));
} else {
return false;
}
}
public void onShowPress(MotionEvent arg0) {
}
private boolean detectCornerButton(MotionEvent e, boolean pressed, boolean action) {
final float x = e.getX();
final float y = e.getY();
final int width = mMain.getWidth();
final int height = mMain.getHeight();
final int cornerWidth = MathUtils.dipToPixel(this, Constants.CORNER_WIDTH_DIP);
String inputKey = null;
String defaultAction = null;
View button = null;
if (x <= cornerWidth && y <= cornerWidth) {
button = mCornerTopLeft;
inputKey = Constants.INPUT_CORNER_TOP_LEFT;
defaultAction = Constants.ACTION_VALUE_NONE;
} else if (x <= cornerWidth && y >= height - cornerWidth) {
button = mCornerBottomLeft;
inputKey = Constants.INPUT_CORNER_BOTTOM_LEFT;
defaultAction = Constants.ACTION_VALUE_PREVIOUS;
} else if (x >= width - cornerWidth && y <= cornerWidth) {
button = mCornerTopRight;
inputKey = Constants.INPUT_CORNER_TOP_RIGHT;
defaultAction = Constants.ACTION_VALUE_NONE;
} else if (x >= width - cornerWidth && y >= height - cornerWidth) {
button = mCornerBottomRight;
inputKey = Constants.INPUT_CORNER_BOTTOM_RIGHT;
defaultAction = Constants.ACTION_VALUE_NEXT;
}
boolean actionPerformed;
if (action && inputKey != null) {
actionPerformed = action(inputKey, defaultAction);
} else {
actionPerformed = false;
}
if (button != null) {
button.setPressed(pressed);
} else {
unpressCornerButtons();
}
return actionPerformed;
}
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mPinch || mCornerButtonConsumed) return false; // TODO is pinch necessary?
final Point p = new Point(Math.round(e.getX()), Math.round(e.getY()));
boolean processed = action(Constants.SINGLE_TAP_KEY, Constants.ACTION_VALUE_NONE, p);
if(!processed)
toggleControls();
return processed;
}
private boolean mCornerButtonConsumed = false;
public boolean onSingleTapUp(MotionEvent e) {
if (mPinch) return false; // TODO is this necessary?
mCornerButtonConsumed = detectCornerButton(e, false, true);
return mCornerButtonConsumed;
}
public void onStop()
{
super.onStop();
if (markCleanExitPending) {
preferencesController.markCleanExit();
}
}
private void onActionDown(MotionEvent e) {
mPinch = false;
detectCornerButton(e, true, false);
}
private void onActionMove(MotionEvent e) {
if (mPinching && Reflect.getPointerCount(e) == 2) {
final float x0 = Reflect.getX(e, 0);
final float y0 = Reflect.getY(e, 0);
final float x1 = Reflect.getX(e, 1);
final float y1 = Reflect.getY(e, 1);
final float newDistance = MathUtils.distance(x0, y0, x1, y1);
float ratio = newDistance / pinchDistance;
ratio = 1 + (ratio - 1) * 0.5f;
mScreen.zoom(ratio, pinchCenter);
pinchDistance = newDistance;
}
detectCornerButton(e, true, false);
}
private void unpressCornerButtons() {
mCornerBottomLeft.setPressed(false);
mCornerBottomRight.setPressed(false);
mCornerTopLeft.setPressed(false);
mCornerTopRight.setPressed(false);
}
private void onActionUp(MotionEvent e) {
mPinching = false;
mScrolling = false;
unpressCornerButtons();
}
private void onActionPointerDown(MotionEvent e) {
if (!mScrolling && isComicLoaded()) {
mPinch = true;
mPinching = true;
final float x0 = Reflect.getX(e, 0);
final float y0 = Reflect.getY(e, 0);
final float x1 = Reflect.getX(e, 1);
final float y1 = Reflect.getY(e, 1);
pinchDistance = MathUtils.distance(x0, y0, x1, y1);
final int centerX = Math.round(x0 + x1) / 2;
final int centerY = Math.round(y0 + y1) / 2;
Point center = new Point(centerX, centerY);
pinchCenter = mScreen.toImagePoint(center);
}
}
private void onActionPointerUp(MotionEvent e) {}
private boolean mPinch = false;
private boolean mPinching = false;
private Point pinchCenter;
private float pinchDistance;
// TODO: Move pinch logic to SuperImageView.
@Override
public boolean onTouchEvent(MotionEvent e) {
final boolean wasScrolling = mScrolling;
final int action = e.getAction() & Reflect.ACTION_MASK();
switch (action) {
case MotionEvent.ACTION_DOWN:
this.onActionDown(e);
break;
case MotionEvent.ACTION_MOVE:
this.onActionMove(e);
break;
case MotionEvent.ACTION_UP:
this.onActionUp(e);
break;
}
if (action == Reflect.ACTION_POINTER_DOWN()) {
this.onActionPointerDown(e);
} else if (action == Reflect.ACTION_POINTER_UP()) {
this.onActionPointerUp(e);
}
if (mPinching) {
return true;
} else {
if (isComicLoaded() && (mScrolling || wasScrolling)) {
mScreen.scroll(e);
}
return mGestureDetector.onTouchEvent(e);
}
}
private boolean action(String preferenceKey, String defaultValue) {
return action(preferenceKey, defaultValue, null);
}
private boolean action(String preferenceKey, String defaultValue, Point p) {
final String actionValue = preferences.getString(preferenceKey, defaultValue);
return actionWithValue(actionValue, preferenceKey, p);
}
private boolean actionWithValue(String actionValue, String preferenceKey, Point p) {
boolean action = false;
// Actions that require a comic
if (isComicLoaded()) {
final int scrollIncrement = MathUtils.dipToPixel(this, Constants.MANUAL_SCROLL_INCREMENT_DIP);
if (Constants.ACTION_VALUE_PREVIOUS.equals(actionValue)) {
action = previous();
} else if (Constants.ACTION_VALUE_PREVIOUS_SCREEN.equals(actionValue)) {
action = previousScreen();
} else if (Constants.ACTION_VALUE_ZOOM_IN.equals(actionValue)) {
action = mScreen.zoom(1, p);
} else if (Constants.ACTION_VALUE_ZOOM_OUT.equals(actionValue)) {
action = mScreen.zoom(-1, p);
} else if (Constants.ACTION_VALUE_SCROLL_UP.equals(actionValue)) {
action = mScreen.scroll(0, -scrollIncrement);
} else if (Constants.ACTION_VALUE_SCROLL_DOWN.equals(actionValue)) {
action = mScreen.scroll(0, scrollIncrement);
} else if (Constants.ACTION_VALUE_SCROLL_LEFT.equals(actionValue)) {
action = mScreen.scroll(-scrollIncrement, 0);
} else if (Constants.ACTION_VALUE_SCROLL_RIGHT.equals(actionValue)) {
action = mScreen.scroll(scrollIncrement, 0);
} else if (Constants.ACTION_VALUE_FIRST.equals(actionValue)) {
action = first();
} else if (Constants.ACTION_VALUE_LAST.equals(actionValue)) {
action = last();
} else if (Constants.ACTION_VALUE_SCREEN_BROWSER.equals(actionValue)) {
startBrowseActivity();
action = true;
} else if (Constants.ACTION_VALUE_NEXT.equals(actionValue)) {
action = next();
} else if (Constants.ACTION_VALUE_NEXT_SCREEN.equals(actionValue)) {
action = nextScreen();
} else if (Constants.ACTION_VALUE_FIT_WIDTH.equals(actionValue)) {
action = mScreen.fitWidth();
} else if (Constants.ACTION_VALUE_FIT_HEIGHT.equals(actionValue)) {
action = mScreen.fitHeight();
} else if (Constants.ACTION_VALUE_FIT_SCREEN.equals(actionValue)) {
action = mScreen.fitScreen();
} else if (Constants.ACTION_VALUE_ACTUAL_SIZE.equals(actionValue)) {
action = mScreen.actualSize();
} else if (Constants.ACTION_VALUE_SHARE_SCREEN.equals(actionValue)) {
shareScreen();
action = true;
} else if (Constants.ACTION_SET_AS.equals(actionValue)) {
setAs();
action = true;
}
}
// Actions that do not require a comic
if (Constants.ACTION_VALUE_SETTINGS.equals(actionValue)) {
startSettingsActivity();
action = true;
} else if (Constants.ACTION_CLOSE.equals(actionValue)){
close();
action = true;
} else if (Constants.ACTION_VALUE_SD_BROWSER.equals(actionValue)) {
startSDBrowserActivity();
action = true;
} else if (Constants.ACTION_VALUE_ROTATE.equals(actionValue)) {
rotate();
action = true;
} else if (Constants.ACTION_VALUE_SHARE_APP.equals(actionValue)) {
shareApp();
return true;
} else if (Constants.ACTION_MENU.equals(actionValue)) {
showMenu();
return true;
}
if (action) {
TrackingManager.track(actionValue, Constants.EVENT_PARAM_INPUT, preferenceKey);
}
return action;
}
private void setAs() {
SetComicScreenAsTask task = new SetComicScreenAsTask(this, comic);
task.execute(mScreen.getIndex());
}
private void close() {
if (isComicLoaded()) {
removePreviousComic(true);
mScreen.setVisibility(View.GONE);
showRecentItems();
preferencesController.savePreference(Constants.COMIC_PATH_KEY, null);
showAds();
} else {
finish();
}
}
private String describeOrientation(int orientation) {
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
return Constants.EVENT_VALUE_LANDSCAPE;
case Configuration.ORIENTATION_PORTRAIT:
return Constants.EVENT_VALUE_PORTRAIT;
case Configuration.ORIENTATION_SQUARE:
return Constants.EVENT_VALUE_SQUARE;
default:
return Constants.EVENT_VALUE_UNDEFINED;
}
}
private ArrayList<File> findCandidates(File file) {
File parent = file.getParentFile();
String[] allContents = parent.list();
TreeMap<String, File> aux = new TreeMap<String, File>();
HashMap<String, Integer> supportedExtensions = Constants.getSupportedExtensions(this);
if (allContents != null) {
String path = parent.getPath();
for (int i = 0; i < allContents.length; i++) {
String contentName = allContents[i];
String extension = FileUtils.getFileExtension(contentName);
if (!net.robotmedia.acv.utils.FileUtils.isHidden(contentName) && supportedExtensions.containsKey(extension.toLowerCase())) {
File contentFile = new File(path, contentName);
aux.put(contentFile.getName().toLowerCase(), contentFile);
}
}
}
ArrayList<File> candidates = new ArrayList<File>();
candidates.addAll(aux.values());
return candidates;
}
private File findNextComic() {
String comicPath = comic.getPath();
File file = new File(comicPath);
ArrayList<File> candidates = findCandidates(file);
String fileName = file.getName().toLowerCase();
boolean next = false;
File nextComic = null;
for (File candidate : candidates) {
if (next) {
nextComic = candidate;
break;
} else if (fileName.equals(candidate.getName().toLowerCase())) {
next = true;
}
}
return nextComic;
}
private File findPreviousComic() {
String comicPath = comic.getPath();
File file = new File(comicPath);
ArrayList<File> candidates = findCandidates(file);
String fileName = file.getName().toLowerCase();
File previousComic = null;
boolean previous = false;
for (File candidate : candidates) {
if (fileName.equals(candidate.getName().toLowerCase())) {
previous = true;
break;
} else {
previousComic = candidate;
}
}
if (previous) {
return previousComic;
} else {
return null;
}
}
private boolean first() {
return mScreen.goToScreen(0);
}
private boolean isComicLoaded() {
return (comic != null && comic.getLength() > 0 && !comic.isError());
}
private boolean last() {
return mScreen.goToScreen(comic.getLength() - 1);
}
private void loadComic(final String comicPath, final Intent intent) {
final File file = new File(comicPath);
int initialIndex = intent.getIntExtra(POSITION_EXTRA, 0);
if (initialIndex == 0) {
initialIndex = HistoryManager.getInstance(this).getBookmark(file);
}
loadComic(comicPath, initialIndex);
}
private void loadComic(final String comicPath) {
final File file = new File(comicPath);
final int initialIndex = HistoryManager.getInstance(this).getBookmark(file);
loadComic(comicPath, initialIndex);
}
private void loadComic(final String comicPath, final int initialIndex) {
final File file = new File(comicPath);
if (file.exists()) {
hideAds();
mComicPath = comicPath;
loadComicTask = new LoadComicTask();
loadComicTask.initialIndex = initialIndex;
loadComicTask.execute(comicPath);
}
}
private boolean attemptToLoadComicFromViewIntent(Intent intent) {
if (intent.getAction().equals(Intent.ACTION_VIEW)) {
final Uri uri = intent.getData();
try {
final File file = new File(new URI(uri.toString()));
String comicPath = file.getAbsolutePath();
if (comicPath != null) {
loadComic(comicPath, intent);
return true;
}
} catch (URISyntaxException e) {
Log.w("attemptToLoadComicFromViewIntent", "Invalid intent data");
}
}
return false;
}
private boolean next() {
int index = mScreen.getIndex();
int frameIndex = mScreen.getFrameIndex();
if (comic.hasFrames(index) && frameIndex + 1 < comic.getFramesSize(index)) {
return mScreen.next();
} else {
return nextScreen();
}
}
private boolean nextScreen() {
int index = mScreen.getIndex();
if (index + 1 >= comic.getLength()) { // Load next comic
File next = findNextComic();
if (next != null) {
this.loadComic(next.getPath(), 0);
return true;
}
} else {
return mScreen.nextScreen();
}
return false;
}
private boolean previous() {
int index = mScreen.getIndex();
int frameIndex = mScreen.getFrameIndex();
if (comic.hasFrames(index) && frameIndex > 0) {
return mScreen.previous();
} else {
return previousScreen();
}
}
private boolean previousScreen() {
int index = mScreen.getIndex();
if (index - 1 < 0) { // Load previous comic
File previous = findPreviousComic();
if (previous != null) {
this.loadComic(previous.getPath(), 0);
return true;
}
} else {
return mScreen.previousScreen();
}
return false;
}
private void rotate() {
int orientation = getResources().getConfiguration().orientation;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
orientation = Configuration.ORIENTATION_PORTRAIT;
break;
case Configuration.ORIENTATION_PORTRAIT:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
orientation = Configuration.ORIENTATION_LANDSCAPE;
break;
}
Editor editor = preferences.edit();
editor.putInt(Constants.ORIENTATION_KEY, orientation);
editor.commit();
requestedRotation = true;
setRequestedOrientation(requestedOrientation);
}
private void shareApp() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_app_title));
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_app_message));
Intent chooser = Intent.createChooser(intent, getString(R.string.item_share_app_title));
startActivity(chooser);
}
private void shareScreen() {
ShareViewTask task = new ShareViewTask(this);
task.setRelativeTempPath(Constants.TEMP_PATH);
task.setChooserTitle(getString(R.string.item_share_screen_title));
task.setExtraSubject(getString(R.string.share_screen_title));
task.setExtraText(getString(R.string.share_screen_message));
task.setName(comic.getName());
task.execute(mScreen);
}
/**
* Shows the menu.
*/
private void showMenu() {
if(isHoneyComb() && !isIcecream())
showActionBar();
openOptionsMenu();
}
private void startBrowseActivity() {
if (isComicLoaded()) {
final Intent intent = new Intent(this, BrowseActivity.class);
intent.putExtra(BrowseActivity.POSITION_EXTRA, mScreen.getIndex());
final String comicID = comic.getID();
intent.putExtra(BrowseActivity.EXTRA_COMIC_ID, comicID);
startActivityForResult(intent, Constants.SCREEN_BROWSER_CODE);
}
}
private void startSDBrowserActivity() {
Intent myIntent = new Intent(this, SDBrowserActivity.class);
String comicsPath = preferences.getString(Constants.COMICS_PATH_KEY, Environment.getExternalStorageDirectory().getAbsolutePath());
myIntent.putExtra(Constants.COMICS_PATH_KEY, comicsPath);
startActivityForResult(myIntent, Constants.SD_BROWSER_CODE);
}
private void startSettingsActivity() {
if(!isHoneyComb()) {
startActivityForResult(new Intent(this, SettingsActivityMobile.class), Constants.SETTINGS_CODE);
} else {
startActivityForResult(new Intent(this, SettingsActivityTablet.class), Constants.SETTINGS_CODE);
}
}
private void startSubscribeActivity() {
Intent myIntent = new Intent(this, SubscribeActivity.class);
startActivityForResult(myIntent, Constants.SUBSCRIBE_CODE);
}
private boolean startupOrientation(Bundle savedInstanceState) {
boolean wasRequestedRotation = savedInstanceState != null ? savedInstanceState.getBoolean(Constants.REQUESTED_ROTATION_KEY) : false;
if (!wasRequestedRotation) { // If the activity was not created because of a rotation request
boolean sensor = preferences.getBoolean(Constants.AUTO_ROTATE_KEY, false);
if (sensor) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} else {
int currentOrientation = getResources().getConfiguration().orientation;
int lastOrientation = preferences.getInt(Constants.ORIENTATION_KEY, currentOrientation);
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
switch (lastOrientation) {
case Configuration.ORIENTATION_LANDSCAPE:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Configuration.ORIENTATION_PORTRAIT:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
}
setRequestedOrientation(requestedOrientation);
return currentOrientation == lastOrientation;
}
}
return true;
}
private void trackOpen() {
TrackingManager.track(Constants.EVENT_OPEN,
Constants.EVENT_PARAM_TYPE, comic.getType(),
Constants.AUTO_ROTATE_KEY, String.valueOf(preferences.getBoolean(Constants.AUTO_ROTATE_KEY, false)),
Constants.DIRECTION_KEY, preferences.getString(Constants.DIRECTION_KEY, Constants.DIRECTION_LEFT_TO_RIGHT_VALUE),
Constants.SCALE_MODE_KEY, preferences.getString(Constants.SCALE_MODE_KEY, Constants.SCALE_MODE_NONE_VALUE),
Constants.TRANSITION_MODE_KEY, preferences.getString(Constants.TRANSITION_MODE_KEY, Constants.TRANSITION_MODE_TRANSLATE_VALUE),
Constants.SHOW_NUMBER_KEY, String.valueOf(preferences.getBoolean(Constants.SHOW_NUMBER_KEY, false)),
Constants.LOAD_LAST_KEY, String.valueOf(preferences.getBoolean(Constants.LOAD_LAST_KEY, true)),
Constants.ORIENTATION_KEY, describeOrientation(getResources().getConfiguration().orientation));
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case Constants.DIALOG_LOAD_ERROR:
return dialogFactory.createLoadErrorDialog();
case Constants.DIALOG_PAGE_ERROR:
return dialogFactory.createPageErrorDialog();
case Constants.DIALOG_FLIP_CONTROLS:
return dialogFactory.createFlipControlsDialog();
}
return null;
}
@Override
protected void onDestroy() {
super.onDestroy();
destroyed = true;
removePreviousComic(true);
if (loadComicTask != null) {
loadComicTask.cancel(true);
}
mScreen.destroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (isComicLoaded()) {
outState.putString(Constants.COMIC_PATH_KEY, comic.getPath());
}
outState.putBoolean(Constants.REQUESTED_ROTATION_KEY, requestedRotation);
}
protected void removePreviousComic(boolean emptyTemp) {
// Free the memory of the current comic
mScreen.recycleBitmaps();
if (emptyTemp) {
File tempDirectory = new File(Environment.getExternalStorageDirectory(), Constants.TEMP_PATH);
if(tempDirectory.exists())
FileUtils.deleteDirectory(tempDirectory);
}
if (comic != null) {
comic.destroy();
comic = null;
}
}
public void onAnimationEnd(ComicView comicView) {
this.adjustCornersVisibility(true);
}
public void onAnimationStart(ComicView comicView) {
this.adjustCornersVisibility(false);
}
@Override
public void onScreenChanged(int index) {
if(comic != null) {
final String path = comic.getPath();
HistoryManager.getInstance(this).setBookmark(new File(path), index);
}
}
private void showAds() {
hideAds();
View ad = AdsManager.getAd(this);
if(ad != null) {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
mAdsContainer.addView(ad, lp);
}
}
private void hideAds() {
AdsManager.destroyAds(this);
mAdsContainer.removeAllViews();
}
@Override
protected boolean toggleControls() {
boolean shown = super.toggleControls();
if(shown) {
showMenu();
}
return shown;
}
private void showRecentItems() {
mRecentItemsListAdapter.refresh();
mRecentItems.setVisibility(View.VISIBLE);
mButtonsContainer.setVisibility(View.INVISIBLE);
}
private void hideRecentItems() {
mRecentItems.setVisibility(View.GONE);
mButtonsContainer.setVisibility(View.VISIBLE);
}
}