/* The MIT License (MIT) Copyright (c) 2013, V. Giacometti, M. Giuriato, B. Petrantuono Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package it.angrydroids.epub3reader; import android.app.Activity; import android.app.DialogFragment; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.view.MotionEventCompat; import android.util.DisplayMetrics; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup.LayoutParams; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Toast; public class EpubReaderMain extends Activity { EpubNavigator navigator; protected static WebView view1; protected static WebView view2; protected static RelativeLayout layoutView1; protected static RelativeLayout layoutView2; protected static LinearLayout layout; protected static Button iconCloseView1; protected static Button iconCloseView2; protected int screenWidth; protected int screenHeight; protected ViewStateEnum stateView1; protected ViewStateEnum stateView2; protected BookEnum bookSelector; protected float swipeOriginX, swipeOriginY; protected static float firstViewSize = (float) 0.5; protected boolean syncScrollActivated = true; protected static String color = "#000000"; // work in progress @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_epub_reader_main); // ----- get activity elements view1 = (WebView) findViewById(R.id.firstViewport); view2 = (WebView) findViewById(R.id.secondViewport); layout = (LinearLayout) findViewById(R.id.mainLayout); iconCloseView1 = (Button) findViewById(R.id.topRightFirstView); iconCloseView2 = (Button) findViewById(R.id.topRightSecondView); layoutView1 = (RelativeLayout) findViewById(R.id.firstViewportLayout); layoutView2 = (RelativeLayout) findViewById(R.id.secondViewportLayout); // ----- // ----- get activity screen size DisplayMetrics metrics = this.getResources().getDisplayMetrics(); screenWidth = metrics.widthPixels; screenHeight = metrics.heightPixels; // ----- navigator = new EpubNavigator(getBaseContext()); // enable JavaScript for cool things to happen! view1.getSettings().setJavaScriptEnabled(true); view2.getSettings().setJavaScriptEnabled(true); // when the app starts, only one view is shown updateView1(ViewStateEnum.books); updateView2(ViewStateEnum.invisible); // ----- SWIPE PAGE view1.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (stateView1 == ViewStateEnum.books) { swipePage(v, event, BookEnum.first); } if (syncScrollActivated == true && stateView2 != ViewStateEnum.invisible) { // syncScroll(v, view2, event); work in progress } WebView view = (WebView) v; return view.onTouchEvent(event); } }); view2.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (stateView2 == ViewStateEnum.books) { swipePage(v, event, BookEnum.second); } WebView view = (WebView) v; return view.onTouchEvent(event); } }); // ----- // ----- VIEW CLOSING iconCloseView1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ViewStateEnum newState = navigator.closeView1(); ViewStateEnum oldState = stateView1; updateView1(newState); if (oldState == ViewStateEnum.books) { updateView2(ViewStateEnum.invisible); } } }); iconCloseView2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { updateView2(navigator.closeView2()); } }); // ----- // ----- NOTE & LINK view1.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (stateView1 == ViewStateEnum.books || stateView1 == ViewStateEnum.notes) { Message msg = new Message(); msg.setTarget(new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); String url = msg.getData().getString( getString(R.string.url)); if (url != null) if (stateView1 == ViewStateEnum.books || stateView1 == ViewStateEnum.notes) { updateView2(ViewStateEnum.notes); navigator.setView2(url); } else { navigator.setView1(url); } } }); view1.requestFocusNodeHref(msg); } return false; } }); view1.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView view, String url) { try { updateView1(navigator.setView1(url)); } catch (Exception e) { errorMessage(getString(R.string.error_LoadPage)); } return true; } }); view2.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { Message msg = new Message(); msg.setTarget(new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); String url = msg.getData().getString( getString(R.string.url)); if (url != null) { try { if (stateView2 == ViewStateEnum.books || stateView2 == ViewStateEnum.notes) { updateView1(ViewStateEnum.notes); navigator.setView1(url); } else { navigator.setView2(url); } } catch (Exception e) { errorMessage(getString(R.string.error_LoadPage)); } } } }); view2.requestFocusNodeHref(msg); return false; } }); view2.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView view, String url) { try { updateView2(navigator.setView2(url)); } catch (Exception e) { errorMessage(getString(R.string.error_LoadPage)); } return true; } }); // ----- // ----- LOAD STATE SharedPreferences preferences = getPreferences(MODE_PRIVATE); if (preferences.getBoolean(getString(R.string.bookOpen), false)) { loadState(preferences); } else { bookSelector = BookEnum.first; Intent goToChooser = new Intent(this, FileChooser.class); startActivityForResult(goToChooser, 0); } // ----- } // load the selected book @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { String path = data.getStringExtra(getString(R.string.bpath)); if (bookSelector == BookEnum.first) { if (navigator.openBook1(path)) { updateView1(ViewStateEnum.books); } else { errorMessage(getString(R.string.error_LoadBook)); } } else if (navigator.openBook2(path)) { updateView2(ViewStateEnum.books); } else { errorMessage(getString(R.string.error_LoadBook)); } } else if (!navigator.isAtLeastOneBookOpen()) { finish(); } } // UI Controller protected void updateView1(ViewStateEnum state) { stateView1 = state; if (stateView1 == ViewStateEnum.invisible) { stateView1 = ViewStateEnum.books; finish(); } } protected void updateView2(ViewStateEnum state) { stateView2 = state; switch (stateView2) { case books: case notes: showSecondView(); break; case invisible: hideSecondView(); break; } } protected void hideSecondView() { layoutView2.setVisibility(RelativeLayout.GONE); } protected void showSecondView() { layoutView2.setVisibility(RelativeLayout.VISIBLE); iconCloseView2.setVisibility(RelativeLayout.VISIBLE); } // Menu @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.epub_reader_main, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (navigator.isParallelTextOn() == false && navigator.isExactlyOneBookOpen() == false) { menu.findItem(R.id.meta1).setVisible(true); menu.findItem(R.id.meta2).setVisible(true); menu.findItem(R.id.toc1).setVisible(true); menu.findItem(R.id.toc2).setVisible(true); menu.findItem(R.id.FirstFront).setVisible(true); menu.findItem(R.id.SecondFront).setVisible(true); } if (navigator.isExactlyOneBookOpen() == false) { menu.findItem(R.id.Synchronize).setVisible(true); menu.findItem(R.id.Align).setVisible(true); } if (navigator.isExactlyOneBookOpen() == true || navigator.isParallelTextOn() == true) { menu.findItem(R.id.meta1).setVisible(false); menu.findItem(R.id.meta2).setVisible(false); menu.findItem(R.id.toc1).setVisible(false); menu.findItem(R.id.toc2).setVisible(false); menu.findItem(R.id.FirstFront).setVisible(false); menu.findItem(R.id.SecondFront).setVisible(false); } if (navigator.isExactlyOneBookOpen() == true) { menu.findItem(R.id.Synchronize).setVisible(false); menu.findItem(R.id.Align).setVisible(false); } // if there is only one view, option "changeSizes" is not visualized if (stateView2.name().equalsIgnoreCase(getString(R.string.invisible))) menu.findItem(R.id.changeSize).setVisible(false); else menu.findItem(R.id.changeSize).setVisible(true); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.FirstEPUB: bookSelector = BookEnum.first; Intent goToChooser1 = new Intent(this, FileChooser.class); goToChooser1.putExtra(getString(R.string.second), getString(R.string.time)); startActivityForResult(goToChooser1, 0); return true; case R.id.SecondEPUB: bookSelector = BookEnum.second; Intent goToChooser2 = new Intent(this, FileChooser.class); goToChooser2.putExtra(getString(R.string.second), getString(R.string.time)); startActivityForResult(goToChooser2, 0); // invalidateOptionsMenu(); return true; case R.id.Front: if (navigator.isExactlyOneBookOpen() == true || navigator.isParallelTextOn() == true) chooseLanguage(BookEnum.first); return true; case R.id.FirstFront: chooseLanguage(BookEnum.first); return true; case R.id.SecondFront: if (navigator.isExactlyOneBookOpen() == false) chooseLanguage(BookEnum.second); else errorMessage(getString(R.string.error_onlyOneBookOpen)); return true; case R.id.PconS: try { boolean yes = navigator.synchronizeView1WithView2(); if (!yes) { errorMessage(getString(R.string.error_onlyOneBookOpen)); } } catch (Exception e) { errorMessage(getString(R.string.error_cannotSynchronize)); } return true; case R.id.SconP: try { boolean ok = navigator.synchronizeView2WithView1(); if (!ok) { errorMessage(getString(R.string.error_onlyOneBookOpen)); } } catch (Exception e) { errorMessage(getString(R.string.error_cannotSynchronize)); } return true; case R.id.Synchronize: boolean sync = navigator.flipSynchronizedReadingActive(); if (!sync) { errorMessage(getString(R.string.error_onlyOneBookOpen)); } return true; case R.id.Metadata: if (navigator.isExactlyOneBookOpen() == true || navigator.isParallelTextOn() == true) { navigator.displayMetadata(BookEnum.first); updateView1(ViewStateEnum.notes); } else { } return true; case R.id.meta1: if (navigator.displayMetadata(BookEnum.first)) updateView1(ViewStateEnum.notes); else errorMessage(getString(R.string.error_metadataNotFound)); return true; case R.id.meta2: if (navigator.displayMetadata(BookEnum.second)) updateView2(ViewStateEnum.notes); else errorMessage(getString(R.string.error_metadataNotFound)); return true; case R.id.tableOfContents: if (navigator.isExactlyOneBookOpen() == true || navigator.isParallelTextOn() == true) { navigator.displayTOC(BookEnum.first); updateView1(ViewStateEnum.notes); } else { } return true; case R.id.toc1: if (navigator.displayTOC(BookEnum.first)) updateView1(ViewStateEnum.notes); else errorMessage(getString(R.string.error_tocNotFound)); return true; case R.id.toc2: if (navigator.displayTOC(BookEnum.second)) updateView1(ViewStateEnum.notes); else errorMessage(getString(R.string.error_tocNotFound)); return true; case R.id.changeSize: try { DialogFragment newFragment = new SetPanelSize(); newFragment.show(getFragmentManager(), ""); } catch (Exception e) { errorMessage(getString(R.string.error_cannotChangeSizes)); } return true; case R.id.Style: // work in progress... try { DialogFragment newFragment = new ChangeCSSMenu(); newFragment.show(getFragmentManager(), ""); } catch (Exception e) { errorMessage(getString(R.string.error_CannotChangeStyle)); } return true; default: return super.onOptionsItemSelected(item); } } // Change page protected void swipePage(View v, MotionEvent event, BookEnum which) { int action = MotionEventCompat.getActionMasked(event); switch (action) { case (MotionEvent.ACTION_DOWN): swipeOriginX = event.getX(); swipeOriginY = event.getY(); break; case (MotionEvent.ACTION_UP): int quarterWidth = (int) (screenWidth * 0.25); float diffX = swipeOriginX - event.getX(); float diffY = swipeOriginY - event.getY(); float absDiffX = Math.abs(diffX); float absDiffY = Math.abs(diffY); if ((diffX > quarterWidth) && (absDiffX > absDiffY)) { try { navigator.goToNextChapter(which); } catch (Exception e) { errorMessage(getString(R.string.error_cannotTurnPage)); } } else if ((diffX < -quarterWidth) && (absDiffX > absDiffY)) { try { navigator.goToPrevChapter(which); } catch (Exception e) { errorMessage(getString(R.string.error_cannotTurnPage)); } } break; } } // TODO work in progress, navigator? /* * protected void syncScroll(View v1, View v2, MotionEvent event) { int * action = MotionEventCompat.getActionMasked(event); * * switch (action) { case (MotionEvent.ACTION_DOWN): swipeOriginY = * event.getY(); swipeOriginX = event.getX(); break; * * case (MotionEvent.ACTION_UP): float endY = event.getY(); float endX = * event.getX(); float diffY = swipeOriginY - endY; float diffX = * swipeOriginX - endX; if (Math.abs(diffY) < Math.abs(diffX) || * Math.abs(diffY) > 300) // solo // per // piccoli // scroll { } * * else { v2.scrollBy(0, (int) diffY); } * * break; } } */ // Language Selection public void chooseLanguage(BookEnum which) { String[] languages; if (which == BookEnum.first) { languages = navigator.getLanguagesBook1(); } else { languages = navigator.getLanguagesBook2(); } if (languages.length > 0) { Bundle bundle = new Bundle(); bundle.putString(getString(R.string.tome), which.toString()); bundle.putStringArray(getString(R.string.lang), languages); LanguageChooser langChooser = new LanguageChooser(); langChooser.setArguments(bundle); langChooser.show(getFragmentManager(), ""); } else { errorMessage(getString(R.string.error_noOtherLanguages)); } } public void refreshLanguages(BookEnum which, int first, int second, int numberOfLanguages) { if (which == BookEnum.second) { int tmp = first; first = second; second = tmp; } navigator.parallelText(which, first, second); if ((which == BookEnum.first) || (numberOfLanguages > 1)) { updateView1(ViewStateEnum.books); } if ((which == BookEnum.second) || (numberOfLanguages > 1)) { updateView2(ViewStateEnum.books); } } // change the views size, changing the weight protected void changeViewsSize(float weight1) { firstViewSize = weight1; if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { weight1 = weight1 + (float) 0.01; LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams( 0, LayoutParams.MATCH_PARENT, weight1); layoutView1.setLayoutParams(params1); LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams( 0, LayoutParams.MATCH_PARENT, (1 - weight1)); layoutView2.setLayoutParams(params2); } else { LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, 0, weight1); layoutView1.setLayoutParams(params1); LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, 0, (1 - weight1)); layoutView2.setLayoutParams(params2); } } // Save/Load State protected void saveState(Editor editor) { editor.putString(getString(R.string.firstState), stateView1.name()); editor.putString(getString(R.string.secondState), stateView2.name()); editor.putFloat(getString(R.string.firstViewSize), getFirstViewWeight()); navigator.saveState(editor); } protected void loadState(SharedPreferences preferences) { updateView1(ViewStateEnum.valueOf(preferences.getString( getString(R.string.firstState), ViewStateEnum.books.name()))); updateView2(ViewStateEnum .valueOf(preferences.getString(getString(R.string.secondState), ViewStateEnum.invisible.name()))); float viewSize = preferences.getFloat( getString(R.string.firstViewSize), getFirstViewWeight()); changeViewsSize(viewSize); if (!navigator.loadState(preferences)) errorMessage(getString(R.string.error_cannotLoadState)); } @Override protected void onPause() { super.onPause(); SharedPreferences preferences = getPreferences(MODE_PRIVATE); Editor editor = preferences.edit(); saveState(editor); editor.commit(); } public static WebView getView1() { return view1; } public static WebView getView2() { return view2; } public static String getColor() { // work in progress return color; } public static void setColor(String my_color) { // work in progress color = my_color; } public static float getFirstViewWeight() { return firstViewSize; } public void errorMessage(String message) { Context context = getApplicationContext(); Toast toast = Toast.makeText(context, message, Toast.LENGTH_SHORT); toast.show(); } }