/** * Copyright (c) 2013-2014. Francisco Contreras, Holland Salazar. * Copyright (c) 2015. Tobias Strebitzer, Francisco Contreras, Holland Salazar. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * Neither the name of the Baker Framework nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written * permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ package com.bakerframework.baker.activity; import android.annotation.SuppressLint; import android.content.Intent; import android.content.pm.ActivityInfo; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.FragmentActivity; import android.support.v4.app.NavUtils; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ViewPager; import android.util.Log; import android.view.GestureDetector; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.Toast; import com.bakerframework.baker.BakerApplication; import com.bakerframework.baker.R; import com.bakerframework.baker.model.BookJson; import com.bakerframework.baker.settings.Configuration; import com.bakerframework.baker.view.CustomWebView; import com.bakerframework.baker.view.WebViewFragment; import com.bakerframework.baker.view.WebViewFragmentPagerAdapter; import com.viewpagerindicator.LinePageIndicator; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.Map; public class IssueActivity extends FragmentActivity { private boolean doubleTap = false; private boolean enableDoubleTap = true; private boolean enableBackNextButton = false; private GestureDetectorCompat gestureDetector; private WebViewFragmentPagerAdapter webViewFragmentPagerAdapter; private ViewPager viewPager; private BookJson jsonBook; public final static String MODAL_URL = "com.bakerframework.baker.MODAL_URL"; public final static String ORIENTATION = "com.bakerframework.baker.ORIENTATION"; private boolean ENABLE_TUTORIAL = false; public BookJson getJsonBook() { return this.jsonBook; } public ViewPager getViewPager() { return this.viewPager; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // We would like to keep the screen on while reading the magazine getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Remove title bar this.requestWindowFeature(Window.FEATURE_NO_TITLE); // Remove notification bar this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // Set Content View setContentView(R.layout.issue_activity); // Initialize Pager viewPager = (ViewPager) findViewById(R.id.pager); // Get issue Intent intent = getIntent(); String issueName = intent.getStringExtra(Configuration.ISSUE_NAME); try { ENABLE_TUTORIAL = intent.getBooleanExtra(Configuration.ISSUE_ENABLE_TUTORIAL, false); if (!intent.getBooleanExtra(Configuration.ISSUE_RETURN_TO_SHELF, true)) { setResult(ShelfActivity.STANDALONE_MAGAZINE_ACTIVITY_FINISH); } else { setResult(0); } jsonBook = new BookJson(); jsonBook.setIssueName(issueName); Log.d(this.getClass().toString(), "THE RAW BOOK.JSON IS: " + intent.getStringExtra(Configuration.BOOK_JSON_KEY)); jsonBook.fromJsonString(intent.getStringExtra(Configuration.BOOK_JSON_KEY)); this.setOrientation(jsonBook.getOrientation()); this.setPagerView(jsonBook); this.setEnableDoubleTap(intent.getBooleanExtra(Configuration.ISSUE_ENABLE_DOUBLE_TAP, true)); this.setEnableBackNextButton(intent.getBooleanExtra(Configuration.ISSUE_ENABLE_BACK_NEXT_BUTTONS, false)); detectFirstOrLastPage(); gestureDetector = new GestureDetectorCompat(this, new MyGestureListener()); } catch (Exception ex) { ex.printStackTrace(); Toast.makeText(this, "Not valid book.json found!", Toast.LENGTH_LONG).show(); } // Plugin Callback BakerApplication.getInstance().getPluginManager().onIssueActivityCreated(this); } private void detectFirstOrLastPage() { if (!isEnableBackNextButton()) { return; } int allItems = this.getJsonBook().getContents().size(); int currentItem = this.viewPager.getCurrentItem(); if (currentItem == (allItems - 1)) { Log.d(this.getClass().getName(), "Last page detected."); ((Button)findViewById(R.id.buttonNext)).setText(getString(R.string.lbl_finish)); if (allItems > 1) { findViewById(R.id.buttonBack).setVisibility(View.VISIBLE); } } else if (currentItem == 0) { Log.d(this.getClass().getName(), "First page detected."); findViewById(R.id.buttonBack).setVisibility(View.GONE); ((Button)findViewById(R.id.buttonNext)).setText(getString(R.string.lbl_next)); } else { findViewById(R.id.buttonBack).setVisibility(View.VISIBLE); ((Button)findViewById(R.id.buttonNext)).setText(getString(R.string.lbl_next)); } } private void goNext() { int currentItem = this.viewPager.getCurrentItem(); int nextItem = currentItem + 1; int allItems = this.getJsonBook().getContents().size(); Log.d(this.getClass().getName(), "All items: " + allItems + ", current item: " + currentItem + ", next item: " + nextItem); if (nextItem < allItems) { this.viewPager.setCurrentItem(nextItem); this.detectFirstOrLastPage(); } else if (nextItem == allItems) { this.finish(); } } private void goBack() { int currentItem = this.viewPager.getCurrentItem(); int nextItem = currentItem - 1; int allItems = this.getJsonBook().getContents().size(); Log.d(this.getClass().getName(), "All items: " + allItems + ", current item: " + currentItem + ", next item: " + nextItem); if (nextItem >= 0) { this.viewPager.setCurrentItem(nextItem); this.detectFirstOrLastPage(); } } private void setOrientation(String _orientation) { _orientation = _orientation.toUpperCase(); final String PORTRAIT = "PORTRAIT"; final String LANDSCAPE = "LANDSCAPE"; switch (_orientation) { case PORTRAIT: this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; case LANDSCAPE: this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; default: this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); break; } } public boolean isEnableDoubleTap() { return enableDoubleTap; } public void setEnableDoubleTap(boolean enableDoubleTap) { this.enableDoubleTap = enableDoubleTap; } public boolean isEnableBackNextButton() { return enableBackNextButton; } public void setEnableBackNextButton(boolean enableBackNextButton) { this.enableBackNextButton = enableBackNextButton; if (enableBackNextButton) { findViewById(R.id.buttonNext).setVisibility(View.VISIBLE); // Click on the next button findViewById(R.id.buttonNext).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { goNext(); } }); // No need for a "Back" button when there's only one page. if (this.getJsonBook().getContents().size() > 1) { findViewById(R.id.buttonBack).setVisibility(View.VISIBLE); // Click on the back button findViewById(R.id.buttonBack).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { goBack(); } }); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.magazine, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // This ID represents the Home or Up button. In the case of this // activity, the Up button is shown. Use NavUtils to allow users // to navigate up one level in the application structure. For // more details, see the Navigation pattern on Android Design: // // http://developer.android.com/design/patterns/navigation.html#up-vs-back // NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); } @SuppressLint("SetJavaScriptEnabled") private void setPagerView(final BookJson book) { // Set asset path final String path = ENABLE_TUTORIAL ? Configuration.getTutorialAssetPath() : Configuration.getMagazineAssetPath(); Log.d(this.getClass().toString(), "THE PATH FOR LOADING THE PAGES WILL BE: " + path); // ViewPager and its adapters use support library fragments, so use getSupportFragmentManager. webViewFragmentPagerAdapter = new WebViewFragmentPagerAdapter(getSupportFragmentManager(), book, path, this); viewPager.setAdapter(webViewFragmentPagerAdapter); viewPager.setOffscreenPageLimit(1); //Bind the title indicator to the adapter LinePageIndicator indicator = (LinePageIndicator)findViewById(R.id.indicator); indicator.setViewPager(viewPager); indicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { super.onPageSelected(position); Log.d(this.getClass().getName(), "Loading page at index: " + position); detectFirstOrLastPage(); } }); // Only show indicator in tutorial mode if (!ENABLE_TUTORIAL) { indicator.setVisibility(View.GONE); } /* indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { WebViewFragment fragment1 = (WebViewFragment) webViewFragmentPagerAdapter.getItem(position); WebViewFragment fragment2 = (WebViewFragment) webViewFragmentPagerAdapter.getItem(position + 1); if(fragment1 != null && fragment1.getView() != null) { fragment1.getWebView().setAlpha(1 - positionOffset); } if(fragment2 != null && fragment2.getView() != null) { fragment2.getWebView().setAlpha(positionOffset); } } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { } }); */ // Set up index webview CustomWebView viewIndex = (CustomWebView) findViewById(R.id.webViewIndex); viewIndex.getSettings().setJavaScriptEnabled(true); viewIndex.getSettings().setUseWideViewPort(true); viewIndex.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String stringUrl) { // mailto links will be handled by the OS. if (stringUrl.startsWith("mailto:")) { Uri uri = Uri.parse(stringUrl); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } else { try { URL url = new URL(stringUrl); // We try to remove the referrer string to avoid passing it to the server in case the URL is an external link. String referrer = ""; if (url.getQuery() != null) { Map<String, String> variables = Configuration.splitUrlQueryString(url); String finalQueryString = ""; for (Map.Entry<String, String> entry : variables.entrySet()) { if (entry.getKey().equals("referrer")) { referrer = entry.getValue(); } else { finalQueryString += entry.getKey() + "=" + entry.getValue() + "&"; } } if (!finalQueryString.isEmpty()) { finalQueryString = "?" + finalQueryString.substring(0, finalQueryString.length() - 1); } stringUrl = stringUrl.replace("?" + url.getQuery(), finalQueryString); } // Aaaaand that was the process of removing the referrer from the query string. if (!url.getProtocol().equals("file")) { Log.d("REFERRER>>>", "THE REFERRER IS: " + referrer); if (referrer.toLowerCase().equals(IssueActivity.this.getString(R.string.url_external_referrer))) { Uri uri = Uri.parse(stringUrl); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } else if (referrer.equals(IssueActivity.this.getString(R.string.url_baker_referrer))) { IssueActivity.this.openLinkInModal(stringUrl); return true; } else { // Open modal window by default IssueActivity.this.openLinkInModal(stringUrl); return true; } } else { stringUrl = url.getPath().substring(url.getPath().lastIndexOf("/") + 1); Log.d(">>>URL_DATA", "FINAL INTERNAL HTML FILENAME = " + stringUrl); int index = IssueActivity.this.getJsonBook().getContents().indexOf(stringUrl); if (index != -1) { Log.d(this.getClass().toString(), "Index to load: " + index + ", page: " + stringUrl); IssueActivity.this.getViewPager().setCurrentItem(index); view.setVisibility(View.GONE); } else { // If the file DOES NOT exist, we won't load it. File htmlFile = new File(url.getPath()); if (htmlFile.exists()) { // Open modal window by default IssueActivity.this.openLinkInModal("file://" + url.getPath()); return true; } } } } catch (MalformedURLException ex) { Log.d(">>>URL_DATA", ex.getMessage()); } catch (UnsupportedEncodingException ignored) {} } return true; } }); viewIndex.loadUrl(path + book.getMagazineName() + File.separator + "index.html"); viewIndex.setBackgroundColor(0x00000000); viewIndex.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null); } @Override public boolean dispatchTouchEvent(@NonNull MotionEvent event) { // Intercept the touch events. this.gestureDetector.onTouchEvent(event); if (doubleTap) { //No need to pass double tap to children doubleTap = false; } else { // We call the superclass implementation for the touch // events to continue along children. return super.dispatchTouchEvent(event); } return true; } @Override public boolean onTouchEvent(MotionEvent event) { this.gestureDetector.onTouchEvent(event); if (doubleTap) { //No need to pass double tap to children doubleTap = false; } else { // We call the superclass implementation. return super.onTouchEvent(event); } return true; } public void openLinkInModal(final String url) { Intent intent = new Intent(this, ModalActivity.class); intent.putExtra(MODAL_URL, url); intent.putExtra(ORIENTATION, this.getRequestedOrientation()); startActivity(intent); } /** * Used to handle the gestures, but we will only need the onDoubleTap. The * other events will be passed to children views. * * @author Holland * */ class MyGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent event) { if (isEnableDoubleTap()) { doubleTap = true; CustomWebView viewIndex = (CustomWebView) findViewById(R.id.webViewIndex); //Disable Index Zoom viewIndex.getSettings().setSupportZoom(false); if (viewIndex.isShown()) { viewIndex.setVisibility(View.GONE); } else { viewIndex.setVisibility(View.VISIBLE); } } return true; } } }