diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 000000000..d5dd98fd8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,11 @@
+
+
+Issue / Feature -
+FolioReader version -
+FolioReader Stock / Modified -
+Android SDK -
+Mobile / Tablet / Emulator Info -
+Crash / Error -
+
+Steps to reproduce / Describe in detail -
\ No newline at end of file
diff --git a/.github/Issue_Template_Examples.md b/.github/Issue_Template_Examples.md
new file mode 100644
index 000000000..e1688d850
--- /dev/null
+++ b/.github/Issue_Template_Examples.md
@@ -0,0 +1,44 @@
+### Example No 1 -
+
+Issue / Feature - Issue
+FolioReader version - 0.3.7
+FolioReader Stock / Modified - Stock
+Android SDK - 4.4.4
+Mobile / Tablet / Emulator Info - Samsung Galaxy S3
+Crash / Error - Crash
+
+Steps to reproduce / Describe in detail -
+
+I'm using library version 0.3.7
+When trying to open epub from my Activity, eg -
+
+```
+FolioReader folioReader = FolioReader.getInstance(getApplicationContext());
+folioReader.openBook(R.raw.barrett);
+```
+
+Getting this error -
+(While i'm opening the file from my activity)
+
+```
+android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
+ at android.app.ContextImpl.startActivity(ContextImpl.java:672)
+ at android.app.ContextImpl.startActivity(ContextImpl.java:659)
+ at android.content.ContextWrapper.startActivity(ContextWrapper.java:331)
+ at com.folioreader.FolioReader.openBook(FolioReader.java:97)
+ at com.devs.ncert.activity.MainActivity$1.onClick(MainActivity.java:35)
+```
+
+### Example No 2 -
+
+Issue / Feature - Feature
+FolioReader version - 0.3.9
+FolioReader Stock / Modified -
+Android SDK -
+Mobile / Tablet / Emulator Info -
+Crash / Error -
+
+Steps to reproduce / Describe in detail -
+
+Hey guys! I need Horizontal scrolling pagination reading mode.
+Is it possible to make this feature available?
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index badb0ab32..70a1e9514 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,13 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+
# Custom
_site
@@ -37,4 +47,7 @@ out
# Maven
target
release.properties
-pom.xml.*
\ No newline at end of file
+pom.xml.*
+
+# Readium DRM LCP
+liblcp.aar
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 827115c1c..fdd116f4a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,17 +1,23 @@
language: android
-before_install:
- - echo yes | android update sdk --filter extra-android-support --no-ui --force > /dev/null
- - echo yes | android update sdk --filter extra-android-m2repository --no-ui --force > /dev/null
+script:
+ - ./gradlew build
android:
- components:
- - build-tools-27.0.3
- - android-27
- - extra-android-m2repository
licenses:
- - android-sdk-license-.+
+ - android-sdk-preview-license-.+
+ - android-sdk-license-.+
+
+ components:
+ # https://docs.travis-ci.com/user/languages/android/#Installing-a-newer-SDK-Platform-Tools-revision
+ - tools
+ - platform-tools
+ - tools
+
+ # The BuildTools version used by your project
+ - build-tools-28.0.3
- script:
- ./gradlew build
\ No newline at end of file
+ # The SDK version used to compile your project
+ - android-28
+ - extra-android-m2repository
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..a4c765112
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,37 @@
+# Change Log
+
+## 0.5.4
+
+- Search API from codetoart/r2-streamer-kotlin now use window.find() instead of Rangy solution.
+
+## 0.5.3
+
+- FolioReader migrated to AndroidX. You will have to migrate your project to AndroidX. Read more about it here - [Migrating to AndroidX](https://developer.android.com/jetpack/androidx/migrate)
+- Search API calls made asynchronous.
+
+### Bugs Fixed
+
+- FolioReader/FolioReader-Android#190
+- FolioReader/FolioReader-Android#276
+- FolioReader/FolioReader-Android#239
+- FolioReader/FolioReader-Android#308
+- FolioReader/FolioReader-Android#284
+- FolioReader/FolioReader-Android#298
+
+## 0.5.2 (Unreleased)
+
+- Developer now don't need to add network_security_config.xml explicitly.
+- Android Min SDK raised to API 21 as Chrome is stuck on version 30 on API 19.
+- ReadPosition replaced with ReadLocator using CFI.
+- TTS feature temporarily disabled, see KNOWN_ISSUES.md for more information.
+- SearchQueryResults and SearchItem replaced with SearchLocator using CFI.
+
+## 0.5.1
+
+- r2-streamer-java replaced with r2-streamer-kotlin and r2-shared-kotlin
+- WebViewMarker replaced with Native text selection popup.
+- Media Overlay feature temporarily unavailable, see KNOWN_ISSUES.md for more information.
+
+## 0.4.1
+
+- Developer now don't need to specify permissions and FolioActivity tag in AndroidManifest.xml
\ No newline at end of file
diff --git a/KNOWN_ISSUES.md b/KNOWN_ISSUES.md
new file mode 100644
index 000000000..b6c9e0132
--- /dev/null
+++ b/KNOWN_ISSUES.md
@@ -0,0 +1,23 @@
+# Known Issues
+
+This is a list of major known issues. For the latest list of all issues see the
+[Github Issues page](https://github.com/FolioReader/FolioReader-Android/issues)
+
+## Errors on API 21 and 22 with Webview lower than version 44
+
+FolioReader might not work properly on API 21 and 22 Emulator images.
+As these images have Android System Webview lower than version 44.
+Devices or Emulator of API 21 and 22 with Google Play Store will be
+able to run FolioReader without any error after updating Android System
+Webview to version 44+
+
+## Media Overlay
+
+From version 0.5.1, Media Overlay would not work as the streamer implementation is not done yet in r2-streamer-kotlin.
+
+## TTS
+
+From version 0.5.2, TTS is disabled.
+In previous versions, sentences were formed by changing the DOM structure.
+To implement TTS without changing DOM would require NLP to extract sentences.
+As of now, no major reading systems other than Google Play Books implements TTS bug free.
\ No newline at end of file
diff --git a/README.md b/README.md
index b81dc0c93..8b7e004a6 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[](https://travis-ci.org/FolioReader/FolioReader-Android)
-FolioReader-Android is an ePub reader written in Java.
+FolioReader-Android is an EPUB reader written in Java and Kotlin.
### Features
@@ -13,75 +13,80 @@ FolioReader-Android is an ePub reader written in Java.
- [x] List / Edit / Delete Highlights
- [x] Handle Internal and External Links
- [x] Portrait / Landscape
-- [x] Reading Time Left / Pages left
+- [ ] Reading Time Left / Pages left
- [x] In-App Dictionary
-- [x] Media Overlays (Sync text rendering with audio playback)
-- [x] TTS - Text to Speech Support
+- [ ] Media Overlays (Sync text rendering with audio playback)
+- [ ] TTS - Text to Speech Support
- [ ] Parse epub cover image
- [ ] PDF support
-- [ ] Book Search
+- [x] Book Search
- [x] Add Notes to a Highlight
- [ ] Better Documentation
-- [x] Last Read Position Listener
+- [x] Last Read Locator
+- [x] Horizontal Reading
+- [x] Distraction Free Reading
## Demo
##### Custom Fonts

##### Day and Night Mode

-##### Text Highlighting
+##### Text Highlighting

-##### Media Overlays
+##### Media Overlays

### Gradle
-Add following dependency to your app build.gradle
-``` java
-compile 'com.folioreader:folioreader:0.3.9'
-```
-Add maven repository to your top level build.gradle
+Add following dependency to your root project `build.gradle` file:
```groovy
allprojects {
repositories {
- maven {
- url "http://dl.bintray.com/mobisystech/maven"
- }
+ ...
+ jcenter()
+ maven { url "https://jitpack.io" }
+ ...
}
}
```
-### Usage
-
-First add activity tag for FolioActivity in your AndroidManifest.xml
+Add following dependency to your app module `build.gradle` file:
-```xml
-
+```groovy
+dependencies {
+ ...
+ implementation "com.folioreader:folioreader:0.5.4"
+ ...
+}
```
-To use FolioReader, get singleton object of **FolioReader**.
+### Enable Multidex support
+
+Enable Multidex support as explained in this [Android Doc](https://developer.android.com/studio/build/multidex)
+
+### Usage
+
+Get singleton object of `FolioReader`:
```java
-FolioReader folioReader = FolioReader.getInstance(getApplicationContext());
+FolioReader folioReader = FolioReader.get();
```
-Call the function openBook().
+Call the function `openBook()`:
-##### opening book from assets
+##### opening book from assets -
```java
-folioReader.openBook("file:///android_asset/adventures.epub");
+folioReader.openBook("file:///android_asset/TheSilverChair.epub");
```
-##### opening book from raw
+##### opening book from raw -
```java
-folioReader.openBook(R.raw.barrett);
+folioReader.openBook(R.raw.accessible_epub_3);
```
+
## WIKI
* [Home](https://github.com/FolioReader/FolioReader-Android/wiki)
@@ -91,14 +96,20 @@ folioReader.openBook(R.raw.barrett);
* [Highlight Action](https://github.com/FolioReader/FolioReader-Android/wiki/Highlight-Action)
* [Highlight Event](https://github.com/FolioReader/FolioReader-Android/wiki/Highlight-Event)
* [Providing External Highlight](https://github.com/FolioReader/FolioReader-Android/wiki/Providing-External-Highlight)
-* [ReadPosition](https://github.com/FolioReader/FolioReader-Android/wiki/ReadPosition)
- * [Get ReadPosition](https://github.com/FolioReader/FolioReader-Android/wiki/Get-ReadPosition)
- * [Set ReadPosition](https://github.com/FolioReader/FolioReader-Android/wiki/Set-ReadPosition)
+* [ReadLocator](https://github.com/FolioReader/FolioReader-Android/wiki/ReadLocator)
* [Clean up code](https://github.com/FolioReader/FolioReader-Android/wiki/Clean-up-code)
+## Reporting Issue
+
+See [KNOWN_ISSUES](https://github.com/FolioReader/FolioReader-Android/blob/master/KNOWN_ISSUES.md) and [CHANGELOG](https://github.com/FolioReader/FolioReader-Android/blob/master/CHANGELOG.md) first before reporting any issue.
+Please follow [Issue Template](https://github.com/FolioReader/FolioReader-Android/blob/master/.github/ISSUE_TEMPLATE.md) to report any issue.
+
+## Share your application
+If you are using FolioReader in your application, share your application link in [this issue](https://github.com/FolioReader/FolioReader-Android/issues/291)
+
### Credits
1. SwipeLayout
-2. r2-streamer-java
+2. r2-streamer-kotlin
3. Pearson Dictionaries
4. rangy
@@ -129,3 +140,4 @@ folioReader.openBook(R.raw.barrett);
## License
FolioReaderKit is available under the BSD license. See the [LICENSE](https://github.com/FolioReader/FolioReader-Android/blob/master/License.md) file.
+
diff --git a/ViewPager/.gitignore b/ViewPager/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/ViewPager/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/ViewPager/build.gradle b/ViewPager/build.gradle
deleted file mode 100644
index 0869ca67a..000000000
--- a/ViewPager/build.gradle
+++ /dev/null
@@ -1,24 +0,0 @@
-apply plugin: 'android-library'
-
-repositories {
- mavenCentral()
-}
-
-android {
- compileSdkVersion 27
- buildToolsVersion "27.0.0"
-
- defaultConfig {
- minSdkVersion 14
- targetSdkVersion 27
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
- }
-}
-
-dependencies {
- compile 'com.android.support:appcompat-v7:27.0.0'
-}
diff --git a/ViewPager/src/main/AndroidManifest.xml b/ViewPager/src/main/AndroidManifest.xml
deleted file mode 100644
index 7830b68b5..000000000
--- a/ViewPager/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/ViewPager/src/main/java/com/ryanharter/viewpager/PagerAdapter.java b/ViewPager/src/main/java/com/ryanharter/viewpager/PagerAdapter.java
deleted file mode 100644
index f3204a9cd..000000000
--- a/ViewPager/src/main/java/com/ryanharter/viewpager/PagerAdapter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.ryanharter.viewpager;
-
-/**
- * {@inheritDoc}
- */
-public abstract class PagerAdapter extends android.support.v4.view.PagerAdapter {
-
- /**
- * {@inheritDoc}
- * @deprecated Use {@link #getPageSize(int)} instead.
- */
- @Override
- public float getPageWidth(int position) {
- return super.getPageWidth(position);
- }
-
- /**
- * Returns the proportional size (width or height depending on orientation)
- * of a given page as a percentage of the ViewPager's measured size from (0.f-1.f).
- *
- * @param position The position of the page requested
- * @return Proportional size for the given page position
- */
- public float getPageSize(int position) {
- return getPageWidth(position);
- }
-}
diff --git a/ViewPager/src/main/java/com/ryanharter/viewpager/ViewPager.java b/ViewPager/src/main/java/com/ryanharter/viewpager/ViewPager.java
deleted file mode 100644
index f0545d786..000000000
--- a/ViewPager/src/main/java/com/ryanharter/viewpager/ViewPager.java
+++ /dev/null
@@ -1,3294 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.ryanharter.viewpager;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.v4.os.ParcelableCompat;
-import android.support.v4.os.ParcelableCompatCreatorCallbacks;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.KeyEventCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewConfigurationCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.support.v4.widget.EdgeEffectCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.FocusFinder;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Interpolator;
-import android.widget.Scroller;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-/**
- * Layout manager that allows the user to flip left and right
- * through pages of data. You supply an implementation of a
- * {@link PagerAdapter} to generate the pages that the view shows.
- *
- *
Note this class is currently under early design and
- * development. The API will likely change in later updates of
- * the compatibility library, requiring changes to the source code
- * of apps when they are compiled against the newer version.
- *
- *
ViewPager is most often used in conjunction with {@link android.app.Fragment},
- * which is a convenient way to supply and manage the lifecycle of each page.
- * There are standard adapters implemented for using fragments with the ViewPager,
- * which cover the most common use cases. These are
- * {@link android.support.v4.app.FragmentPagerAdapter} and
- * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
- * classes have simple code showing how to build a full user interface
- * with them.
- *
- *
Here is a more complicated example of ViewPager, using it in conjuction
- * with {@link android.app.ActionBar} tabs. You can find other examples of using
- * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code.
- *
- * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java
- * complete}
- */
-public class ViewPager extends ViewGroup {
- private static final String TAG = "ViewPager";
- private static final boolean DEBUG = false;
-
- private static final boolean USE_CACHE = false;
-
- private static final int DEFAULT_OFFSCREEN_PAGES = 1;
- private static final int MAX_SETTLE_DURATION = 600; // ms
- private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
-
- private static final int DEFAULT_GUTTER_SIZE = 16; // dips
-
- private static final int MIN_FLING_VELOCITY = 400; // dips
-
- private static final int[] LAYOUT_ATTRS = new int[] {
- android.R.attr.layout_gravity
- };
-
- private static final int ORIENTATION_HORIZONTAL = 0;
- private static final int ORIENTATION_VERTICAL = 1;
-
- /**
- * Used to track what the expected number of items in the adapter should be.
- * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.
- */
- private int mExpectedAdapterCount;
-
- static class ItemInfo {
- Object object;
- int position;
- boolean scrolling;
- float sizeFactor;
- float offset;
- }
-
- private static final Comparator COMPARATOR = new Comparator(){
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- return lhs.position - rhs.position;
- }
- };
-
- private static final Interpolator sInterpolator = new Interpolator() {
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t * t * t * t * t + 1.0f;
- }
- };
-
- private final ArrayList mItems = new ArrayList();
- private final ItemInfo mTempItem = new ItemInfo();
-
- private final Rect mTempRect = new Rect();
-
- private PagerAdapter mAdapter;
- private int mCurItem; // Index of currently displayed page.
- private int mRestoredCurItem = -1;
- private Parcelable mRestoredAdapterState = null;
- private ClassLoader mRestoredClassLoader = null;
- private Scroller mScroller;
- private PagerObserver mObserver;
-
- private int mOrientation = ORIENTATION_HORIZONTAL;
- private int mPageMargin;
- private Drawable mMarginDrawable;
- private int mLeftPageBounds;
- private int mTopPageBounds;
- private int mRightPageBounds;
- private int mBottomPageBounds;
-
- // Offsets of the first and last items, if known.
- // Set during population, used to determine if we are at the beginning
- // or end of the pager data set during touch scrolling.
- private float mFirstOffset = -Float.MAX_VALUE;
- private float mLastOffset = Float.MAX_VALUE;
-
- private int mChildWidthMeasureSpec;
- private int mChildHeightMeasureSpec;
- private boolean mInLayout;
-
- private boolean mScrollingCacheEnabled;
-
- private boolean mPopulatePending;
- private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
-
- private boolean mIsBeingDragged;
- private boolean mIsUnableToDrag;
- private boolean mIgnoreGutter;
- private int mDefaultGutterSize;
- private int mGutterSize;
- private int mTouchSlop;
- /**
- * Position of the last motion event.
- */
- private float mLastMotionX;
- private float mLastMotionY;
- private float mInitialMotionX;
- private float mInitialMotionY;
- /**
- * ID of the active pointer. This is used to retain consistency during
- * drags/flings if multiple pointers are used.
- */
- private int mActivePointerId = INVALID_POINTER;
- /**
- * Sentinel value for no current active pointer.
- * Used by {@link #mActivePointerId}.
- */
- private static final int INVALID_POINTER = -1;
-
- /**
- * Determines speed during touch scrolling
- */
- private VelocityTracker mVelocityTracker;
- private int mMinimumVelocity;
- private int mMaximumVelocity;
- private int mFlingDistance;
- private int mCloseEnough;
-
- // If the pager is at least this close to its final position, complete the scroll
- // on touch down and let the user interact with the content inside instead of
- // "catching" the flinging pager.
- private static final int CLOSE_ENOUGH = 2; // dp
-
- private boolean mFakeDragging;
- private long mFakeDragBeginTime;
-
- // TODO Do I need separate edge effects, or can I just reuse these with better names like mStartEdge, mEndEdge?
- private EdgeEffectCompat mLeftEdge;
- private EdgeEffectCompat mRightEdge;
-
- private boolean mFirstLayout = true;
- private boolean mNeedCalculatePageOffsets = false;
- private boolean mCalledSuper;
- private int mDecorChildCount;
-
- private OnPageChangeListener mOnPageChangeListener;
- private OnPageChangeListener mInternalPageChangeListener;
- private OnAdapterChangeListener mAdapterChangeListener;
- private PageTransformer mPageTransformer;
- private Method mSetChildrenDrawingOrderEnabled;
-
- private static final int DRAW_ORDER_DEFAULT = 0;
- private static final int DRAW_ORDER_FORWARD = 1;
- private static final int DRAW_ORDER_REVERSE = 2;
- private int mDrawingOrder;
- private ArrayList mDrawingOrderedChildren;
- private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();
-
- /**
- * Indicates that the pager is in an idle, settled state. The current page
- * is fully in view and no animation is in progress.
- */
- public static final int SCROLL_STATE_IDLE = 0;
-
- /**
- * Indicates that the pager is currently being dragged by the user.
- */
- public static final int SCROLL_STATE_DRAGGING = 1;
-
- /**
- * Indicates that the pager is in the process of settling to a final position.
- */
- public static final int SCROLL_STATE_SETTLING = 2;
-
- private final Runnable mEndScrollRunnable = new Runnable() {
- public void run() {
- setScrollState(SCROLL_STATE_IDLE);
- populate();
- }
- };
-
- private int mScrollState = SCROLL_STATE_IDLE;
-
- /**
- * Callback interface for responding to changing state of the selected page.
- */
- public interface OnPageChangeListener {
-
- /**
- * This method will be invoked when the current page is scrolled, either as part
- * of a programmatically initiated smooth scroll or a user initiated touch scroll.
- *
- * @param position Position index of the first page currently being displayed.
- * Page position+1 will be visible if positionOffset is nonzero.
- * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
- * @param positionOffsetPixels Value in pixels indicating the offset from position.
- */
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
-
- /**
- * This method will be invoked when a new page becomes selected. Animation is not
- * necessarily complete.
- *
- * @param position Position index of the new selected page.
- */
- public void onPageSelected(int position);
-
- /**
- * Called when the scroll state changes. Useful for discovering when the user
- * begins dragging, when the pager is automatically settling to the current page,
- * or when it is fully stopped/idle.
- *
- * @param state The new scroll state.
- * @see ViewPager#SCROLL_STATE_IDLE
- * @see ViewPager#SCROLL_STATE_DRAGGING
- * @see ViewPager#SCROLL_STATE_SETTLING
- */
- public void onPageScrollStateChanged(int state);
- }
-
- /**
- * Simple implementation of the {@link OnPageChangeListener} interface with stub
- * implementations of each method. Extend this if you do not intend to override
- * every method of {@link OnPageChangeListener}.
- */
- public static class SimpleOnPageChangeListener implements OnPageChangeListener {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- // This space for rent
- }
-
- @Override
- public void onPageSelected(int position) {
- // This space for rent
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- // This space for rent
- }
- }
-
- /**
- * A PageTransformer is invoked whenever a visible/attached page is scrolled.
- * This offers an opportunity for the application to apply a custom transformation
- * to the page views using animation properties.
- *
- *
As property animation is only supported as of Android 3.0 and forward,
- * setting a PageTransformer on a ViewPager on earlier platform versions will
- * be ignored.
- */
- public interface PageTransformer {
- /**
- * Apply a property transformation to the given page.
- *
- * @param page Apply the transformation to this page
- * @param position Position of page relative to the current front-and-center
- * position of the pager. 0 is front and center. 1 is one full
- * page position to the right, and -1 is one page position to the left.
- */
- public void transformPage(View page, float position);
- }
-
- /**
- * Used internally to monitor when adapters are switched.
- */
- interface OnAdapterChangeListener {
- public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);
- }
-
- /**
- * Used internally to tag special types of child views that should be added as
- * pager decorations by default.
- */
- interface Decor {}
-
- public ViewPager(Context context) {
- this(context, null);
- }
-
- public ViewPager(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ViewPager(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.ViewPager, defStyle, 0);
- mOrientation = a.getInt(R.styleable.ViewPager_orientation, ORIENTATION_HORIZONTAL);
- a.recycle();
-
- initViewPager();
- }
-
- void initViewPager() {
- setWillNotDraw(false);
- setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
- setFocusable(true);
- final Context context = getContext();
- mScroller = new Scroller(context, sInterpolator);
-
- final ViewConfiguration configuration = ViewConfiguration.get(context);
- final float density = context.getResources().getDisplayMetrics().density;
-
- mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
- mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mLeftEdge = new EdgeEffectCompat(context);
- mRightEdge = new EdgeEffectCompat(context);
-
- mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
- mCloseEnough = (int) (CLOSE_ENOUGH * density);
- mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
-
- ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());
-
- if (ViewCompat.getImportantForAccessibility(this)
- == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- ViewCompat.setImportantForAccessibility(this,
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- removeCallbacks(mEndScrollRunnable);
- super.onDetachedFromWindow();
- }
-
- private void setScrollState(int newState) {
- if (mScrollState == newState) {
- return;
- }
-
- mScrollState = newState;
- if (mPageTransformer != null) {
- // PageTransformers can do complex things that benefit from hardware layers.
- enableLayers(newState != SCROLL_STATE_IDLE);
- }
- if (mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageScrollStateChanged(newState);
- }
- }
-
- /**
- * Set a PagerAdapter that will supply views for this pager as needed.
- *
- * @param adapter Adapter to use
- */
- public void setAdapter(PagerAdapter adapter) {
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mObserver);
- mAdapter.startUpdate(this);
- for (int i = 0; i < mItems.size(); i++) {
- final ItemInfo ii = mItems.get(i);
- mAdapter.destroyItem(this, ii.position, ii.object);
- }
- mAdapter.finishUpdate(this);
- mItems.clear();
- removeNonDecorViews();
- mCurItem = 0;
- scrollTo(0, 0);
- }
-
- final PagerAdapter oldAdapter = mAdapter;
- mAdapter = adapter;
- mExpectedAdapterCount = 0;
-
- if (mAdapter != null) {
- if (mObserver == null) {
- mObserver = new PagerObserver();
- }
- mAdapter.registerDataSetObserver(mObserver);
- mPopulatePending = false;
- final boolean wasFirstLayout = mFirstLayout;
- mFirstLayout = true;
- mExpectedAdapterCount = mAdapter.getCount();
- if (mRestoredCurItem >= 0) {
- mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
- setCurrentItemInternal(mRestoredCurItem, false, true);
- mRestoredCurItem = -1;
- mRestoredAdapterState = null;
- mRestoredClassLoader = null;
- } else if (!wasFirstLayout) {
- populate();
- } else {
- requestLayout();
- }
- }
-
- if (mAdapterChangeListener != null && oldAdapter != adapter) {
- mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
- }
- }
-
- private void removeNonDecorViews() {
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.isDecor) {
- removeViewAt(i);
- i--;
- }
- }
- }
-
- /**
- * Retrieve the current adapter supplying pages.
- *
- * @return The currently registered PagerAdapter
- */
- public PagerAdapter getAdapter() {
- return mAdapter;
- }
-
- void setOnAdapterChangeListener(OnAdapterChangeListener listener) {
- mAdapterChangeListener = listener;
- }
-
- public int getOrientation() {
- return mOrientation;
- }
-
- public boolean isOrientationHorizontal() {
- return mOrientation == ORIENTATION_HORIZONTAL;
- }
-
- private int getClientWidth() {
- return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- }
-
- private int getClientHeight() {
- return getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- }
-
- /**
- * Set the currently selected page. If the ViewPager has already been through its first
- * layout with its current adapter there will be a smooth animated transition between
- * the current item and the specified item.
- *
- * @param item Item index to select
- */
- public void setCurrentItem(int item) {
- mPopulatePending = false;
- setCurrentItemInternal(item, !mFirstLayout, false);
- }
-
- /**
- * Set the currently selected page.
- *
- * @param item Item index to select
- * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
- */
- public void setCurrentItem(int item, boolean smoothScroll) {
- mPopulatePending = false;
- setCurrentItemInternal(item, smoothScroll, false);
- }
-
- public int getCurrentItem() {
- return mCurItem;
- }
-
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
- setCurrentItemInternal(item, smoothScroll, always, 0);
- }
-
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
- if (mAdapter == null || mAdapter.getCount() <= 0) {
- setScrollingCacheEnabled(false);
- return;
- }
- if (!always && mCurItem == item && mItems.size() != 0) {
- setScrollingCacheEnabled(false);
- return;
- }
-
- if (item < 0) {
- item = 0;
- } else if (item >= mAdapter.getCount()) {
- item = mAdapter.getCount() - 1;
- }
- final int pageLimit = mOffscreenPageLimit;
- if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
- // We are doing a jump by more than one page. To avoid
- // glitches, we want to keep all current pages in the view
- // until the scroll ends.
- for (int i=0; iNote: Prior to Android 3.0 the property animation APIs did not exist.
- * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.
- *
- * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
- * to be drawn from last to first instead of first to last.
- * @param transformer PageTransformer that will modify each page's animation properties
- */
- public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
- if (Build.VERSION.SDK_INT >= 11) {
- final boolean hasTransformer = transformer != null;
- final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
- mPageTransformer = transformer;
- setChildrenDrawingOrderEnabledCompat(hasTransformer);
- if (hasTransformer) {
- mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
- } else {
- mDrawingOrder = DRAW_ORDER_DEFAULT;
- }
- if (needsPopulate) populate();
- }
- }
-
- void setChildrenDrawingOrderEnabledCompat(boolean enable) {
- if (Build.VERSION.SDK_INT >= 7) {
- if (mSetChildrenDrawingOrderEnabled == null) {
- try {
- mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(
- "setChildrenDrawingOrderEnabled", new Class[] { Boolean.TYPE });
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Can't find setChildrenDrawingOrderEnabled", e);
- }
- }
- try {
- mSetChildrenDrawingOrderEnabled.invoke(this, enable);
- } catch (Exception e) {
- Log.e(TAG, "Error changing children drawing order", e);
- }
- }
- }
-
- @Override
- protected int getChildDrawingOrder(int childCount, int i) {
- final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;
- final int result = ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;
- return result;
- }
-
- /**
- * Set a separate OnPageChangeListener for internal use by the support library.
- *
- * @param listener Listener to set
- * @return The old listener that was set, if any.
- */
- OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
- OnPageChangeListener oldListener = mInternalPageChangeListener;
- mInternalPageChangeListener = listener;
- return oldListener;
- }
-
- /**
- * Returns the number of pages that will be retained to either side of the
- * current page in the view hierarchy in an idle state. Defaults to 1.
- *
- * @return How many pages will be kept offscreen on either side
- * @see #setOffscreenPageLimit(int)
- */
- public int getOffscreenPageLimit() {
- return mOffscreenPageLimit;
- }
-
- /**
- * Set the number of pages that should be retained to either side of the
- * current page in the view hierarchy in an idle state. Pages beyond this
- * limit will be recreated from the adapter when needed.
- *
- *
This is offered as an optimization. If you know in advance the number
- * of pages you will need to support or have lazy-loading mechanisms in place
- * on your pages, tweaking this setting can have benefits in perceived smoothness
- * of paging animations and interaction. If you have a small number of pages (3-4)
- * that you can keep active all at once, less time will be spent in layout for
- * newly created view subtrees as the user pages back and forth.
- *
- *
You should keep this limit low, especially if your pages have complex layouts.
- * This setting defaults to 1.
- *
- * @param limit How many pages will be kept offscreen in an idle state.
- */
- public void setOffscreenPageLimit(int limit) {
- if (limit < DEFAULT_OFFSCREEN_PAGES) {
- Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
- DEFAULT_OFFSCREEN_PAGES);
- limit = DEFAULT_OFFSCREEN_PAGES;
- }
- if (limit != mOffscreenPageLimit) {
- mOffscreenPageLimit = limit;
- populate();
- }
- }
-
- /**
- * Set the margin between pages.
- *
- * @param marginPixels Distance between adjacent pages in pixels
- * @see #getPageMargin()
- * @see #setPageMarginDrawable(Drawable)
- * @see #setPageMarginDrawable(int)
- */
- public void setPageMargin(int marginPixels) {
- final int oldMargin = mPageMargin;
- mPageMargin = marginPixels;
-
- int spacing = 0;
- if (isOrientationHorizontal()) {
- spacing = getWidth();
- } else {
- spacing = getHeight();
- }
- recomputeScrollPosition(spacing, spacing, spacing, spacing, marginPixels, oldMargin);
-
- requestLayout();
- }
-
- /**
- * Return the margin between pages.
- *
- * @return The size of the margin in pixels
- */
- public int getPageMargin() {
- return mPageMargin;
- }
-
- /**
- * Set a drawable that will be used to fill the margin between pages.
- *
- * @param d Drawable to display between pages
- */
- public void setPageMarginDrawable(Drawable d) {
- mMarginDrawable = d;
- if (d != null) refreshDrawableState();
- setWillNotDraw(d == null);
- invalidate();
- }
-
- /**
- * Set a drawable that will be used to fill the margin between pages.
- *
- * @param resId Resource ID of a drawable to display between pages
- */
- public void setPageMarginDrawable(int resId) {
- setPageMarginDrawable(getContext().getResources().getDrawable(resId));
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || who == mMarginDrawable;
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- final Drawable d = mMarginDrawable;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
- }
- }
-
- // We want the duration of the page snap animation to be influenced by the distance that
- // the screen has to travel, however, we don't want this duration to be effected in a
- // purely linear fashion. Instead, we use this method to moderate the effect that the distance
- // of travel has on the overall snap duration.
- float distanceInfluenceForSnapDuration(float f) {
- f -= 0.5f; // center the values about 0.
- f *= 0.3f * Math.PI / 2.0f;
- return (float) Math.sin(f);
- }
-
- /**
- * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
- *
- * @param x the number of pixels to scroll by on the X axis
- * @param y the number of pixels to scroll by on the Y axis
- */
- void smoothScrollTo(int x, int y) {
- smoothScrollTo(x, y, 0);
- }
-
- /**
- * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
- *
- * @param x the number of pixels to scroll by on the X axis
- * @param y the number of pixels to scroll by on the Y axis
- * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
- */
- void smoothScrollTo(int x, int y, int velocity) {
- if (getChildCount() == 0) {
- // Nothing to do.
- setScrollingCacheEnabled(false);
- return;
- }
- int sx = getScrollX();
- int sy = getScrollY();
- int dx = x - sx;
- int dy = y - sy;
- if (dx == 0 && dy == 0) {
- completeScroll(false);
- populate();
- setScrollState(SCROLL_STATE_IDLE);
- return;
- }
-
- setScrollingCacheEnabled(true);
- setScrollState(SCROLL_STATE_SETTLING);
-
- final int size = isOrientationHorizontal() ? getClientWidth() : getClientHeight();
- final int halfSize = size / 2;
- final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / size);
- final float distance = halfSize + halfSize *
- distanceInfluenceForSnapDuration(distanceRatio);
-
- int duration = 0;
- velocity = Math.abs(velocity);
- if (velocity > 0) {
- duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- } else {
- final float pageSize = size * mAdapter.getPageSize(mCurItem);
- final float pageDelta = (float) Math.abs(dx) / (pageSize + mPageMargin);
- duration = (int) ((pageDelta + 1) * 100);
- }
- duration = Math.min(duration, MAX_SETTLE_DURATION);
-
- mScroller.startScroll(sx, sy, dx, dy, duration);
- ViewCompat.postInvalidateOnAnimation(this);
- }
-
- ItemInfo addNewItem(int position, int index) {
- ItemInfo ii = new ItemInfo();
- ii.position = position;
- ii.object = mAdapter.instantiateItem(this, position);
- ii.sizeFactor = mAdapter.getPageSize(position);
- if (index < 0 || index >= mItems.size()) {
- mItems.add(ii);
- } else {
- mItems.add(index, ii);
- }
- return ii;
- }
-
- void dataSetChanged() {
- // This method only gets called if our observer is attached, so mAdapter is non-null.
-
- final int adapterCount = mAdapter.getCount();
- mExpectedAdapterCount = adapterCount;
- boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&
- mItems.size() < adapterCount;
- int newCurrItem = mCurItem;
-
- boolean isUpdating = false;
- for (int i = 0; i < mItems.size(); i++) {
- final ItemInfo ii = mItems.get(i);
- final int newPos = mAdapter.getItemPosition(ii.object);
-
- if (newPos == PagerAdapter.POSITION_UNCHANGED) {
- continue;
- }
-
- if (newPos == PagerAdapter.POSITION_NONE) {
- mItems.remove(i);
- i--;
-
- if (!isUpdating) {
- mAdapter.startUpdate(this);
- isUpdating = true;
- }
-
- mAdapter.destroyItem(this, ii.position, ii.object);
- needPopulate = true;
-
- if (mCurItem == ii.position) {
- // Keep the current item in the valid range
- newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
- needPopulate = true;
- }
- continue;
- }
-
- if (ii.position != newPos) {
- if (ii.position == mCurItem) {
- // Our current item changed position. Follow it.
- newCurrItem = newPos;
- }
-
- ii.position = newPos;
- needPopulate = true;
- }
- }
-
- if (isUpdating) {
- mAdapter.finishUpdate(this);
- }
-
- Collections.sort(mItems, COMPARATOR);
-
- if (needPopulate) {
- // Reset our known page widths; populate will recompute them.
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.isDecor) {
- lp.sizeFactor = 0.f;
- }
- }
-
- setCurrentItemInternal(newCurrItem, false, true);
- requestLayout();
- }
- }
-
- void populate() {
- populate(mCurItem);
- }
-
- void populate(int newCurrentItem) {
- ItemInfo oldCurInfo = null;
- int focusDirection = View.FOCUS_FORWARD;
- if (mCurItem != newCurrentItem) {
- if (isOrientationHorizontal()) {
- focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
- } else {
- focusDirection = mCurItem < newCurrentItem ? View.FOCUS_DOWN : View.FOCUS_UP;
- }
- oldCurInfo = infoForPosition(mCurItem);
- mCurItem = newCurrentItem;
- }
-
- if (mAdapter == null) {
- sortChildDrawingOrder();
- return;
- }
-
- // Bail now if we are waiting to populate. This is to hold off
- // on creating views from the time the user releases their finger to
- // fling to a new position until we have finished the scroll to
- // that position, avoiding glitches from happening at that point.
- if (mPopulatePending) {
- if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
- sortChildDrawingOrder();
- return;
- }
-
- // Also, don't populate until we are attached to a window. This is to
- // avoid trying to populate before we have restored our view hierarchy
- // state and conflicting with what is restored.
- if (getWindowToken() == null) {
- return;
- }
-
- mAdapter.startUpdate(this);
-
- final int pageLimit = mOffscreenPageLimit;
- final int startPos = Math.max(0, mCurItem - pageLimit);
- final int N = mAdapter.getCount();
- final int endPos = Math.min(N-1, mCurItem + pageLimit);
-
- if (N != mExpectedAdapterCount) {
- String resName;
- try {
- resName = getResources().getResourceName(getId());
- } catch (Resources.NotFoundException e) {
- resName = Integer.toHexString(getId());
- }
- throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +
- " contents without calling PagerAdapter#notifyDataSetChanged!" +
- " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +
- " Pager id: " + resName +
- " Pager class: " + getClass() +
- " Problematic adapter: " + mAdapter.getClass());
- }
-
- // Locate the currently focused item or add it if needed.
- int curIndex = -1;
- ItemInfo curItem = null;
- for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
- final ItemInfo ii = mItems.get(curIndex);
- if (ii.position >= mCurItem) {
- if (ii.position == mCurItem) curItem = ii;
- break;
- }
- }
-
- if (curItem == null && N > 0) {
- curItem = addNewItem(mCurItem, curIndex);
- }
-
- // Fill 3x the available width or up to the number of offscreen
- // pages requested to either side, whichever is larger.
- // If we have no current item we have no work to do.
- if (curItem != null) {
- float extraSizeStart = 0.f;
- int itemIndex = curIndex - 1;
- ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- final int paddingStart = isOrientationHorizontal() ? getPaddingLeft() : getPaddingTop();
- final int clientSize = isOrientationHorizontal() ? getClientWidth() : getClientHeight();
- final float startSizeNeeded = clientSize <= 0 ? 0 :
- 2.f - curItem.sizeFactor + (float) paddingStart / (float) clientSize;
- for (int pos = mCurItem - 1; pos >= 0; pos--) {
- if (extraSizeStart >= startSizeNeeded && pos < startPos) {
- if (ii == null) {
- break;
- }
- if (pos == ii.position && !ii.scrolling) {
- mItems.remove(itemIndex);
- mAdapter.destroyItem(this, pos, ii.object);
- if (DEBUG) {
- Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
- }
- itemIndex--;
- curIndex--;
- ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- }
- } else if (ii != null && pos == ii.position) {
- extraSizeStart += ii.sizeFactor;
- itemIndex--;
- ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- } else {
- ii = addNewItem(pos, itemIndex + 1);
- extraSizeStart += ii.sizeFactor;
- curIndex++;
- ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- }
- }
-
- float extraSizeEnd = curItem.sizeFactor;
- itemIndex = curIndex + 1;
- if (extraSizeEnd < 2.f) {
- ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
- final int paddingEnd = isOrientationHorizontal() ? getPaddingRight() : getPaddingBottom();
- final float endSizeNeeded = clientSize <= 0 ? 0 :
- (float) paddingEnd / (float) clientSize + 2.f;
- for (int pos = mCurItem + 1; pos < N; pos++) {
- if (extraSizeEnd >= endSizeNeeded && pos > endPos) {
- if (ii == null) {
- break;
- }
- if (pos == ii.position && !ii.scrolling) {
- mItems.remove(itemIndex);
- mAdapter.destroyItem(this, pos, ii.object);
- if (DEBUG) {
- Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
- }
- ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
- }
- } else if (ii != null && pos == ii.position) {
- extraSizeEnd += ii.sizeFactor;
- itemIndex++;
- ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
- } else {
- ii = addNewItem(pos, itemIndex);
- itemIndex++;
- extraSizeEnd += ii.sizeFactor;
- ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
- }
- }
- }
-
- calculatePageOffsets(curItem, curIndex, oldCurInfo);
- }
-
- if (DEBUG) {
- Log.i(TAG, "Current page list:");
- for (int i=0; i();
- } else {
- mDrawingOrderedChildren.clear();
- }
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- mDrawingOrderedChildren.add(child);
- }
- Collections.sort(mDrawingOrderedChildren, sPositionComparator);
- }
- }
-
- private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
- final int N = mAdapter.getCount();
- final int size = isOrientationHorizontal() ? getClientWidth() : getClientHeight();
- final float marginOffset = size > 0 ? (float) mPageMargin / size : 0;
- // Fix up offsets for later layout.
- if (oldCurInfo != null) {
- final int oldCurPosition = oldCurInfo.position;
- // Base offsets off of oldCurInfo.
- if (oldCurPosition < curItem.position) {
- int itemIndex = 0;
- ItemInfo ii = null;
- float offset = oldCurInfo.offset + oldCurInfo.sizeFactor + marginOffset;
- for (int pos = oldCurPosition + 1;
- pos <= curItem.position && itemIndex < mItems.size(); pos++) {
- ii = mItems.get(itemIndex);
- while (pos > ii.position && itemIndex < mItems.size() - 1) {
- itemIndex++;
- ii = mItems.get(itemIndex);
- }
- while (pos < ii.position) {
- // We don't have an item populated for this,
- // ask the adapter for an offset.
- offset += mAdapter.getPageSize(pos) + marginOffset;
- pos++;
- }
- ii.offset = offset;
- offset += ii.sizeFactor + marginOffset;
- }
- } else if (oldCurPosition > curItem.position) {
- int itemIndex = mItems.size() - 1;
- ItemInfo ii = null;
- float offset = oldCurInfo.offset;
- for (int pos = oldCurPosition - 1;
- pos >= curItem.position && itemIndex >= 0; pos--) {
- ii = mItems.get(itemIndex);
- while (pos < ii.position && itemIndex > 0) {
- itemIndex--;
- ii = mItems.get(itemIndex);
- }
- while (pos > ii.position) {
- // We don't have an item populated for this,
- // ask the adapter for an offset.
- offset -= mAdapter.getPageSize(pos) + marginOffset;
- pos--;
- }
- offset -= ii.sizeFactor + marginOffset;
- ii.offset = offset;
- }
- }
- }
-
- // Base all offsets off of curItem.
- final int itemCount = mItems.size();
- float offset = curItem.offset;
- int pos = curItem.position - 1;
- mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
- mLastOffset = curItem.position == N - 1 ?
- curItem.offset + curItem.sizeFactor - 1 : Float.MAX_VALUE;
- // Previous pages
- for (int i = curIndex - 1; i >= 0; i--, pos--) {
- final ItemInfo ii = mItems.get(i);
- while (pos > ii.position) {
- offset -= mAdapter.getPageSize(pos--) + marginOffset;
- }
- offset -= ii.sizeFactor + marginOffset;
- ii.offset = offset;
- if (ii.position == 0) mFirstOffset = offset;
- }
- offset = curItem.offset + curItem.sizeFactor + marginOffset;
- pos = curItem.position + 1;
- // Next pages
- for (int i = curIndex + 1; i < itemCount; i++, pos++) {
- final ItemInfo ii = mItems.get(i);
- while (pos < ii.position) {
- offset += mAdapter.getPageSize(pos++) + marginOffset;
- }
- if (ii.position == N - 1) {
- mLastOffset = offset + ii.sizeFactor - 1;
- }
- ii.offset = offset;
- offset += ii.sizeFactor + marginOffset;
- }
-
- mNeedCalculatePageOffsets = false;
- }
-
- /**
- * This is the persistent state that is saved by ViewPager. Only needed
- * if you are creating a sublass of ViewPager that must save its own
- * state, in which case it should implement a subclass of this which
- * contains that state.
- */
- public static class SavedState extends BaseSavedState {
- int position;
- Parcelable adapterState;
- ClassLoader loader;
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(position);
- out.writeParcelable(adapterState, flags);
- }
-
- @Override
- public String toString() {
- return "FragmentPager.SavedState{"
- + Integer.toHexString(System.identityHashCode(this))
- + " position=" + position + "}";
- }
-
- public static final Parcelable.Creator CREATOR
- = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- });
-
- SavedState(Parcel in, ClassLoader loader) {
- super(in);
- if (loader == null) {
- loader = getClass().getClassLoader();
- }
- position = in.readInt();
- adapterState = in.readParcelable(loader);
- this.loader = loader;
- }
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
- ss.position = mCurItem;
- if (mAdapter != null) {
- ss.adapterState = mAdapter.saveState();
- }
- return ss;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- SavedState ss = (SavedState)state;
- super.onRestoreInstanceState(ss.getSuperState());
-
- if (mAdapter != null) {
- mAdapter.restoreState(ss.adapterState, ss.loader);
- setCurrentItemInternal(ss.position, false, true);
- } else {
- mRestoredCurItem = ss.position;
- mRestoredAdapterState = ss.adapterState;
- mRestoredClassLoader = ss.loader;
- }
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- if (!checkLayoutParams(params)) {
- params = generateLayoutParams(params);
- }
- final LayoutParams lp = (LayoutParams) params;
- lp.isDecor |= child instanceof Decor;
- if (mInLayout) {
- if (lp != null && lp.isDecor) {
- throw new IllegalStateException("Cannot add pager decor view during layout");
- }
- lp.needsMeasure = true;
- addViewInLayout(child, index, params);
- } else {
- super.addView(child, index, params);
- }
-
- if (USE_CACHE) {
- if (child.getVisibility() != GONE) {
- child.setDrawingCacheEnabled(mScrollingCacheEnabled);
- } else {
- child.setDrawingCacheEnabled(false);
- }
- }
- }
-
- @Override
- public void removeView(View view) {
- if (mInLayout) {
- removeViewInLayout(view);
- } else {
- super.removeView(view);
- }
- }
-
- ItemInfo infoForChild(View child) {
- for (int i=0; i 0 && !mItems.isEmpty()) {
- final int paddingStart = isOrientationHorizontal() ? getPaddingLeft() : getPaddingTop();
- final int paddingEnd = isOrientationHorizontal() ? getPaddingRight()
- : getPaddingBottom();
- final int sizeWithMargin = size - paddingStart - paddingEnd + margin;
- final int oldSizeWithMargin = oldSize - paddingStart - paddingEnd + oldMargin;
- final int xPos = getScrollX();
- final int yPos = getScrollY();
-
- final float pageOffset = (float) (isOrientationHorizontal() ? xPos : yPos) / oldSizeWithMargin;
- final int newXOffsetPixels = isOrientationHorizontal() ? (int) (pageOffset * sizeWithMargin) : xPos;
- final int newYOffsetPixels = isOrientationHorizontal() ? yPos : (int) (pageOffset * sizeWithMargin);
-
- scrollTo(newXOffsetPixels, newYOffsetPixels);
- if (!mScroller.isFinished()) {
- // We now return to your regularly schedules scroll, already in progress.
- final int newDuration = mScroller.getDuration() - mScroller.timePassed();
- ItemInfo targetInfo = infoForPosition(mCurItem);
- if (isOrientationHorizontal()) {
- mScroller.startScroll(newXOffsetPixels, 0,
- (int) (targetInfo.offset * size), 0, newDuration);
- } else {
- mScroller.startScroll(0, newYOffsetPixels,
- (int) (targetInfo.offset * size), 0, newDuration);
- }
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int count = getChildCount();
- int width = r - l;
- int height = b - t;
- int paddingLeft = getPaddingLeft();
- int paddingTop = getPaddingTop();
- int paddingRight = getPaddingRight();
- int paddingBottom = getPaddingBottom();
- final int scrollX = getScrollX();
- final int scrollY = getScrollY();
-
- int decorCount = 0;
-
- // First pass - decor views. We need to do this in two passes so that
- // we have the proper offsets for non-decor views later.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- int childLeft = 0;
- int childTop = 0;
- if (lp.isDecor) {
- final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- switch (hgrav) {
- default:
- childLeft = paddingLeft;
- break;
- case Gravity.LEFT:
- childLeft = paddingLeft;
- paddingLeft += child.getMeasuredWidth();
- break;
- case Gravity.CENTER_HORIZONTAL:
- childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
- paddingLeft);
- break;
- case Gravity.RIGHT:
- childLeft = width - paddingRight - child.getMeasuredWidth();
- paddingRight += child.getMeasuredWidth();
- break;
- }
- switch (vgrav) {
- default:
- childTop = paddingTop;
- break;
- case Gravity.TOP:
- childTop = paddingTop;
- paddingTop += child.getMeasuredHeight();
- break;
- case Gravity.CENTER_VERTICAL:
- childTop = Math.max((height - child.getMeasuredHeight()) / 2,
- paddingTop);
- break;
- case Gravity.BOTTOM:
- childTop = height - paddingBottom - child.getMeasuredHeight();
- paddingBottom += child.getMeasuredHeight();
- break;
- }
- if (isOrientationHorizontal()) {
- childLeft += scrollX;
- } else {
- childTop += scrollY;
- }
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- decorCount++;
- }
- }
- }
-
- int childSize = 0;
- if (isOrientationHorizontal()) {
- childSize = width - paddingLeft - paddingRight;
- } else {
- childSize = height - paddingTop - paddingBottom;
- }
- // Page views. Do this once we have the right padding offsets from above.
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- ItemInfo ii;
- if (!lp.isDecor && (ii = infoForChild(child)) != null) {
- int loff = (int) (childSize * ii.offset);
- int childLeft = paddingLeft + (isOrientationHorizontal() ? loff : 0);
- int childTop = paddingTop + (isOrientationHorizontal() ? 0 : loff);
- if (lp.needsMeasure) {
- // This was added during layout and needs measurement.
- // Do it now that we know what we're working with.
- lp.needsMeasure = false;
- int widthSpec = 0, heightSpec = 0;
- if (isOrientationHorizontal()) {
- widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (childSize * lp.sizeFactor),
- MeasureSpec.EXACTLY);
- heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (height - paddingTop - paddingBottom),
- MeasureSpec.EXACTLY);
- } else {
- widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (width - paddingLeft - paddingRight),
- MeasureSpec.EXACTLY);
- heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (childSize * lp.sizeFactor),
- MeasureSpec.EXACTLY);
- }
- child.measure(widthSpec, heightSpec);
- }
- if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
- + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
- + "x" + child.getMeasuredHeight());
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
- }
- }
- mLeftPageBounds = paddingLeft;
- mTopPageBounds = paddingTop;
- mRightPageBounds = width - paddingRight;
- mBottomPageBounds = height - paddingBottom;
- mDecorChildCount = decorCount;
-
- if (mFirstLayout) {
- scrollToItem(mCurItem, false, 0, false);
- }
- mFirstLayout = false;
- }
-
- @Override
- public void computeScroll() {
- if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
-
- if (oldX != x || oldY != y) {
- scrollTo(x, y);
- if (!pageScrolled(isOrientationHorizontal() ? x : y)) {
- mScroller.abortAnimation();
- if (isOrientationHorizontal()) {
- scrollTo(0, y);
- } else {
- scrollTo(x, 0);
- }
- }
- }
-
- // Keep on drawing until the animation has finished.
- ViewCompat.postInvalidateOnAnimation(this);
- return;
- }
-
- // Done with scroll, clean up state.
- completeScroll(true);
- }
-
- private boolean pageScrolled(int pos) {
- if (mItems.size() == 0) {
- mCalledSuper = false;
- onPageScrolled(0, 0, 0);
- if (!mCalledSuper) {
- throw new IllegalStateException(
- "onPageScrolled did not call superclass implementation");
- }
- return false;
- }
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int size = isOrientationHorizontal() ? getClientWidth() : getClientHeight();
- final int sizeWithMargin = size + mPageMargin;
- final float marginOffset = (float) mPageMargin / size;
- final int currentPage = ii.position;
- final float pageOffset = (((float) pos / size) - ii.offset) /
- (ii.sizeFactor + marginOffset);
- final int offsetPixels = (int) (pageOffset * sizeWithMargin);
-
- mCalledSuper = false;
- onPageScrolled(currentPage, pageOffset, offsetPixels);
- if (!mCalledSuper) {
- throw new IllegalStateException(
- "onPageScrolled did not call superclass implementation");
- }
- return true;
- }
-
- /**
- * This method will be invoked when the current page is scrolled, either as part
- * of a programmatically initiated smooth scroll or a user initiated touch scroll.
- * If you override this method you must call through to the superclass implementation
- * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
- * returns.
- *
- * @param position Position index of the first page currently being displayed.
- * Page position+1 will be visible if positionOffset is nonzero.
- * @param offset Value from [0, 1) indicating the offset from the page at position.
- * @param offsetPixels Value in pixels indicating the offset from position.
- */
- protected void onPageScrolled(int position, float offset, int offsetPixels) {
- // Offset any decor views if needed - keep them on-screen at all times.
- if (mDecorChildCount > 0) {
- // TODO This is where I start getting tired. Refactor this better later.
- if (isOrientationHorizontal()) {
- final int scrollX = getScrollX();
- int paddingLeft = getPaddingLeft();
- int paddingRight = getPaddingRight();
- final int width = getWidth();
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.isDecor) continue;
-
- final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- int childLeft = 0;
- switch (hgrav) {
- default:
- childLeft = paddingLeft;
- break;
- case Gravity.LEFT:
- childLeft = paddingLeft;
- paddingLeft += child.getWidth();
- break;
- case Gravity.CENTER_HORIZONTAL:
- childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
- paddingLeft);
- break;
- case Gravity.RIGHT:
- childLeft = width - paddingRight - child.getMeasuredWidth();
- paddingRight += child.getMeasuredWidth();
- break;
- }
- childLeft += scrollX;
-
- final int childOffset = childLeft - child.getLeft();
- if (childOffset != 0) {
- child.offsetLeftAndRight(childOffset);
- }
- }
- } else {
- final int scrollY = getScrollY();
- int paddingTop = getPaddingTop();
- int paddingBottom = getPaddingBottom();
- final int height = getHeight();
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.isDecor) continue;
-
- final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int childTop = 0;
- switch (vgrav) {
- default:
- childTop = paddingTop;
- break;
- case Gravity.TOP:
- childTop = paddingTop;
- paddingTop += child.getHeight();
- break;
- case Gravity.CENTER_VERTICAL:
- childTop = Math.max((height - child.getMeasuredHeight()) / 2,
- paddingTop);
- break;
- case Gravity.BOTTOM:
- childTop = height - paddingBottom - child.getMeasuredHeight();
- paddingBottom += child.getMeasuredHeight();
- break;
- }
- childTop += scrollY;
-
- final int childOffset = childTop - child.getTop();
- if (childOffset != 0) {
- child.offsetTopAndBottom(childOffset);
- }
- }
- }
- }
-
- if (mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
- if (mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
-
- if (mPageTransformer != null) {
- final boolean horizontal = isOrientationHorizontal();
- final int scroll = horizontal ? getScrollX() : getScrollY();
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.isDecor) continue;
-
- float transformPos;
- if (horizontal) {
- transformPos = (float) (child.getLeft() - scroll) / getClientWidth();
- } else {
- transformPos = (float) (child.getTop() - scroll) / getClientHeight();
- }
- mPageTransformer.transformPage(child, transformPos);
- }
- }
-
- mCalledSuper = true;
- }
-
- private void completeScroll(boolean postEvents) {
- boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
- if (needPopulate) {
- // Done with scroll, no longer want to cache view drawing.
- setScrollingCacheEnabled(false);
- mScroller.abortAnimation();
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
- if (oldX != x || oldY != y) {
- scrollTo(x, y);
- }
- }
- mPopulatePending = false;
- for (int i=0; i 0) || (x > getWidth() - mGutterSize && dx < 0);
- } else {
- return (x < mGutterSize && dx > 0) || (x > getHeight() - mGutterSize && dx < 0);
- }
- }
-
- private void enableLayers(boolean enable) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final int layerType = enable ?
- ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;
- ViewCompat.setLayerType(getChildAt(i), layerType, null);
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- /*
- * This method JUST determines whether we want to intercept the motion.
- * If we return true, onMotionEvent will be called and we do the actual
- * scrolling there.
- */
-
- final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
-
- // Always take care of the touch gesture being complete.
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- // Release the drag.
- if (DEBUG) Log.v(TAG, "Intercept done!");
- mIsBeingDragged = false;
- mIsUnableToDrag = false;
- mActivePointerId = INVALID_POINTER;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- return false;
- }
-
- // Nothing more to do here if we have decided whether or not we
- // are dragging.
- if (action != MotionEvent.ACTION_DOWN) {
- if (mIsBeingDragged) {
- if (DEBUG) Log.v(TAG, "Intercept returning true!");
- return true;
- }
- if (mIsUnableToDrag) {
- if (DEBUG) Log.v(TAG, "Intercept returning false!");
- return false;
- }
- }
-
- switch (action) {
- case MotionEvent.ACTION_MOVE: {
- /*
- * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
- */
-
- /*
- * Locally do absolute value. mLastMotionY is set to the y value
- * of the down event.
- */
- final int activePointerId = mActivePointerId;
- if (activePointerId == INVALID_POINTER) {
- // If we don't have a valid id, the touch down wasn't on content.
- break;
- }
-
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float dx = x - mLastMotionX;
- final float xDiff = Math.abs(dx);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
- final float dy = y - mLastMotionY;
- final float yDiff = Math.abs(y - mInitialMotionY);
- if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
-
- if (isOrientationHorizontal()) {
- if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&
- canScroll(this, false, (int) dx, (int) x, (int) y)) {
- // Nested view has scrollable area under this point. Let it be handled there.
- mLastMotionX = x;
- mLastMotionY = y;
- mIsUnableToDrag = true;
- return false;
- }
- if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :
- mInitialMotionX - mTouchSlop;
- mLastMotionY = y;
- setScrollingCacheEnabled(true);
- } else if (yDiff > mTouchSlop) {
- // The finger has moved enough in the vertical
- // direction to be counted as a drag... abort
- // any attempt to drag horizontally, to work correctly
- // with children that have scrolling containers.
- if (DEBUG) Log.v(TAG, "Starting unable to drag!");
- mIsUnableToDrag = true;
- }
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- if (performDrag(x)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
- } else {
- if (dy != 0 && !isGutterDrag(mLastMotionY, dy) &&
- canScroll(this, false, (int) dx, (int) x, (int) y)) {
- // Nested view has scrollable area under this point. Let it be handled there.
- mLastMotionX = x;
- mLastMotionY = y;
- mIsUnableToDrag = true;
- return false;
- }
- if (yDiff > mTouchSlop && yDiff * 0.5f > xDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mLastMotionY = dy > 0 ? mInitialMotionY + mTouchSlop :
- mInitialMotionY - mTouchSlop;
- mLastMotionX = x;
- setScrollingCacheEnabled(true);
- } else if (xDiff > mTouchSlop) {
- // The finger has moved enough in the vertical
- // direction to be counted as a drag... abort
- // any attempt to drag horizontally, to work correctly
- // with children that have scrolling containers.
- if (DEBUG) Log.v(TAG, "Starting unable to drag!");
- mIsUnableToDrag = true;
- }
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- if (performDrag(y)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
- }
- break;
- }
-
- case MotionEvent.ACTION_DOWN: {
- /*
- * Remember location of down touch.
- * ACTION_DOWN always refers to pointer index 0.
- */
- mLastMotionX = mInitialMotionX = ev.getX();
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- mIsUnableToDrag = false;
-
- mScroller.computeScrollOffset();
- int distance = 0;
- if (isOrientationHorizontal()) {
- distance = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
- } else {
- distance = Math.abs(mScroller.getFinalY() - mScroller.getCurrY());
- }
- if (mScrollState == SCROLL_STATE_SETTLING && distance > mCloseEnough) {
- // Let the user 'catch' the pager as it animates.
- mScroller.abortAnimation();
- mPopulatePending = false;
- populate();
- mIsBeingDragged = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- } else {
- completeScroll(false);
- mIsBeingDragged = false;
- }
-
- if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
- + " mIsBeingDragged=" + mIsBeingDragged
- + "mIsUnableToDrag=" + mIsUnableToDrag);
- break;
- }
-
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- /*
- * The only time we want to intercept motion events is if we are in the
- * drag mode.
- */
- return mIsBeingDragged;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mFakeDragging) {
- // A fake drag is in progress already, ignore this real one
- // but still eat the touch events.
- // (It is likely that the user is multi-touching the screen.)
- return true;
- }
-
- if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
- // Don't handle edge touches immediately -- they may actually belong to one of our
- // descendants.
- return false;
- }
-
- if (mAdapter == null || mAdapter.getCount() == 0) {
- // Nothing to present or scroll; nothing to touch.
- return false;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getAction();
- boolean needsInvalidate = false;
-
- switch (action & MotionEventCompat.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- mScroller.abortAnimation();
- mPopulatePending = false;
- populate();
- mIsBeingDragged = true;
- setScrollState(SCROLL_STATE_DRAGGING);
-
- // Remember where the motion event started
- mLastMotionX = mInitialMotionX = ev.getX();
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- break;
- }
- case MotionEvent.ACTION_MOVE:
- if (!mIsBeingDragged) {
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
- final float xDiff = Math.abs(x - mLastMotionX);
- final float y = MotionEventCompat.getY(ev, pointerIndex);
- final float yDiff = Math.abs(y - mLastMotionY);
- if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
- if (isOrientationHorizontal()) {
- if (xDiff > mTouchSlop && xDiff > yDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
- mInitialMotionX - mTouchSlop;
- mLastMotionY = y;
- setScrollState(SCROLL_STATE_DRAGGING);
- setScrollingCacheEnabled(true);
- }
- } else {
- if (yDiff > mTouchSlop && yDiff > xDiff) {
- if (DEBUG) Log.v(TAG, "Starting drag!");
- mIsBeingDragged = true;
- mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY + mTouchSlop :
- mInitialMotionY - mTouchSlop;
- mLastMotionX = x;
- setScrollState(SCROLL_STATE_DRAGGING);
- setScrollingCacheEnabled(true);
- }
- }
- }
- // Not else! Note that mIsBeingDragged can be set above.
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- final int activePointerIndex = MotionEventCompat.findPointerIndex(
- ev, mActivePointerId);
- float x = 0;
- if (isOrientationHorizontal()) {
- x = MotionEventCompat.getX(ev, activePointerIndex);
- } else {
- x = MotionEventCompat.getY(ev, activePointerIndex);
- }
- needsInvalidate |= performDrag(x);
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mIsBeingDragged) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- mPopulatePending = true;
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
-
- int initialVelocity, totalDelta;
- float pageOffset;
- if (isOrientationHorizontal()) {
- initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
- velocityTracker, mActivePointerId);
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- pageOffset = (((float) scrollX / width) - ii.offset) / ii.sizeFactor;
- final int activePointerIndex =
- MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float x = MotionEventCompat.getX(ev, activePointerIndex);
- totalDelta = (int) (x - mInitialMotionX);
- } else {
- initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
- velocityTracker, mActivePointerId);
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- pageOffset = (((float) scrollY / height) - ii.offset) / ii.sizeFactor;
- final int activePointerIndex =
- MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- final float y = MotionEventCompat.getY(ev, activePointerIndex);
- totalDelta = (int) (y - mInitialMotionY);
- }
-
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
-
- mActivePointerId = INVALID_POINTER;
- endDrag();
- needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- if (mIsBeingDragged) {
- scrollToItem(mCurItem, true, 0, false);
- mActivePointerId = INVALID_POINTER;
- endDrag();
- needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
- }
- break;
- case MotionEventCompat.ACTION_POINTER_DOWN: {
- final int index = MotionEventCompat.getActionIndex(ev);
- final float x = MotionEventCompat.getX(ev, index);
- final float y = MotionEventCompat.getY(ev, index);
- mLastMotionX = x;
- mLastMotionY = y;
- mActivePointerId = MotionEventCompat.getPointerId(ev, index);
- break;
- }
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- mLastMotionX = MotionEventCompat.getX(ev,
- MotionEventCompat.findPointerIndex(ev, mActivePointerId));
- mLastMotionY = MotionEventCompat.getY(ev,
- MotionEventCompat.findPointerIndex(ev, mActivePointerId));
- break;
- }
- if (needsInvalidate) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- return true;
- }
-
- private boolean performDrag(float pos) {
- boolean needsInvalidate = false;
-
- if (isOrientationHorizontal()) {
- final float deltaX = mLastMotionX - pos;
- mLastMotionX = pos;
-
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX + deltaX;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
- boolean leftAbsolute = true;
- boolean rightAbsolute = true;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftAbsolute = false;
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightAbsolute = false;
- rightBound = lastItem.offset * width;
- }
-
- if (scrollX < leftBound) {
- if (leftAbsolute) {
- float over = leftBound - scrollX;
- needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
- }
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- if (rightAbsolute) {
- float over = scrollX - rightBound;
- needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
- }
- scrollX = rightBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
- } else {
- final float deltaY = mLastMotionY - pos;
- mLastMotionY = pos;
-
- float oldScrollY = getScrollY();
- float scrollY = oldScrollY + deltaY;
- final int height = getClientHeight();
-
- float topBound = height * mFirstOffset;
- float bottomBound = height * mLastOffset;
- boolean topAbsolute = true;
- boolean bottomAbsolute = true;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- topAbsolute = false;
- topBound = firstItem.offset * height;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- bottomAbsolute = false;
- bottomBound = lastItem.offset * height;
- }
-
- if (scrollY < topBound) {
- if (topAbsolute) {
- float over = topBound - scrollY;
- needsInvalidate = mLeftEdge.onPull(Math.abs(over) / height);
- }
- scrollY = topBound;
- } else if (scrollY > bottomBound) {
- if (bottomAbsolute) {
- float over = scrollY - bottomBound;
- needsInvalidate = mRightEdge.onPull(Math.abs(over) / height);
- }
- scrollY = bottomBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollY - (int) scrollY;
- scrollTo(getScrollX(), (int) scrollY);
- pageScrolled((int) scrollY);
- }
-
- return needsInvalidate;
- }
-
- /**
- * @return Info about the page at the current scroll position.
- * This can be synthetic for a missing middle page; the 'object' field can be null.
- */
- private ItemInfo infoForCurrentScrollPosition() {
- final int scroll = isOrientationHorizontal() ? getScrollX() : getScrollY();
- final int size = isOrientationHorizontal() ? getClientWidth() : getClientHeight();
- final float scrollOffset = size > 0 ? (float) scroll / size : 0;
- final float marginOffset = size > 0 ? (float) mPageMargin / size : 0;
- int lastPos = -1;
- float lastOffset = 0.f;
- float lastSize = 0.f;
- boolean first = true;
-
- ItemInfo lastItem = null;
- for (int i = 0; i < mItems.size(); i++) {
- ItemInfo ii = mItems.get(i);
- float offset;
- if (!first && ii.position != lastPos + 1) {
- // Create a synthetic item for a missing page.
- ii = mTempItem;
- ii.offset = lastOffset + lastSize + marginOffset;
- ii.position = lastPos + 1;
- ii.sizeFactor = mAdapter.getPageSize(ii.position);
- i--;
- }
- offset = ii.offset;
-
- final float startBound = offset;
- final float endBound = offset + ii.sizeFactor + marginOffset;
- if (first || scrollOffset >= startBound) {
- if (scrollOffset < endBound || i == mItems.size() - 1) {
- return ii;
- }
- } else {
- return lastItem;
- }
- first = false;
- lastPos = ii.position;
- lastOffset = offset;
- lastSize = ii.sizeFactor;
- lastItem = ii;
- }
-
- return lastItem;
- }
-
- private int determineTargetPage(int currentPage, float pageOffset, int velocity, int delta) {
- int targetPage;
- if (Math.abs(delta) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- targetPage = velocity > 0 ? currentPage : currentPage + 1;
- } else {
- final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
- targetPage = (int) (currentPage + pageOffset + truncator);
- }
-
- if (mItems.size() > 0) {
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-
- // Only let the user target pages we have items for
- targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
- }
-
- return targetPage;
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- boolean needsInvalidate = false;
-
- final int overScrollMode = ViewCompat.getOverScrollMode(this);
- if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
- (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
- mAdapter != null && mAdapter.getCount() > 1)) {
- if (!mLeftEdge.isFinished()) {
- final int restoreCount = canvas.save();
- final int width = isOrientationHorizontal()
- ? getHeight() - getPaddingTop() - getPaddingBottom()
- : getWidth() - getPaddingLeft() - getPaddingRight();
- final int height = isOrientationHorizontal()
- ? getWidth() - getPaddingLeft() - getPaddingRight()
- : getHeight() - getPaddingTop() - getPaddingBottom();
-
- if (isOrientationHorizontal()) {
- canvas.rotate(270);
- canvas.translate(-width + getPaddingTop(), mFirstOffset * height);
- }
- mLeftEdge.setSize(width, height);
- needsInvalidate |= mLeftEdge.draw(canvas);
- canvas.restoreToCount(restoreCount);
- }
- if (!mRightEdge.isFinished()) {
- final int restoreCount = canvas.save();
- final int width = isOrientationHorizontal()
- ? getHeight() - getPaddingTop() - getPaddingBottom()
- : getWidth() - getPaddingLeft() - getPaddingRight();
- final int height = isOrientationHorizontal()
- ? getWidth() - getPaddingLeft() - getPaddingRight()
- : getHeight() - getPaddingTop() - getPaddingBottom();
-
- if (isOrientationHorizontal()) {
- canvas.rotate(90);
- canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * height);
- } else {
- canvas.rotate(180);
- canvas.translate(-width, -(mLastOffset + 1) * height);
- }
- mRightEdge.setSize(width, height);
- needsInvalidate |= mRightEdge.draw(canvas);
- canvas.restoreToCount(restoreCount);
- }
- } else {
- mLeftEdge.finish();
- mRightEdge.finish();
- }
-
- if (needsInvalidate) {
- // Keep animating
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Draw the margin drawable between pages if needed.
- if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {
- if (isOrientationHorizontal()) {
- final int scrollX = getScrollX();
- final int width = getWidth();
-
- final float marginOffset = (float) mPageMargin / width;
- int itemIndex = 0;
- ItemInfo ii = mItems.get(0);
- float offset = ii.offset;
- final int itemCount = mItems.size();
- final int firstPos = ii.position;
- final int lastPos = mItems.get(itemCount - 1).position;
- for (int pos = firstPos; pos < lastPos; pos++) {
- while (pos > ii.position && itemIndex < itemCount) {
- ii = mItems.get(++itemIndex);
- }
-
- float drawAt;
- if (pos == ii.position) {
- drawAt = (ii.offset + ii.sizeFactor) * width;
- offset = ii.offset + ii.sizeFactor + marginOffset;
- } else {
- float widthFactor = mAdapter.getPageWidth(pos);
- drawAt = (offset + widthFactor) * width;
- offset += widthFactor + marginOffset;
- }
-
- if (drawAt + mPageMargin > scrollX) {
- mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
- (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
- mMarginDrawable.draw(canvas);
- }
-
- if (drawAt > scrollX + width) {
- break; // No more visible, no sense in continuing
- }
- }
- } else {
- final int scrollY = getScrollY();
- final int height = getHeight();
-
- final float marginOffset = (float) mPageMargin / height;
- int itemIndex = 0;
- ItemInfo ii = mItems.get(0);
- float offset = ii.offset;
- final int itemCount = mItems.size();
- final int firstPos = ii.position;
- final int lastPos = mItems.get(itemCount - 1).position;
- for (int pos = firstPos; pos < lastPos; pos++) {
- while (pos > ii.position && itemIndex < itemCount) {
- ii = mItems.get(++itemIndex);
- }
-
- float drawAt;
- if (pos == ii.position) {
- drawAt = (ii.offset + ii.sizeFactor) * height;
- offset = ii.offset + ii.sizeFactor + marginOffset;
- } else {
- float sizeFactor = mAdapter.getPageSize(pos);
- drawAt = (offset + sizeFactor) * height;
- offset += sizeFactor + marginOffset;
- }
-
- if (drawAt + mPageMargin > scrollY) {
- mMarginDrawable.setBounds(mLeftPageBounds, (int) drawAt,
- mRightPageBounds, (int) (drawAt + mPageMargin + 0.5f));
- mMarginDrawable.draw(canvas);
- }
-
- if (drawAt > scrollY + height) {
- break; // No more visible, no sense in continuing
- }
- }
- }
- }
- }
-
- /**
- * Start a fake drag of the pager.
- *
- *
A fake drag can be useful if you want to synchronize the motion of the ViewPager
- * with the touch scrolling of another view, while still letting the ViewPager
- * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
- * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
- * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
- *
- *
During a fake drag the ViewPager will ignore all touch events. If a real drag
- * is already in progress, this method will return false.
- *
- * @return true if the fake drag began successfully, false if it could not be started.
- *
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean beginFakeDrag() {
- if (mIsBeingDragged) {
- return false;
- }
- mFakeDragging = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- if (isOrientationHorizontal()) {
- mInitialMotionX = mLastMotionX = 0;
- } else {
- mInitialMotionY = mLastMotionY = 0;
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- mFakeDragBeginTime = time;
- return true;
- }
-
- /**
- * End a fake drag of the pager.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- */
- public void endFakeDrag() {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
-
- int initialVelocity, totalDelta;
- float pageOffset;
- if (isOrientationHorizontal()) {
- initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
- velocityTracker, mActivePointerId);
- mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- pageOffset = (((float) scrollX / width) - ii.offset) / ii.sizeFactor;
- totalDelta = (int) (mLastMotionX - mInitialMotionX);
- } else {
- initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
- velocityTracker, mActivePointerId);
- mPopulatePending = true;
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- pageOffset = (((float) scrollY / height) - ii.offset) / ii.sizeFactor;
- totalDelta = (int) (mLastMotionY - mInitialMotionY);
- }
-
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
- endDrag();
-
- mFakeDragging = false;
- }
-
- /**
- * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
- *
- * @param offset Offset in pixels to drag by.
- * @see #beginFakeDrag()
- * @see #endFakeDrag()
- */
- public void fakeDragBy(float offset) {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- if (isOrientationHorizontal()) {
- mLastMotionX += offset;
-
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX - offset;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightBound = lastItem.offset * width;
- }
-
- if (scrollX < leftBound) {
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- scrollX = rightBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- mLastMotionX, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- } else {
- mLastMotionY += offset;
-
- float oldScrollY = getScrollY();
- float scrollY = oldScrollY - offset;
- final int height = getClientHeight();
-
- float topBound = height * mFirstOffset;
- float bottomBound = height * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- topBound = firstItem.offset * height;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- bottomBound = lastItem.offset * height;
- }
-
- if (scrollY < topBound) {
- scrollY = topBound;
- } else if (scrollY > bottomBound) {
- scrollY = bottomBound;
- }
- // Don't lose the rounded component
- mLastMotionY += scrollY - (int) scrollY;
- scrollTo(getScrollX(), (int) scrollY);
- pageScrolled((int) scrollY);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- 0, mLastMotionY, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- }
- }
-
- /**
- * Returns true if a fake drag is in progress.
- *
- * @return true if currently in a fake drag, false otherwise.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean isFakeDragging() {
- return mFakeDragging;
- }
-
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
- mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
- private void endDrag() {
- mIsBeingDragged = false;
- mIsUnableToDrag = false;
-
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private void setScrollingCacheEnabled(boolean enabled) {
- if (mScrollingCacheEnabled != enabled) {
- mScrollingCacheEnabled = enabled;
- if (USE_CACHE) {
- final int size = getChildCount();
- for (int i = 0; i < size; ++i) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- child.setDrawingCacheEnabled(enabled);
- }
- }
- }
- }
- }
-
- public boolean canScrollHorizontally(int direction) {
- if (mAdapter == null) {
- return false;
- }
-
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- if (direction < 0) {
- return (scrollX > (int) (width * mFirstOffset));
- } else if (direction > 0) {
- return (scrollX < (int) (width * mLastOffset));
- } else {
- return false;
- }
- }
-
- public boolean canScrollVertically(int direction) {
- if (mAdapter == null) {
- return false;
- }
-
- final int height = getClientHeight();
- final int scrollY = getScrollY();
- if (direction < 0) {
- return (scrollY > (int) (height * mFirstOffset));
- } else if (direction > 0) {
- return (scrollY < (int) (height * mLastOffset));
- } else {
- return false;
- }
- }
-
- /**
- * Tests scrollability within child views of v given a delta of dx.
- *
- * @param v View to test for horizontal scrollability
- * @param checkV Whether the view v passed should itself be checked for scrollability (true),
- * or just its children (false).
- * @param dx Delta scrolled in pixels
- * @param x X coordinate of the active touch point
- * @param y Y coordinate of the active touch point
- * @return true if child views of v can be scrolled by delta of dx.
- */
- protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
- if (v instanceof ViewGroup) {
- final ViewGroup group = (ViewGroup) v;
- final int scrollX = v.getScrollX();
- final int scrollY = v.getScrollY();
- final int count = group.getChildCount();
- // Count backwards - let topmost views consume scroll distance first.
- for (int i = count - 1; i >= 0; i--) {
- // TODO: Add versioned support here for transformed views.
- // This will not work for transformed views in Honeycomb+
- final View child = group.getChildAt(i);
- if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
- y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
- canScroll(child, true, dx, x + scrollX - child.getLeft(),
- y + scrollY - child.getTop())) {
- return true;
- }
- }
- }
-
- boolean canScroll = false;
- if (isOrientationHorizontal()) {
- canScroll = ViewCompat.canScrollHorizontally(v, -dx);
- } else {
- canScroll = ViewCompat.canScrollVertically(v, -dx);
- }
- return checkV && canScroll;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- // Let the focused view and/or our descendants get the key first
- return super.dispatchKeyEvent(event) || executeKeyEvent(event);
- }
-
- /**
- * You can call this function yourself to have the scroll view perform
- * scrolling from a key event, just as if the event had been dispatched to
- * it by the view hierarchy.
- *
- * @param event The key event to execute.
- * @return Return true if the event was handled, else false.
- */
- public boolean executeKeyEvent(KeyEvent event) {
- boolean handled = false;
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (isOrientationHorizontal())
- handled = arrowScroll(FOCUS_LEFT);
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (isOrientationHorizontal())
- handled = arrowScroll(FOCUS_RIGHT);
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- if (!isOrientationHorizontal())
- handled = arrowScroll(FOCUS_UP);
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (!isOrientationHorizontal())
- handled = arrowScroll(FOCUS_DOWN);
- break;
- case KeyEvent.KEYCODE_TAB:
- if (Build.VERSION.SDK_INT >= 11) {
- // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
- // before Android 3.0. Ignore the tab key on those devices.
- if (KeyEventCompat.hasNoModifiers(event)) {
- handled = arrowScroll(FOCUS_FORWARD);
- } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
- handled = arrowScroll(FOCUS_BACKWARD);
- }
- }
- break;
- }
- }
- return handled;
- }
-
- public boolean arrowScroll(int direction) {
- View currentFocused = findFocus();
- if (currentFocused == this) {
- currentFocused = null;
- } else if (currentFocused != null) {
- boolean isChild = false;
- for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
- parent = parent.getParent()) {
- if (parent == this) {
- isChild = true;
- break;
- }
- }
- if (!isChild) {
- // This would cause the focus search down below to fail in fun ways.
- final StringBuilder sb = new StringBuilder();
- sb.append(currentFocused.getClass().getSimpleName());
- for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
- parent = parent.getParent()) {
- sb.append(" => ").append(parent.getClass().getSimpleName());
- }
- Log.e(TAG, "arrowScroll tried to find focus based on non-child " +
- "current focused view " + sb.toString());
- currentFocused = null;
- }
- }
-
- boolean handled = false;
-
- View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
- direction);
- if (nextFocused != null && nextFocused != currentFocused) {
- if (direction == View.FOCUS_LEFT) {
- // If there is nothing to the left, or this is causing us to
- // jump to the right, then what we really want to do is page left.
- final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
- final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
- if (currentFocused != null && nextLeft >= currLeft) {
- handled = pageLeft();
- } else {
- handled = nextFocused.requestFocus();
- }
- } else if (direction == View.FOCUS_RIGHT) {
- // If there is nothing to the right, or this is causing us to
- // jump to the left, then what we really want to do is page right.
- final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
- final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
- if (currentFocused != null && nextLeft <= currLeft) {
- handled = pageRight();
- } else {
- handled = nextFocused.requestFocus();
- }
- } else if (direction == View.FOCUS_UP) {
- final int nextUp = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;
- final int currUp = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;
- if (currentFocused != null && nextUp >= currUp) {
- handled = pageLeft();
- } else {
- handled = nextFocused.requestFocus();
- }
- } else if (direction == View.FOCUS_DOWN) {
- final int nextUp = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;
- final int currUp = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;
- if (currentFocused != null && nextUp <= currUp) {
- handled = pageRight();
- } else {
- handled = nextFocused.requestFocus();
- }
- }
- } else if (direction == FOCUS_LEFT || direction == FOCUS_UP || direction == FOCUS_BACKWARD) {
- // Trying to move left and nothing there; try to page.
- handled = pageLeft();
- } else if (direction == FOCUS_RIGHT || direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {
- // Trying to move right and nothing there; try to page.
- handled = pageRight();
- }
- if (handled) {
- playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
- }
- return handled;
- }
-
- private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {
- if (outRect == null) {
- outRect = new Rect();
- }
- if (child == null) {
- outRect.set(0, 0, 0, 0);
- return outRect;
- }
- outRect.left = child.getLeft();
- outRect.right = child.getRight();
- outRect.top = child.getTop();
- outRect.bottom = child.getBottom();
-
- ViewParent parent = child.getParent();
- while (parent instanceof ViewGroup && parent != this) {
- final ViewGroup group = (ViewGroup) parent;
- outRect.left += group.getLeft();
- outRect.right += group.getRight();
- outRect.top += group.getTop();
- outRect.bottom += group.getBottom();
-
- parent = group.getParent();
- }
- return outRect;
- }
-
- boolean pageLeft() {
- if (mCurItem > 0) {
- setCurrentItem(mCurItem-1, true);
- return true;
- }
- return false;
- }
-
- boolean pageRight() {
- if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
- setCurrentItem(mCurItem+1, true);
- return true;
- }
- return false;
- }
-
- /**
- * We only want the current page that is being shown to be focusable.
- */
- @Override
- public void addFocusables(ArrayList views, int direction, int focusableMode) {
- final int focusableCount = views.size();
-
- final int descendantFocusability = getDescendantFocusability();
-
- if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- child.addFocusables(views, direction, focusableMode);
- }
- }
- }
- }
-
- // we add ourselves (if focusable) in all cases except for when we are
- // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
- // to avoid the focus search finding layouts when a more precise search
- // among the focusable children would be more interesting.
- if (
- descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
- // No focusable descendants
- (focusableCount == views.size())) {
- // Note that we can't call the superclass here, because it will
- // add all views in. So we need to do the same thing View does.
- if (!isFocusable()) {
- return;
- }
- if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
- isInTouchMode() && !isFocusableInTouchMode()) {
- return;
- }
- if (views != null) {
- views.add(this);
- }
- }
- }
-
- /**
- * We only want the current page that is being shown to be touchable.
- */
- @Override
- public void addTouchables(ArrayList views) {
- // Note that we don't call super.addTouchables(), which means that
- // we don't call View.addTouchables(). This is okay because a ViewPager
- // is itself not touchable.
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- child.addTouchables(views);
- }
- }
- }
- }
-
- /**
- * We only want the current page that is being shown to be focusable.
- */
- @Override
- protected boolean onRequestFocusInDescendants(int direction,
- Rect previouslyFocusedRect) {
- int index;
- int increment;
- int end;
- int count = getChildCount();
- if ((direction & FOCUS_FORWARD) != 0) {
- index = 0;
- increment = 1;
- end = count;
- } else {
- index = count - 1;
- increment = -1;
- end = -1;
- }
- for (int i = index; i != end; i += increment) {
- View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem) {
- if (child.requestFocus(direction, previouslyFocusedRect)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- // Dispatch scroll events from this ViewPager.
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- // Dispatch all other accessibility events from the current page.
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- final ItemInfo ii = infoForChild(child);
- if (ii != null && ii.position == mCurItem &&
- child.dispatchPopulateAccessibilityEvent(event)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams();
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return generateDefaultLayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams && super.checkLayoutParams(p);
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- class MyAccessibilityDelegate extends AccessibilityDelegateCompat {
-
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(host, event);
- event.setClassName(ViewPager.class.getName());
- final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();
- recordCompat.setScrollable(canScroll());
- if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED
- && mAdapter != null) {
- recordCompat.setItemCount(mAdapter.getCount());
- recordCompat.setFromIndex(mCurItem);
- recordCompat.setToIndex(mCurItem);
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setClassName(ViewPager.class.getName());
- info.setScrollable(canScroll());
- if (canScrollHorizontally(1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
- }
- if (canScrollHorizontally(-1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
- }
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (super.performAccessibilityAction(host, action, args)) {
- return true;
- }
- switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
- if (canScrollHorizontally(1)) {
- setCurrentItem(mCurItem + 1);
- return true;
- }
- } return false;
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
- if (canScrollHorizontally(-1)) {
- setCurrentItem(mCurItem - 1);
- return true;
- }
- } return false;
- }
- return false;
- }
-
- private boolean canScroll() {
- return (mAdapter != null) && (mAdapter.getCount() > 1);
- }
- }
-
- private class PagerObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- dataSetChanged();
- }
- @Override
- public void onInvalidated() {
- dataSetChanged();
- }
- }
-
- /**
- * Layout parameters that should be supplied for views added to a
- * ViewPager.
- */
- public static class LayoutParams extends ViewGroup.LayoutParams {
- /**
- * true if this view is a decoration on the pager itself and not
- * a view supplied by the adapter.
- */
- public boolean isDecor;
-
- /**
- * Gravity setting for use on decor views only:
- * Where to position the view page within the overall ViewPager
- * container; constants are defined in {@link android.view.Gravity}.
- */
- public int gravity;
-
- /**
- * Width as a 0-1 multiplier of the measured pager width
- */
- float sizeFactor = 0.f;
-
- /**
- * true if this view was added during layout and needs to be measured
- * before being positioned.
- */
- boolean needsMeasure;
-
- /**
- * Adapter position this view is for if !isDecor
- */
- int position;
-
- /**
- * Current child index within the ViewPager that this view occupies
- */
- int childIndex;
-
- public LayoutParams() {
- super(MATCH_PARENT, MATCH_PARENT);
- }
-
- public LayoutParams(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
- gravity = a.getInteger(0, Gravity.TOP);
- a.recycle();
- }
- }
-
- static class ViewPositionComparator implements Comparator {
- @Override
- public int compare(View lhs, View rhs) {
- final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();
- final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();
- if (llp.isDecor != rlp.isDecor) {
- return llp.isDecor ? 1 : -1;
- }
- return llp.position - rlp.position;
- }
- }
-}
\ No newline at end of file
diff --git a/ViewPager/src/main/res/values-v11/styles.xml b/ViewPager/src/main/res/values-v11/styles.xml
deleted file mode 100644
index 3c02242ad..000000000
--- a/ViewPager/src/main/res/values-v11/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
diff --git a/ViewPager/src/main/res/values-v14/styles.xml b/ViewPager/src/main/res/values-v14/styles.xml
deleted file mode 100644
index a91fd0372..000000000
--- a/ViewPager/src/main/res/values-v14/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
diff --git a/ViewPager/src/main/res/values/attrs.xml b/ViewPager/src/main/res/values/attrs.xml
deleted file mode 100644
index be14d4ce3..000000000
--- a/ViewPager/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ViewPager/src/main/res/values/strings.xml b/ViewPager/src/main/res/values/strings.xml
deleted file mode 100644
index cc18fb0a3..000000000
--- a/ViewPager/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- ViewPager
-
diff --git a/ViewPager/src/main/res/values/styles.xml b/ViewPager/src/main/res/values/styles.xml
deleted file mode 100644
index 6ce89c7ba..000000000
--- a/ViewPager/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/build.gradle b/build.gradle
index 91847bdaa..143e57b76 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,36 +1,38 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
-
- ext.KOTLIN_VERSION = '1.2.41'
- ext.ANDROID_LIB_VERSION = '27.1.1'
- ext.R2_STREAMER_VERSION = '0.1.5'
- def KOTLIN_VERSION = ext.KOTLIN_VERSION
+ apply from: 'versions.gradle'
repositories {
google()
jcenter()
}
+
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.3'
+ classpath "com.android.tools.build:gradle:$versions.androidGradlePlugin"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
- classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
- classpath "org.jetbrains.kotlin:kotlin-android-extensions:$KOTLIN_VERSION"
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
}
}
allprojects {
+
+ configurations.all {
+ resolutionStrategy {
+ cacheChangingModulesFor 0, 'seconds'
+ }
+ }
+
repositories {
+ mavenLocal()
google()
jcenter()
- maven {
- url "http://dl.bintray.com/mobisystech/maven"
- }
+ maven { url "https://jitpack.io" }
}
}
diff --git a/config/quality/checkstyle/checkstyle-config.xml b/config/quality/checkstyle/checkstyle-config.xml
index 262a6c3d1..5577b4f35 100755
--- a/config/quality/checkstyle/checkstyle-config.xml
+++ b/config/quality/checkstyle/checkstyle-config.xml
@@ -1,16 +1,15 @@
-
-
-
+
-
+
-
+
-
+
@@ -18,148 +17,123 @@
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+ value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)" />
+
+
-
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
diff --git a/config/quality/findbugs/android-exclude-filter.xml b/config/quality/findbugs/android-exclude-filter.xml
index b724212b1..aed4207f3 100755
--- a/config/quality/findbugs/android-exclude-filter.xml
+++ b/config/quality/findbugs/android-exclude-filter.xml
@@ -1,10 +1,10 @@
-
+
-
+
diff --git a/config/quality/pmd/pmd-ruleset.xml b/config/quality/pmd/pmd-ruleset.xml
index ad418930c..e018fb435 100755
--- a/config/quality/pmd/pmd-ruleset.xml
+++ b/config/quality/pmd/pmd-ruleset.xml
@@ -1,7 +1,6 @@
Custom ruleset for ribot Android application
diff --git a/config/quality/quality.gradle b/config/quality/quality.gradle
index 8a0689711..f7f6f45ec 100755
--- a/config/quality/quality.gradle
+++ b/config/quality/quality.gradle
@@ -31,23 +31,23 @@ task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code
reports {
xml.enabled = true
xml {
- destination "$reportsDir/checkstyle/checkstyle.xml"
+ destination = file("$reportsDir/checkstyle/checkstyle.xml")
}
}
- classpath = files( )
+ classpath = files()
}
task findbugs(type: FindBugs,
group: 'Verification',
description: 'Inspect java bytecode for bugs',
- dependsOn: ['compileDebugSources','compileReleaseSources']) {
+ dependsOn: ['compileDebugSources', 'compileReleaseSources']) {
ignoreFailures = false
effort = "max"
reportLevel = "high"
excludeFilter = new File("$qualityConfigDir/findbugs/android-exclude-filter.xml")
- classes = files("$project.rootDir/folioreader/build/intermediates/classes")
+ classes = files("$project.rootDir/folioreader/build/intermediates/javac")
source 'src'
include '**/*.java'
@@ -57,10 +57,10 @@ task findbugs(type: FindBugs,
xml.enabled = false
html.enabled = true
xml {
- destination "$reportsDir/findbugs/findbugs.xml"
+ destination = file("$reportsDir/findbugs/findbugs.xml")
}
html {
- destination "$reportsDir/findbugs/findbugs.html"
+ destination = file("$reportsDir/findbugs/findbugs.html")
}
}
@@ -81,10 +81,10 @@ task pmd(type: Pmd, group: 'Verification', description: 'Inspect sourcecode for
xml.enabled = true
html.enabled = true
xml {
- destination "$reportsDir/pmd/pmd.xml"
+ destination = file("$reportsDir/pmd/pmd.xml")
}
html {
- destination "$reportsDir/pmd/pmd.html"
+ destination = file("$reportsDir/pmd/pmd.html")
}
}
}
\ No newline at end of file
diff --git a/folioreader/AndroidManifest.xml b/folioreader/AndroidManifest.xml
index d62406cb3..96e7be6ee 100644
--- a/folioreader/AndroidManifest.xml
+++ b/folioreader/AndroidManifest.xml
@@ -1,9 +1,43 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/bintray/bintrayv1.gradle b/folioreader/bintray/bintrayv1.gradle
index fa392e442..79811e671 100644
--- a/folioreader/bintray/bintrayv1.gradle
+++ b/folioreader/bintray/bintrayv1.gradle
@@ -31,7 +31,7 @@ artifacts {
// Bintray
def propertiesFile = project.rootProject.file('local.properties')
-if( propertiesFile.exists()) {
+if (propertiesFile.exists()) {
Properties properties = new Properties()
properties.load(propertiesFile.newDataInputStream())
diff --git a/folioreader/build.gradle b/folioreader/build.gradle
index 5508f23ad..f41712cc4 100644
--- a/folioreader/build.gradle
+++ b/folioreader/build.gradle
@@ -17,7 +17,7 @@ ext {
siteUrl = 'https://github.com/FolioReader/FolioReader-Android'
gitUrl = 'https://github.com/FolioReader/FolioReader-Android.git'
- libraryVersion = '0.3.9'
+ libraryVersion = versions.folioreaderSdk
developerId = 'mobisystech'
developerName = 'Folio Reader'
@@ -30,13 +30,14 @@ ext {
android {
useLibrary 'org.apache.http.legacy'
- compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
+ compileSdkVersion versions.androidCompileSdk
defaultConfig {
- minSdkVersion Integer.parseInt(project.ANDROID_MIN_SDK)
- targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
- versionCode Integer.parseInt(project.VERSION_CODE)
- versionName project.VERSION_NAME
+ minSdkVersion versions.androidMinSdk
+ targetSdkVersion versions.androidTargetSdk
+ versionCode versions.projectVersionCode
+ versionName versions.projectVersionName
+ vectorDrawables.useSupportLibrary = true
}
sourceSets {
@@ -50,11 +51,6 @@ android {
}
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
- }
-
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/DEPENDENCIES.txt'
@@ -72,6 +68,7 @@ android {
lintOptions {
abortOnError false
+ lintConfig file("lint.xml")
}
checkstyle {
@@ -79,29 +76,47 @@ android {
}
}
-
apply from: '../folioreader/bintray/installv1.gradle'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
- implementation project(':webViewMarker')
- //noinspection GradleDependency
- implementation 'com.android.support.constraint:constraint-layout:1.1.0'
- implementation "com.android.support:appcompat-v7:$ANDROID_LIB_VERSION"
- implementation "com.android.support:recyclerview-v7:$ANDROID_LIB_VERSION"
- implementation "com.android.support:support-v4:$ANDROID_LIB_VERSION"
- implementation "com.android.support:design:$ANDROID_LIB_VERSION"
+ implementation "androidx.appcompat:appcompat:$versions.appcompat"
+ implementation "androidx.constraintlayout:constraintlayout:$versions.constraintLayout"
+ implementation "androidx.recyclerview:recyclerview:$versions.recyclerview"
+ implementation "com.google.android.material:material:$versions.material"
+ testImplementation 'junit:junit:4.12'
+ implementation 'org.slf4j:slf4j-android:1.7.25'
implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
//Kotlin
- implementation "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION"
-
- // r2-streamer
- api "org.readium:r2-fetcher:$R2_STREAMER_VERSION"
- api "org.readium:r2-parser:$R2_STREAMER_VERSION"
- api "org.readium:r2-server:$R2_STREAMER_VERSION"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
implementation 'org.greenrobot:eventbus:3.1.1'
+
+ implementation "com.fasterxml.jackson.core:jackson-core:$versions.jackson"
+ implementation "com.fasterxml.jackson.core:jackson-annotations:$versions.jackson"
+ implementation "com.fasterxml.jackson.core:jackson-databind:$versions.jackson"
+ implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$versions.jackson"
+ implementation "com.google.code.gson:gson:$versions.gson"
+
+ implementation "com.squareup.retrofit2:retrofit:$versions.retrofit"
+ implementation "com.squareup.retrofit2:converter-jackson:$versions.retrofit"
+ implementation "com.squareup.retrofit2:converter-gson:$versions.retrofit"
+
+ // R2 modules
+ api("com.github.codetoart:r2-shared-kotlin:$versions.r2SharedKotlin") {
+ changing = true
+ }
+ api("com.github.codetoart:r2-streamer-kotlin:$versions.r2StreamerKotlin") {
+ exclude group: "org.slf4j", module: "slf4j-api"
+ changing = true
+ }
+
+ // Only ReflectionUtils in Spring framework is used
+ implementation 'org.springframework:spring-core:4.3.19.RELEASE'
+
+ // Lifecycle
+ implementation "androidx.lifecycle:lifecycle-extensions:$versions.lifecycle"
}
apply from: '../folioreader/bintray/bintrayv1.gradle'
diff --git a/folioreader/libs/epublib-core-latest.jar b/folioreader/libs/epublib-core-latest.jar
deleted file mode 100644
index 6799a3033..000000000
Binary files a/folioreader/libs/epublib-core-latest.jar and /dev/null differ
diff --git a/folioreader/libs/slf4j-android-1.5.8.jar b/folioreader/libs/slf4j-android-1.5.8.jar
deleted file mode 100644
index e0128bc84..000000000
Binary files a/folioreader/libs/slf4j-android-1.5.8.jar and /dev/null differ
diff --git a/folioreader/lint.xml b/folioreader/lint.xml
new file mode 100644
index 000000000..45028b5fc
--- /dev/null
+++ b/folioreader/lint.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/anim/disappear.xml b/folioreader/res/anim/disappear.xml
index 21e6a4df3..9c0d3f23f 100644
--- a/folioreader/res/anim/disappear.xml
+++ b/folioreader/res/anim/disappear.xml
@@ -2,8 +2,8 @@
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="400" />
\ No newline at end of file
diff --git a/folioreader/res/anim/enter_from_left.xml b/folioreader/res/anim/enter_from_left.xml
index 7e0506f0d..2d00384df 100644
--- a/folioreader/res/anim/enter_from_left.xml
+++ b/folioreader/res/anim/enter_from_left.xml
@@ -2,7 +2,9 @@
+ android:fromXDelta="-100%"
+ android:toXDelta="0%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="700" />
\ No newline at end of file
diff --git a/folioreader/res/anim/enter_from_right.xml b/folioreader/res/anim/enter_from_right.xml
index a78db395e..e2a8e7c21 100644
--- a/folioreader/res/anim/enter_from_right.xml
+++ b/folioreader/res/anim/enter_from_right.xml
@@ -2,7 +2,9 @@
\ No newline at end of file
diff --git a/folioreader/res/anim/exit_to_left.xml b/folioreader/res/anim/exit_to_left.xml
index 8c076732c..0b8b5fc71 100644
--- a/folioreader/res/anim/exit_to_left.xml
+++ b/folioreader/res/anim/exit_to_left.xml
@@ -2,7 +2,9 @@
+ android:fromXDelta="0%"
+ android:toXDelta="-100%"
+ android:fromYDelta="0%"
+ android:toYDelta="0%"
+ android:duration="700" />
\ No newline at end of file
diff --git a/folioreader/res/anim/exit_to_right.xml b/folioreader/res/anim/exit_to_right.xml
index 255d47abd..b7d1893cc 100644
--- a/folioreader/res/anim/exit_to_right.xml
+++ b/folioreader/res/anim/exit_to_right.xml
@@ -2,7 +2,9 @@
\ No newline at end of file
diff --git a/folioreader/res/anim/fadein.xml b/folioreader/res/anim/fadein.xml
index 3c7c297fc..7df24c3fa 100644
--- a/folioreader/res/anim/fadein.xml
+++ b/folioreader/res/anim/fadein.xml
@@ -1,9 +1,8 @@
+ android:interpolator="@android:anim/linear_interpolator">
+ android:fromAlpha="0.1"
+ android:toAlpha="1.0"
+ android:duration="1500" />
\ No newline at end of file
diff --git a/folioreader/res/anim/fadeout.xml b/folioreader/res/anim/fadeout.xml
index 836015d7a..e0408f293 100644
--- a/folioreader/res/anim/fadeout.xml
+++ b/folioreader/res/anim/fadeout.xml
@@ -1,9 +1,8 @@
+ android:interpolator="@android:anim/linear_interpolator">
+ android:fromAlpha="1.0"
+ android:toAlpha="0.1"
+ android:duration="1500" />
\ No newline at end of file
diff --git a/folioreader/res/anim/grow_from_bottom.xml b/folioreader/res/anim/grow_from_bottom.xml
index b9edc5a0a..2547166bb 100644
--- a/folioreader/res/anim/grow_from_bottom.xml
+++ b/folioreader/res/anim/grow_from_bottom.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="100%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/grow_from_bottomleft_to_topright.xml b/folioreader/res/anim/grow_from_bottomleft_to_topright.xml
index 7cbecadc5..13768b5ee 100644
--- a/folioreader/res/anim/grow_from_bottomleft_to_topright.xml
+++ b/folioreader/res/anim/grow_from_bottomleft_to_topright.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="0%"
+ android:pivotY="50%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
\ No newline at end of file
diff --git a/folioreader/res/anim/grow_from_bottomright_to_topleft.xml b/folioreader/res/anim/grow_from_bottomright_to_topleft.xml
index 965f0904d..97f029400 100644
--- a/folioreader/res/anim/grow_from_bottomright_to_topleft.xml
+++ b/folioreader/res/anim/grow_from_bottomright_to_topleft.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="100%"
+ android:pivotY="50%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
\ No newline at end of file
diff --git a/folioreader/res/anim/grow_from_top.xml b/folioreader/res/anim/grow_from_top.xml
index e2831ffe7..c0418d81a 100644
--- a/folioreader/res/anim/grow_from_top.xml
+++ b/folioreader/res/anim/grow_from_top.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/grow_from_topleft_to_bottomright.xml b/folioreader/res/anim/grow_from_topleft_to_bottomright.xml
index a75e86c0a..b172ebe1f 100644
--- a/folioreader/res/anim/grow_from_topleft_to_bottomright.xml
+++ b/folioreader/res/anim/grow_from_topleft_to_bottomright.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="0%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/grow_from_topright_to_bottomleft.xml b/folioreader/res/anim/grow_from_topright_to_bottomleft.xml
index 141c19c3a..44a1b0998 100644
--- a/folioreader/res/anim/grow_from_topright_to_bottomleft.xml
+++ b/folioreader/res/anim/grow_from_topright_to_bottomleft.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="0.3"
+ android:toXScale="1.0"
+ android:fromYScale="0.3"
+ android:toYScale="1.0"
+ android:pivotX="100%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
\ No newline at end of file
diff --git a/folioreader/res/anim/pump_bottom.xml b/folioreader/res/anim/pump_bottom.xml
index 2c8ec4c15..e5854e855 100644
--- a/folioreader/res/anim/pump_bottom.xml
+++ b/folioreader/res/anim/pump_bottom.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.1"
+ android:toXScale="1.0"
+ android:fromYScale="1.1"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="100%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/pump_top.xml b/folioreader/res/anim/pump_top.xml
index c77e8cfaf..ab2b6121a 100644
--- a/folioreader/res/anim/pump_top.xml
+++ b/folioreader/res/anim/pump_top.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.1"
+ android:toXScale="1.0"
+ android:fromYScale="1.1"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/shrink_from_bottom.xml b/folioreader/res/anim/shrink_from_bottom.xml
index 98a15c93e..ac51e4eed 100644
--- a/folioreader/res/anim/shrink_from_bottom.xml
+++ b/folioreader/res/anim/shrink_from_bottom.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="50%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/shrink_from_bottomleft_to_topright.xml b/folioreader/res/anim/shrink_from_bottomleft_to_topright.xml
index 0551a768b..0a409b3ee 100644
--- a/folioreader/res/anim/shrink_from_bottomleft_to_topright.xml
+++ b/folioreader/res/anim/shrink_from_bottomleft_to_topright.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="100%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
\ No newline at end of file
diff --git a/folioreader/res/anim/shrink_from_bottomright_to_topleft.xml b/folioreader/res/anim/shrink_from_bottomright_to_topleft.xml
index bcbb9a92b..87558b9ce 100644
--- a/folioreader/res/anim/shrink_from_bottomright_to_topleft.xml
+++ b/folioreader/res/anim/shrink_from_bottomright_to_topleft.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="0%"
+ android:pivotY="0%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/shrink_from_top.xml b/folioreader/res/anim/shrink_from_top.xml
index 8ea8ab72a..c7bc1813b 100644
--- a/folioreader/res/anim/shrink_from_top.xml
+++ b/folioreader/res/anim/shrink_from_top.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="50%"
+ android:pivotY="100%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/shrink_from_topleft_to_bottomright.xml b/folioreader/res/anim/shrink_from_topleft_to_bottomright.xml
index 86cb79fe1..415e0c9e0 100644
--- a/folioreader/res/anim/shrink_from_topleft_to_bottomright.xml
+++ b/folioreader/res/anim/shrink_from_topleft_to_bottomright.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="100%"
+ android:pivotY="100%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
\ No newline at end of file
diff --git a/folioreader/res/anim/shrink_from_topright_to_bottomleft.xml b/folioreader/res/anim/shrink_from_topright_to_bottomleft.xml
index 11103f66e..645424c4d 100644
--- a/folioreader/res/anim/shrink_from_topright_to_bottomleft.xml
+++ b/folioreader/res/anim/shrink_from_topright_to_bottomleft.xml
@@ -1,14 +1,16 @@
+ android:fromXScale="1.0"
+ android:toXScale="0.3"
+ android:fromYScale="1.0"
+ android:toYScale="0.3"
+ android:pivotX="0%"
+ android:pivotY="100%"
+ android:duration="@android:integer/config_shortAnimTime" />
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/folioreader/res/anim/slide_down.xml b/folioreader/res/anim/slide_down.xml
index f7d612422..da6aa6e2d 100644
--- a/folioreader/res/anim/slide_down.xml
+++ b/folioreader/res/anim/slide_down.xml
@@ -1,5 +1,5 @@
-
+
-
-
-
-
-
-
diff --git a/folioreader/res/drawable-hdpi/icon_font.png b/folioreader/res/drawable-hdpi/icon_font.png
deleted file mode 100644
index 475b72869..000000000
Binary files a/folioreader/res/drawable-hdpi/icon_font.png and /dev/null differ
diff --git a/folioreader/res/drawable-mdpi/icon_font.png b/folioreader/res/drawable-mdpi/icon_font.png
deleted file mode 100644
index 0c812cd9d..000000000
Binary files a/folioreader/res/drawable-mdpi/icon_font.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/colors_marker.png b/folioreader/res/drawable-xhdpi/colors_marker.png
deleted file mode 100644
index ecf58d4aa..000000000
Binary files a/folioreader/res/drawable-xhdpi/colors_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_action_discard.png b/folioreader/res/drawable-xhdpi/ic_action_discard.png
deleted file mode 100644
index 9eeeed124..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_action_discard.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_action_share.png b/folioreader/res/drawable-xhdpi/ic_action_share.png
deleted file mode 100644
index 40771e480..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_action_share.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_blue_marker.png b/folioreader/res/drawable-xhdpi/ic_blue_marker.png
deleted file mode 100644
index 0a9e0af56..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_blue_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_drawer.png b/folioreader/res/drawable-xhdpi/ic_drawer.png
deleted file mode 100644
index 2da331e71..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_drawer.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_green_marker.png b/folioreader/res/drawable-xhdpi/ic_green_marker.png
deleted file mode 100644
index 2f47b528d..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_green_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_pink_marker.png b/folioreader/res/drawable-xhdpi/ic_pink_marker.png
deleted file mode 100644
index 483460499..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_pink_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_underline_marker.png b/folioreader/res/drawable-xhdpi/ic_underline_marker.png
deleted file mode 100644
index 601a06fbf..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_underline_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/ic_yellow_marker.png b/folioreader/res/drawable-xhdpi/ic_yellow_marker.png
deleted file mode 100644
index de18c7162..000000000
Binary files a/folioreader/res/drawable-xhdpi/ic_yellow_marker.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/icon_close.png b/folioreader/res/drawable-xhdpi/icon_close.png
deleted file mode 100644
index 365f53727..000000000
Binary files a/folioreader/res/drawable-xhdpi/icon_close.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/icon_font.png b/folioreader/res/drawable-xhdpi/icon_font.png
deleted file mode 100644
index af945f909..000000000
Binary files a/folioreader/res/drawable-xhdpi/icon_font.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/man_speech_icon.png b/folioreader/res/drawable-xhdpi/man_speech_icon.png
deleted file mode 100644
index c6f6d9a3e..000000000
Binary files a/folioreader/res/drawable-xhdpi/man_speech_icon.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/next_icon.png b/folioreader/res/drawable-xhdpi/next_icon.png
deleted file mode 100644
index 43c6985a2..000000000
Binary files a/folioreader/res/drawable-xhdpi/next_icon.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/pause_btn.png b/folioreader/res/drawable-xhdpi/pause_btn.png
deleted file mode 100644
index 952043cbe..000000000
Binary files a/folioreader/res/drawable-xhdpi/pause_btn.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/play_icon.png b/folioreader/res/drawable-xhdpi/play_icon.png
deleted file mode 100644
index bc3409a00..000000000
Binary files a/folioreader/res/drawable-xhdpi/play_icon.png and /dev/null differ
diff --git a/folioreader/res/drawable-xhdpi/prev_con.png b/folioreader/res/drawable-xhdpi/prev_con.png
deleted file mode 100644
index 97bcc9949..000000000
Binary files a/folioreader/res/drawable-xhdpi/prev_con.png and /dev/null differ
diff --git a/folioreader/res/drawable-xxhdpi/icon_font.png b/folioreader/res/drawable-xxhdpi/icon_font.png
deleted file mode 100644
index 22a41067c..000000000
Binary files a/folioreader/res/drawable-xxhdpi/icon_font.png and /dev/null differ
diff --git a/folioreader/res/drawable-xxxhdpi/icon_font.png b/folioreader/res/drawable-xxxhdpi/icon_font.png
deleted file mode 100644
index d34fd9b92..000000000
Binary files a/folioreader/res/drawable-xxxhdpi/icon_font.png and /dev/null differ
diff --git a/folioreader/res/drawable/blue_border_background.xml b/folioreader/res/drawable/blue_border_background.xml
index ed411a74c..6fc8480c6 100644
--- a/folioreader/res/drawable/blue_border_background.xml
+++ b/folioreader/res/drawable/blue_border_background.xml
@@ -4,5 +4,5 @@
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/btn_contents_highlights.xml b/folioreader/res/drawable/btn_contents_highlights.xml
index 0107251fa..d54daa44c 100644
--- a/folioreader/res/drawable/btn_contents_highlights.xml
+++ b/folioreader/res/drawable/btn_contents_highlights.xml
@@ -1,8 +1,9 @@
-
-
+
-
+ android:shape="rectangle">
+
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/btn_moon_selector.xml b/folioreader/res/drawable/btn_moon_selector.xml
index 883d4e512..23b83c05e 100644
--- a/folioreader/res/drawable/btn_moon_selector.xml
+++ b/folioreader/res/drawable/btn_moon_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/btn_sun_selector.xml b/folioreader/res/drawable/btn_sun_selector.xml
index 4e863bd38..dd4e31900 100644
--- a/folioreader/res/drawable/btn_sun_selector.xml
+++ b/folioreader/res/drawable/btn_sun_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/content_highlight_back_selector_night_mode.xml b/folioreader/res/drawable/content_highlight_back_selector_night_mode.xml
index 2b3fc9461..64dc07803 100644
--- a/folioreader/res/drawable/content_highlight_back_selector_night_mode.xml
+++ b/folioreader/res/drawable/content_highlight_back_selector_night_mode.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/content_highlight_text_selector.xml b/folioreader/res/drawable/content_highlight_text_selector.xml
index c453f88f5..ad9decd48 100644
--- a/folioreader/res/drawable/content_highlight_text_selector.xml
+++ b/folioreader/res/drawable/content_highlight_text_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/dottet_line.xml b/folioreader/res/drawable/dottet_line.xml
index da836b12a..2debf098a 100644
--- a/folioreader/res/drawable/dottet_line.xml
+++ b/folioreader/res/drawable/dottet_line.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/folioreader/res/drawable/font_text_selector.xml b/folioreader/res/drawable/font_text_selector.xml
index 0762eb28c..0394fbb8f 100644
--- a/folioreader/res/drawable/font_text_selector.xml
+++ b/folioreader/res/drawable/font_text_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/green_border_background.xml b/folioreader/res/drawable/green_border_background.xml
index 0abbd7131..6c9e4d7c0 100644
--- a/folioreader/res/drawable/green_border_background.xml
+++ b/folioreader/res/drawable/green_border_background.xml
@@ -4,5 +4,5 @@
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/ic_close_green_24dp.xml b/folioreader/res/drawable/ic_close_green_24dp.xml
index e6e81abbe..62dfc837c 100644
--- a/folioreader/res/drawable/ic_close_green_24dp.xml
+++ b/folioreader/res/drawable/ic_close_green_24dp.xml
@@ -2,8 +2,8 @@
+ android:viewportWidth="24"
+ android:viewportHeight="24">
diff --git a/folioreader/res/drawable/ic_drawer.xml b/folioreader/res/drawable/ic_drawer.xml
new file mode 100644
index 000000000..d51676b75
--- /dev/null
+++ b/folioreader/res/drawable/ic_drawer.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_drawer_green_24dp.xml b/folioreader/res/drawable/ic_drawer_green_24dp.xml
deleted file mode 100644
index a0b9d3694..000000000
--- a/folioreader/res/drawable/ic_drawer_green_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/folioreader/res/drawable/ic_fast_forward.xml b/folioreader/res/drawable/ic_fast_forward.xml
new file mode 100644
index 000000000..c66e5f529
--- /dev/null
+++ b/folioreader/res/drawable/ic_fast_forward.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_fast_rewind.xml b/folioreader/res/drawable/ic_fast_rewind.xml
new file mode 100644
index 000000000..234730cf3
--- /dev/null
+++ b/folioreader/res/drawable/ic_fast_rewind.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_format_underlined.xml b/folioreader/res/drawable/ic_format_underlined.xml
new file mode 100644
index 000000000..946b35543
--- /dev/null
+++ b/folioreader/res/drawable/ic_format_underlined.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/folioreader/res/drawable/ic_highlight_delete.xml b/folioreader/res/drawable/ic_highlight_delete.xml
new file mode 100644
index 000000000..2b27d142b
--- /dev/null
+++ b/folioreader/res/drawable/ic_highlight_delete.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/folioreader/res/drawable/ic_offline_gray_layer_list.xml b/folioreader/res/drawable/ic_offline_gray_layer_list.xml
new file mode 100644
index 000000000..2b2f4125d
--- /dev/null
+++ b/folioreader/res/drawable/ic_offline_gray_layer_list.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/ic_pause.xml b/folioreader/res/drawable/ic_pause.xml
new file mode 100644
index 000000000..98c36068a
--- /dev/null
+++ b/folioreader/res/drawable/ic_pause.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_play.xml b/folioreader/res/drawable/ic_play.xml
new file mode 100644
index 000000000..e86ee425e
--- /dev/null
+++ b/folioreader/res/drawable/ic_play.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_plus_black_24dp.xml b/folioreader/res/drawable/ic_plus_black_24dp.xml
index ce4e94c4e..61814f1ca 100644
--- a/folioreader/res/drawable/ic_plus_black_24dp.xml
+++ b/folioreader/res/drawable/ic_plus_black_24dp.xml
@@ -4,5 +4,7 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/ic_search.xml b/folioreader/res/drawable/ic_search.xml
new file mode 100644
index 000000000..2774870ad
--- /dev/null
+++ b/folioreader/res/drawable/ic_search.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/folioreader/res/drawable/ic_volume_gray_24dp.xml b/folioreader/res/drawable/ic_volume_gray_24dp.xml
index 68969151e..a5ff217ae 100644
--- a/folioreader/res/drawable/ic_volume_gray_24dp.xml
+++ b/folioreader/res/drawable/ic_volume_gray_24dp.xml
@@ -4,5 +4,7 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/icon_font.xml b/folioreader/res/drawable/icon_font.xml
new file mode 100644
index 000000000..f25b3bf10
--- /dev/null
+++ b/folioreader/res/drawable/icon_font.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/man_speech_icon.xml b/folioreader/res/drawable/man_speech_icon.xml
new file mode 100644
index 000000000..9c71ce2b0
--- /dev/null
+++ b/folioreader/res/drawable/man_speech_icon.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/folioreader/res/drawable/note_edittext_background.xml b/folioreader/res/drawable/note_edittext_background.xml
index f344669ca..7dd4d8ee0 100644
--- a/folioreader/res/drawable/note_edittext_background.xml
+++ b/folioreader/res/drawable/note_edittext_background.xml
@@ -1,8 +1,9 @@
-
-
+
-
-
-
+ android:shape="rectangle">
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/round_button.xml b/folioreader/res/drawable/round_button.xml
index 55204468b..5ce392580 100644
--- a/folioreader/res/drawable/round_button.xml
+++ b/folioreader/res/drawable/round_button.xml
@@ -1,7 +1,8 @@
+ android:shape="rectangle"
+ android:padding="10dp">
-
-
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/search_thumb_vertical.xml b/folioreader/res/drawable/search_thumb_vertical.xml
new file mode 100644
index 000000000..2e1c422b6
--- /dev/null
+++ b/folioreader/res/drawable/search_thumb_vertical.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/shape_highlight_blue.xml b/folioreader/res/drawable/shape_highlight_blue.xml
new file mode 100644
index 000000000..4ff69b693
--- /dev/null
+++ b/folioreader/res/drawable/shape_highlight_blue.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/shape_highlight_green.xml b/folioreader/res/drawable/shape_highlight_green.xml
new file mode 100644
index 000000000..26c6fe633
--- /dev/null
+++ b/folioreader/res/drawable/shape_highlight_green.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/shape_highlight_pink.xml b/folioreader/res/drawable/shape_highlight_pink.xml
new file mode 100644
index 000000000..bbd5f6f07
--- /dev/null
+++ b/folioreader/res/drawable/shape_highlight_pink.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/shape_highlight_yellow.xml b/folioreader/res/drawable/shape_highlight_yellow.xml
new file mode 100644
index 000000000..8219d4f8e
--- /dev/null
+++ b/folioreader/res/drawable/shape_highlight_yellow.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/style_back_color_selector.xml b/folioreader/res/drawable/style_back_color_selector.xml
index 0a911ab19..89605eac4 100644
--- a/folioreader/res/drawable/style_back_color_selector.xml
+++ b/folioreader/res/drawable/style_back_color_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
diff --git a/folioreader/res/drawable/style_text_color_selector.xml b/folioreader/res/drawable/style_text_color_selector.xml
index d04361384..744b3a4e7 100644
--- a/folioreader/res/drawable/style_text_color_selector.xml
+++ b/folioreader/res/drawable/style_text_color_selector.xml
@@ -1,7 +1,7 @@
-
+
-
-
+
+
diff --git a/folioreader/res/drawable/thumb.xml b/folioreader/res/drawable/thumb.xml
index 17f8bff7c..520f85711 100644
--- a/folioreader/res/drawable/thumb.xml
+++ b/folioreader/res/drawable/thumb.xml
@@ -1,12 +1,13 @@
+ android:shape="oval">
-
+
-
+
-
+
\ No newline at end of file
diff --git a/folioreader/res/drawable/transparent_selector.xml b/folioreader/res/drawable/transparent_selector.xml
index 8591e2bf0..a3a75f9f4 100644
--- a/folioreader/res/drawable/transparent_selector.xml
+++ b/folioreader/res/drawable/transparent_selector.xml
@@ -1,7 +1,7 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/action_item_horizontal.xml b/folioreader/res/layout/action_item_horizontal.xml
index c6e721128..0591ca351 100644
--- a/folioreader/res/layout/action_item_horizontal.xml
+++ b/folioreader/res/layout/action_item_horizontal.xml
@@ -1,28 +1,27 @@
-
+
+ android:id="@+id/iv_icon"
+ android:layout_centerHorizontal="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ android:id="@+id/tv_title"
+ android:layout_below="@+id/iv_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip"
+ android:text="Chart"
+ android:textColor="#fff" />
\ No newline at end of file
diff --git a/folioreader/res/layout/action_item_vertical.xml b/folioreader/res/layout/action_item_vertical.xml
index b16fa2124..103b29f17 100644
--- a/folioreader/res/layout/action_item_vertical.xml
+++ b/folioreader/res/layout/action_item_vertical.xml
@@ -1,25 +1,24 @@
-
+
+ android:id="@+id/iv_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ android:id="@+id/tv_title"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical"
+ android:paddingLeft="5dip"
+ android:paddingRight="10dip"
+ android:text="Chart"
+ android:textColor="#fff" />
\ No newline at end of file
diff --git a/folioreader/res/layout/activity_content_highlight.xml b/folioreader/res/layout/activity_content_highlight.xml
index a77565702..fb6535cf0 100644
--- a/folioreader/res/layout/activity_content_highlight.xml
+++ b/folioreader/res/layout/activity_content_highlight.xml
@@ -4,9 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context="com.folioreader.ui.folio.activity.ContentHighlightActivity">
+ tools:context="com.folioreader.ui.activity.ContentHighlightActivity">
-
+ app:srcCompat="@drawable/ic_close_green_24dp" />
-
-
+
+ android:layout_below="@id/toolbar" />
diff --git a/folioreader/res/layout/activity_search.xml b/folioreader/res/layout/activity_search.xml
new file mode 100644
index 000000000..8579a3602
--- /dev/null
+++ b/folioreader/res/layout/activity_search.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/dialog_edit_notes.xml b/folioreader/res/layout/dialog_edit_notes.xml
index 89ed00460..4b2954084 100644
--- a/folioreader/res/layout/dialog_edit_notes.xml
+++ b/folioreader/res/layout/dialog_edit_notes.xml
@@ -1,16 +1,17 @@
+
+
+
+
diff --git a/folioreader/res/layout/folio_activity.xml b/folioreader/res/layout/folio_activity.xml
index be99caae4..b4a01b5ee 100644
--- a/folioreader/res/layout/folio_activity.xml
+++ b/folioreader/res/layout/folio_activity.xml
@@ -1,25 +1,39 @@
-
+ tools:context="com.folioreader.ui.activity.FolioActivity"
+ tools:menu="@menu/menu_main">
-
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:direction="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
+
-
+
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/folio_page_fragment.xml b/folioreader/res/layout/folio_page_fragment.xml
index 4c227be6e..50c879c24 100644
--- a/folioreader/res/layout/folio_page_fragment.xml
+++ b/folioreader/res/layout/folio_page_fragment.xml
@@ -1,6 +1,8 @@
@@ -11,20 +13,26 @@
android:layout_height="match_parent"
android:layout_above="@+id/indicatorLayout">
-
-
-
+
+
+ android:visibility="gone">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/folioreader/res/layout/fragment_contents.xml b/folioreader/res/layout/fragment_contents.xml
index 2760d1614..f8c412e8e 100644
--- a/folioreader/res/layout/fragment_contents.xml
+++ b/folioreader/res/layout/fragment_contents.xml
@@ -2,9 +2,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context="com.folioreader.ui.tableofcontents.view.TableOfContentFragment">
+ tools:context="com.folioreader.ui.fragment.TableOfContentFragment">
-
-
diff --git a/folioreader/res/layout/horiz_separator.xml b/folioreader/res/layout/horiz_separator.xml
index 59a85b115..8aa80cb1b 100644
--- a/folioreader/res/layout/horiz_separator.xml
+++ b/folioreader/res/layout/horiz_separator.xml
@@ -1,12 +1,13 @@
-
-
+
+
+
diff --git a/folioreader/res/layout/item_dictionary.xml b/folioreader/res/layout/item_dictionary.xml
index 59e5925ac..bcd3a2925 100644
--- a/folioreader/res/layout/item_dictionary.xml
+++ b/folioreader/res/layout/item_dictionary.xml
@@ -1,7 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/item_search_failure.xml b/folioreader/res/layout/item_search_failure.xml
new file mode 100644
index 000000000..9ecc27410
--- /dev/null
+++ b/folioreader/res/layout/item_search_failure.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/item_search_init.xml b/folioreader/res/layout/item_search_init.xml
new file mode 100644
index 000000000..f586e583a
--- /dev/null
+++ b/folioreader/res/layout/item_search_init.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/item_search_loading.xml b/folioreader/res/layout/item_search_loading.xml
new file mode 100644
index 000000000..c2537508c
--- /dev/null
+++ b/folioreader/res/layout/item_search_loading.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/item_search_normal.xml b/folioreader/res/layout/item_search_normal.xml
new file mode 100644
index 000000000..aaaec6699
--- /dev/null
+++ b/folioreader/res/layout/item_search_normal.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/item_search_pagination_in_progress.xml b/folioreader/res/layout/item_search_pagination_in_progress.xml
new file mode 100644
index 000000000..9e1ba14da
--- /dev/null
+++ b/folioreader/res/layout/item_search_pagination_in_progress.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/layout_dictionary.xml b/folioreader/res/layout/layout_dictionary.xml
index 9189a87d1..6929fd78f 100644
--- a/folioreader/res/layout/layout_dictionary.xml
+++ b/folioreader/res/layout/layout_dictionary.xml
@@ -1,10 +1,11 @@
-
+ android:layout_height="match_parent">
+ app:srcCompat="@drawable/ic_close_green_24dp" />
-
+
-
+ android:visibility="gone"
+ tools:visibility="visible" />
+ android:visibility="gone"
+ tools:visibility="visible" />
\ No newline at end of file
diff --git a/folioreader/res/layout/popup_horizontal.xml b/folioreader/res/layout/popup_horizontal.xml
index c3d1095fb..f381be1f0 100644
--- a/folioreader/res/layout/popup_horizontal.xml
+++ b/folioreader/res/layout/popup_horizontal.xml
@@ -1,41 +1,39 @@
-
+
+ android:id="@+id/scroller"
+ android:layout_marginTop="16dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/popup"
+ android:fadingEdgeLength="5dip"
+ android:scrollbars="none">
+ android:id="@+id/tracks"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:padding="10dip" />
+ android:id="@+id/arrow_up"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/arrow_up" />
+ android:id="@+id/arrow_down"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/scroller"
+ android:layout_marginTop="-4dip"
+ android:src="@drawable/arrow_down" />
\ No newline at end of file
diff --git a/folioreader/res/layout/popup_vertical.xml b/folioreader/res/layout/popup_vertical.xml
index 2a97b50ca..18cdd5a3b 100644
--- a/folioreader/res/layout/popup_vertical.xml
+++ b/folioreader/res/layout/popup_vertical.xml
@@ -1,41 +1,39 @@
-
+
+
+ android:background="@drawable/popup"
+ android:fadingEdgeLength="5dip"
+ android:scrollbars="none">
-
-
-
+ android:layout_weight="1"
+ android:padding="10dip" />
+ android:id="@+id/arrow_up"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/arrow_up" />
+ android:id="@+id/arrow_down"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/scroller"
+ android:layout_marginTop="-4dip"
+ android:src="@drawable/arrow_down" />
\ No newline at end of file
diff --git a/folioreader/res/layout/progress_dialog.xml b/folioreader/res/layout/progress_dialog.xml
index 56235981d..0d823fe0e 100644
--- a/folioreader/res/layout/progress_dialog.xml
+++ b/folioreader/res/layout/progress_dialog.xml
@@ -2,21 +2,27 @@
-
-
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/row_font.xml b/folioreader/res/layout/row_font.xml
index 8f7783c2e..bcefd5065 100644
--- a/folioreader/res/layout/row_font.xml
+++ b/folioreader/res/layout/row_font.xml
@@ -1,8 +1,8 @@
\ No newline at end of file
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textSize="24sp"
+ android:gravity="center_vertical"
+ android:padding="16dp" />
\ No newline at end of file
diff --git a/folioreader/res/layout/row_highlight.xml b/folioreader/res/layout/row_highlight.xml
index 88755847e..027592e89 100644
--- a/folioreader/res/layout/row_highlight.xml
+++ b/folioreader/res/layout/row_highlight.xml
@@ -43,7 +43,7 @@
android:textSize="14sp"
android:textStyle="bold" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/view_audio_player.xml b/folioreader/res/layout/view_audio_player.xml
index 39a44caf1..db4fc9738 100644
--- a/folioreader/res/layout/view_audio_player.xml
+++ b/folioreader/res/layout/view_audio_player.xml
@@ -1,175 +1,168 @@
+ android:layout_height="172dp"
+ android:layout_gravity="bottom"
+ android:background="@color/white">
-
+ android:layout_height="50dp"
+ android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.2" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+ android:layerType="software"
+ android:text="@string/style_underline"
+ android:textSize="17sp"
+ app:folio_font="@string/lora_font" />
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/view_config.xml b/folioreader/res/layout/view_config.xml
index 2a008dbb3..341574794 100644
--- a/folioreader/res/layout/view_config.xml
+++ b/folioreader/res/layout/view_config.xml
@@ -1,11 +1,11 @@
-
+ tools:layout_gravity="bottom">
@@ -21,10 +20,8 @@
android:id="@+id/view_config_ib_night_mode"
android:layout_width="0dp"
android:layout_height="50dp"
- android:adjustViewBounds="true"
android:background="@android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/view_config_ib_day_mode"
app:srcCompat="@drawable/icon_moon_sel" />
@@ -33,12 +30,11 @@
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/grey_color"
- android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_config_ib_night_mode" />
-
-
-
-
@@ -96,9 +86,7 @@
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/grey_color"
- android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.483"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_config_font_lato" />
@@ -106,90 +94,74 @@
android:id="@+id/view_config_iv_label_font_small"
android:layout_width="36dp"
android:layout_height="36dp"
- android:layout_marginBottom="8dp"
- android:layout_marginEnd="8dp"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
+ android:layout_margin="8dp"
android:adjustViewBounds="false"
android:scaleType="center"
android:tint="@color/grey_color"
+ app:layout_constraintBottom_toTopOf="@+id/view5"
app:layout_constraintEnd_toStartOf="@+id/view_config_font_size_seek_bar"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/view_config_font_andada"
+ app:layout_constraintTop_toBottomOf="@+id/view4"
app:srcCompat="@drawable/ic_format_font_black_24dp" />
+ app:layout_constraintTop_toBottomOf="@+id/view4" />
+ app:layout_constraintTop_toBottomOf="@+id/view_config_iv_label_font_small" />
+ app:layout_constraintTop_toBottomOf="@+id/view5" />
+ app:layout_constraintStart_toEndOf="@+id/buttonVertical"
+ app:layout_constraintTop_toBottomOf="@+id/view5" />
-
+
diff --git a/folioreader/res/layout/view_loading.xml b/folioreader/res/layout/view_loading.xml
new file mode 100644
index 000000000..2db55cd04
--- /dev/null
+++ b/folioreader/res/layout/view_loading.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/layout/view_webview_pager.xml b/folioreader/res/layout/view_webview_pager.xml
index e94ed64b1..c6b233746 100644
--- a/folioreader/res/layout/view_webview_pager.xml
+++ b/folioreader/res/layout/view_webview_pager.xml
@@ -1,5 +1,5 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/folioreader/res/menu/context_menu.xml b/folioreader/res/menu/context_menu.xml
deleted file mode 100644
index 67da498f0..000000000
--- a/folioreader/res/menu/context_menu.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
\ No newline at end of file
diff --git a/folioreader/res/menu/menu_main.xml b/folioreader/res/menu/menu_main.xml
new file mode 100644
index 000000000..442709c10
--- /dev/null
+++ b/folioreader/res/menu/menu_main.xml
@@ -0,0 +1,29 @@
+
+
\ No newline at end of file
diff --git a/folioreader/res/menu/menu_on_highlight.xml b/folioreader/res/menu/menu_on_highlight.xml
deleted file mode 100644
index 3e8d36402..000000000
--- a/folioreader/res/menu/menu_on_highlight.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/folioreader/res/menu/menu_search.xml b/folioreader/res/menu/menu_search.xml
new file mode 100644
index 000000000..b4704b569
--- /dev/null
+++ b/folioreader/res/menu/menu_search.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/folioreader/res/menu/menu_text_selection.xml b/folioreader/res/menu/menu_text_selection.xml
deleted file mode 100644
index aff062895..000000000
--- a/folioreader/res/menu/menu_text_selection.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
\ No newline at end of file
diff --git a/folioreader/res/values-cs/strings.xml b/folioreader/res/values-cs/strings.xml
new file mode 100644
index 000000000..fbb619868
--- /dev/null
+++ b/folioreader/res/values-cs/strings.xml
@@ -0,0 +1,73 @@
+
+
+ %1$d minut ·
+ %1$d minuta ·
+ Méně než minuta ·
+
+ Zkopírované
+ Odeslat
+
+ Kopírovat
+ Sdílet
+ Definice
+ Zvýraznit
+
+ %1$d stran zůstává
+ %1$d strana zůstává
+ Zvýrazněné
+ 1/2x
+ 1x
+ 1 1/2x
+ 2x
+ Styl
+ Styl]]>
+ <tr><td>Názvy:</td>
+ <tr><td>Autoři:</td>
+ <tr><td>Přispěvatelé:</td>
+ <tr><td>Jazyky:</td><td>
+ <tr><td>Vydavatelé:</td>
+ <tr><td>Druhy:</td>
+ <tr><td>Popisy:</td>
+ <tr><td>Práva:</td>
+ <tr><td>Obsah:</td>
+ Změnit písmo
+ Barva písma:
+ Změna stylu
+ Není možné změnit styl!
+ OK
+ Zrušit
+ Velikost horního panelu:
+ Vyberte dva jazyky:
+ Probíhá načítání
+ prosím čekejte...
+ čekejte prosím na analyzování zvuku, o chvíli zkuste znovu
+ Uložte si poznámku
+ Uložit poznámku
+ zadejte poznámku
+ Obsah
+
+ Slovník
+ Wikipedie
+ Nelze otevřít elektronickou knihu, je třeba povolit práva na zápis do externí paměti!
+ Horizontální
+ Vertikální
+
+ Kopírovat
+ Zvýraznit
+ Definice
+ Sdílet
+
+ Vyhledávání
+ Nastavení
+ Převod textu na řeč
+
+ Vyhledávání v knize
+ Žádné výsledky.
+ Něco se pokazilo.
+
+
+ %d výskyt
+ %d výskytů
+
+
+
diff --git a/folioreader/res/values-pt-rBR/strings.xml b/folioreader/res/values-pt-rBR/strings.xml
index 47524d529..10e1a1324 100644
--- a/folioreader/res/values-pt-rBR/strings.xml
+++ b/folioreader/res/values-pt-rBR/strings.xml
@@ -1,6 +1,6 @@
- folioreader
+ FolioReaderCompartilharCopiarEnviar
diff --git a/folioreader/res/values-sk/strings.xml b/folioreader/res/values-sk/strings.xml
new file mode 100644
index 000000000..4eab0851c
--- /dev/null
+++ b/folioreader/res/values-sk/strings.xml
@@ -0,0 +1,73 @@
+
+
+ %1$d minút ·
+ %1$d minúta ·
+ Menej ako minúta ·
+
+ Skopírované
+ Poslať
+
+ Kopírovať
+ Zdieľať
+ Definícia
+ Zvýrazniť
+
+ %1$d strán zostáva
+ %1$d strana zostáva
+ Zvýraznené
+ 1/2x
+ 1x
+ 1 1/2x
+ 2x
+ Štýl
+ Štýl]]>
+ <tr><td>Názvy:</td>
+ <tr><td>Autori:</td>
+ <tr><td>Prispievatelia:</td>
+ <tr><td>Jazyky:</td><td>
+ <tr><td>Vydavatelia:</td>
+ <tr><td>Druhy:</td>
+ <tr><td>Popisy:</td>
+ <tr><td>Práva:</td>
+ <tr><td>Obsah:</td>
+ Zmeniť písmo
+ Farba písma:
+ Zmena štýlu
+ Nie je možné zmeniť štýl!
+ OK
+ Zrušiť
+ Veľkosť horného panelu:
+ Vyberte dva jazyky:
+ Načítava
+ prosím čakajte...
+ čakajte prosím na analyzovanie zvuku, o chvíľu skúste znovu
+ Uložte si poznámku
+ Uložiť poznámku
+ zadajte poznámku
+ Obsah
+
+ Slovník
+ Wikipédia
+ Nie je možné otvoriť elektronickú knihu, je potrebné povoliť práva na zápis do externej pamäte!
+ Horizontálne
+ Vertikálne
+
+ Kopírovať
+ Zvýrazniť
+ Definícia
+ Zdieľať
+
+ Vyhľadávanie
+ Nastavenia
+ Prevod textu na reč
+
+ Vyhľadávanie v knihe
+ Žiadne výsledky.
+ Niečo sa pokazilo.
+
+
+ %d výskyt
+ %d výskytov
+
+
+
diff --git a/folioreader/res/values/attrs.xml b/folioreader/res/values/attrs.xml
index 8d331c883..2816b9bbd 100644
--- a/folioreader/res/values/attrs.xml
+++ b/folioreader/res/values/attrs.xml
@@ -1,13 +1,32 @@
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/values/colors.xml b/folioreader/res/values/colors.xml
index 60533744e..c2bd97086 100644
--- a/folioreader/res/values/colors.xml
+++ b/folioreader/res/values/colors.xml
@@ -1,20 +1,32 @@
+
+ #008577
+ #00574B
+ #D81B60
+
+ #71C951#80000000#F1EFF2#000000#322D32#090609
- #71C951#bcbcbc
- #FFEB6B
- #C0ED72
- #ADD8FF
- #FFB0CA
+
#B6B6B6#ff0000
- #767676
+ #767676
+ #848484#FFFFFF#F02814#a8a8a8
+ #131313
+
+ #AAAAAA
+
+ #FFEB6B
+ #C0ED72
+ #ADD8FF
+ #FFB0CA
+
\ No newline at end of file
diff --git a/folioreader/res/values/day_theme.xml b/folioreader/res/values/day_theme.xml
new file mode 100644
index 000000000..c5587e3cc
--- /dev/null
+++ b/folioreader/res/values/day_theme.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ #FFFAFAFA
+ @color/white
+ #000000
+ #333333
+ #333333
+ #44000000
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/values/night_theme.xml b/folioreader/res/values/night_theme.xml
new file mode 100644
index 000000000..97303a1c9
--- /dev/null
+++ b/folioreader/res/values/night_theme.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+ @color/webview_night
+ #070707
+ #767676
+ #888888
+ #888888
+ #44FFFFFF
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/values/strings.xml b/folioreader/res/values/strings.xml
index 0ae3da5f8..9d944130e 100644
--- a/folioreader/res/values/strings.xml
+++ b/folioreader/res/values/strings.xml
@@ -1,5 +1,7 @@
- folioreader
+
+ FolioReader
+
AndadaLatoLora
@@ -66,19 +68,17 @@
please wait till audio is been parsed, Retry after some timejavascript:alert(audioMarkID(\'epub-media-overlay-playing\',\'%s\'))javascript:setMediaOverlayStyle(\'%s\')
- javascript:gotoHighlight(\'%s\')
- javascript:getHighlightString(\'%s\')
+ javascript:goToAnchor(\'%s\')
+ javascript:goToHighlight(\'%s\')javascript:setHighlightStyle(\'%s\');
- ]]>
+ ]]>]]>%s]]>
%s
]]>
- \\{\\{(-?\\d+\\.?\\d*)\\,(-?\\d+\\.?\\d*)\\}\\,\\s\\{(-?\\d+\\.?\\d*)\\,(-?\\d+\\.?\\d*)\\}\\}
-
%s]]>
-
- %s
-
+
+ %s
+ Save your noteSave noteplease enter a note
@@ -91,6 +91,29 @@
DictionaryWikipediaCannot open epub it needs storage access !
- horizontal
- vertical
+ Horizontal
+ Vertical
+
+ application/xhtml+xml
+ text/html
+
+ Search
+ Configuration
+ Text To Speech
+
+ Search In Book
+ No results.
+ Something wen\'t wrong.
+
+
+ %d result
+ %d results
+
+
+
+ javascript:computeLastReadCfi()
+ javascript:scrollToCfi(\"%s\")
+ javascript:highlightSearchLocator(\"%s\")
+ javascript:clearSelection()
+
diff --git a/folioreader/res/values/styles.xml b/folioreader/res/values/styles.xml
index f8920c36e..e14bcb51a 100644
--- a/folioreader/res/values/styles.xml
+++ b/folioreader/res/values/styles.xml
@@ -1,11 +1,21 @@
-
-
-
+
+
@@ -54,11 +64,6 @@
@anim/disappear
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/folioreader/res/xml/network_security_config.xml b/folioreader/res/xml/network_security_config.xml
index 85285232d..95b646fb7 100644
--- a/folioreader/res/xml/network_security_config.xml
+++ b/folioreader/res/xml/network_security_config.xml
@@ -2,5 +2,6 @@
127.0.0.1
+ localhost
\ No newline at end of file
diff --git a/folioreader/res/xml/searchable.xml b/folioreader/res/xml/searchable.xml
new file mode 100644
index 000000000..350765806
--- /dev/null
+++ b/folioreader/res/xml/searchable.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/folioreader/src/main/assets/css/Style.css b/folioreader/src/main/assets/css/Style.css
index 4751e311e..a1e2c9513 100755
--- a/folioreader/src/main/assets/css/Style.css
+++ b/folioreader/src/main/assets/css/Style.css
@@ -33,10 +33,12 @@ footer, header, hgroup, menu, nav, section {
/* ePUB */
html {
-webkit-text-size-adjust: none; /* Never autoresize text */
+ margin: 0 !important;
padding: 0 0 !important;
}
body {
+ margin: 0 !important;
padding: 20px 20px !important;
overflow: !important;
}
@@ -85,7 +87,7 @@ b, strong, th {font-weight: bolder !important;}
/* Forced font overrides */
code, kbd, pre, samp, tt {
font-family: monospace, monospace !important;
- font-size: 1em;
+ font-size: 1rem;
}
button, input, select, textarea { display: inline-block !important; }
/*h1, h2, h3, h4, h5, h6 { font-weight: 400!important; }*/
@@ -98,8 +100,8 @@ hr {
/* Sub and Super */
-big { font-size: 1.15em !important; }
-small, sub, sup { font-size: .65em !important; }
+big { font-size: 1.15rem !important; }
+small, sub, sup { font-size: .65rem !important; }
sub { vertical-align: sub !important; }
sup {
font-family: monospace !important;
@@ -194,7 +196,6 @@ html .highlight_yellow, html .highlight_green, html .highlight_blue, html .highl
*/
html {
- -webkit-transition: all 0.6s ease;
background-color: #FFFFFF !important;
}
@@ -268,27 +269,27 @@ html.textSizeFour { font-size: 19px !important; }
html.textSizeFive { font-size: 21px !important; }
h1 {
- font-size: 2em;
+ font-size: 2rem;
line-height: 1.2;
}
h2 {
- font-size: 1.5em;
+ font-size: 1.5rem;
line-height: 1.2;
}
h3 {
- font-size: 1.17em;
+ font-size: 1.17rem;
line-height: 1.2;
}
h4 {
- font-size: 1em;
+ font-size: 1rem;
line-height: 1.2;
}
h5 {
- font-size: 0.83em;
+ font-size: 0.83rem;
line-height: 1.2;
}
h6 {
- font-size: 0.67em;
+ font-size: 0.67rem;
line-height: 1.2;
}
body {
@@ -297,12 +298,11 @@ body {
hyphens: auto !important;
}
p, span, div {
- font-size: 1em;
line-height: 1.5 !important;
}
@media only screen and (min-device-width: 600px) {
div {
- font-size: 1em;
+ font-size: 1rem;
line-height: 1.438em !important;
}
body {
diff --git a/folioreader/src/main/assets/js/Bridge.js b/folioreader/src/main/assets/js/Bridge.js
index 0da621a7d..8e91df482 100755
--- a/folioreader/src/main/assets/js/Bridge.js
+++ b/folioreader/src/main/assets/js/Bridge.js
@@ -1,6 +1,6 @@
//
// Bridge.js
-// FolioReaderKit
+// FolioReader-Android
//
// Created by Heberti Almeida on 06/05/15.
// Copyright (c) 2015 Folio Reader. All rights reserved.
@@ -10,76 +10,41 @@ var thisHighlight;
var audioMarkClass;
var wordsPerMinute = 180;
-document.addEventListener("DOMContentLoaded", function(event) {
-// var lnk = document.getElementsByClassName("lnk");
-// for (var i=0; i document.body.scrollTop && el.offsetTop < (window.innerHeight + document.body.scrollTop))
- return el
- }
-
- // @NOTE: is `span` too limiting?
- var els = document.querySelectorAll("span[id]")
-
- for(indx in els) {
- var element = els[indx];
-
- // Horizontal scroll
- if (document.body.scrollTop == 0) {
- var elLeft = document.body.clientWidth * Math.floor(element.offsetTop / window.innerHeight);
- // document.body.scrollLeft = elLeft;
-
- if (elLeft == document.body.scrollLeft) {
- return element;
- }
-
- // Vertical
- } else if(element.offsetTop > document.body.scrollTop) {
- return element;
- }
- }
-
- return null
-}
-
-
-/**
- Play Audio - called by native UIMenuController when a user selects a bit of text and presses "play"
- */
-function playAudio() {
- var sel = getSelection();
- var node = null;
-
- // user selected text? start playing from the selected node
- if (sel.toString() != "") {
- node = sel.anchorNode ? findElementWithID(sel.anchorNode.parentNode) : null;
-
- // find the first ID'd element that is within view (it will
- } else {
- node = findElementWithIDInView()
- }
-
- playAudioFragmentID(node ? node.id : null)
-}
-
-
-/**
- Play Audio Fragment ID - tells page controller to begin playing audio from the following ID
- */
-function playAudioFragmentID(fragmentID) {
- var URLBase = "play-audio://";
- window.location = URLBase + (fragmentID?encodeURIComponent(fragmentID):"")
-}
-
-function bodyOrHtml() {
- if ('scrollingElement' in document) {
- return document.scrollingElement;
- }
- // Fallback for legacy browsers
- if (navigator.userAgent.indexOf('WebKit') != -1) {
- return document.body;
- }
- return document.documentElement;
-}
-
-/**
- Go To Element - scrolls the webview to the requested element
- */
-function goToElement(element) {
-
- var scrollingElement = bodyOrHtml();
- var top = scrollingElement.scrollTop;
- var elementTop = element.offsetTop - 20;
- var bottom = window.innerHeight + top;
- var elementBottom = element.offsetHeight + element.offsetTop + 60;
-
- //console.log(window);
- //console.log("-> top = " + top);
- //console.log("-> elementTop = " + elementTop);
- //console.log("-> bottom = " + bottom);
- //console.log("-> elementBottom = " + elementBottom);
-
- if (FolioPageFragment.getDirection() == "VERTICAL" &&
- (elementBottom > bottom || elementTop < top)) {
-
- var newScrollTop = elementTop;
- //console.log("-> newScrollTop = " + newScrollTop);
- scrollingElement.scrollTop = newScrollTop;
-
- } else if (FolioPageFragment.getDirection() == "HORIZONTAL" && top == 0) {
-
- var windowVisibleWidth = document.documentElement.clientWidth;
- var pageIndex = Math.floor(element.offsetLeft / windowVisibleWidth);
- var newScrollLeft = windowVisibleWidth * pageIndex;
- //console.log("-> newScrollLeft = " + newScrollLeft);
- scrollingElement.scrollLeft = newScrollLeft;
- WebViewPager.setCurrentPage(pageIndex);
- }
-
- return element;
-}
-
/**
Remove All Classes - removes the given class from all elements in the DOM
*/
function removeAllClasses(className) {
var els = document.body.getElementsByClassName(className)
- if( els.length > 0 )
- for( i = 0; i <= els.length; i++) {
- els[i].classList.remove(className);
- }
-}
-
-//For testing purpose only
-function sleep(seconds)
-{
- var e = new Date().getTime() + (seconds * 1000);
- while (new Date().getTime() <= e) {}
-}
-
-function initHorizontalDirection() {
- preInitHorizontalDirection();
- var pageCount = postInitHorizontalDirection();
- FolioPageFragment.setHorizontalPageCount(pageCount);
-}
-
-function preInitHorizontalDirection() {
-
- //console.log(window);
- //console.log("-> " + document.getElementsByTagName('title')[0].innerText);
- var htmlElement = document.getElementsByTagName('html')[0];
- var bodyElement = document.getElementsByTagName('body')[0];
-
- htmlElement.style.width = null;
- bodyElement.style.width = null;
- htmlElement.style.height = null;
- bodyElement.style.height = null;
-
- var bodyStyle = bodyElement.currentStyle || window.getComputedStyle(bodyElement);
- var paddingTop = parseInt(bodyStyle.paddingTop, 10);
- var paddingRight = parseInt(bodyStyle.paddingRight, 10);
- var paddingBottom = parseInt(bodyStyle.paddingBottom, 10);
- var paddingLeft = parseInt(bodyStyle.paddingLeft, 10);
- //console.log("-> padding = " + paddingTop + ", " + paddingRight + ", " + paddingBottom + ", " + paddingLeft);
-
- //document.documentElement.clientWidth is window.innerWidth excluding x scrollbar width
- var pageWidth = document.documentElement.clientWidth - (paddingLeft + paddingRight);
- //document.documentElement.clientHeight is window.innerHeight excluding y scrollbar height
- var pageHeight = document.documentElement.clientHeight - (paddingTop + paddingBottom);
-
- bodyElement.style.webkitColumnGap = (paddingLeft + paddingRight) + 'px';
- bodyElement.style.webkitColumnWidth = pageWidth + 'px';
-
- //console.log("-> window.innerWidth = " + window.innerWidth);
- //console.log("-> window.innerHeight = " + window.innerHeight);
- //console.log("-> bodyElement.offsetWidth = " + bodyElement.offsetWidth);
- //console.log("-> bodyElement.offsetHeight = " + bodyElement.offsetHeight);
- //console.log("-> pageWidth = " + pageWidth);
- //console.log("-> pageHeight = " + pageHeight);
-
- htmlElement.style.height = (pageHeight + (paddingTop + paddingBottom)) + 'px';
- bodyElement.style.height = pageHeight + 'px';
-}
-
-function postInitHorizontalDirection() {
-
- var htmlElement = document.getElementsByTagName('html')[0];
- var bodyElement = document.getElementsByTagName('body')[0];
- var bodyStyle = bodyElement.currentStyle || window.getComputedStyle(bodyElement);
- var paddingTop = parseInt(bodyStyle.paddingTop, 10);
- var paddingRight = parseInt(bodyStyle.paddingRight, 10);
- var paddingBottom = parseInt(bodyStyle.paddingBottom, 10);
- var paddingLeft = parseInt(bodyStyle.paddingLeft, 10);
- var windowVisibleWidth = document.documentElement.clientWidth;
-
- var scrollWidth = document.documentElement.scrollWidth + paddingRight;
- var newBodyWidth = scrollWidth - (paddingLeft + paddingRight);
-
- htmlElement.style.width = scrollWidth + 'px';
- bodyElement.style.width = newBodyWidth + 'px';
-
- var pageCount = Math.round(scrollWidth / windowVisibleWidth);
- //console.log("-> scrollWidth = " + scrollWidth);
- //console.log("-> windowVisibleWidth = " + windowVisibleWidth);
- //console.log("-> newBodyWidth = " + newBodyWidth);
- //console.log("-> pageCount = " + pageCount);
-
- return pageCount;
+ if (els.length > 0)
+ for (i = 0; i <= els.length; i++) {
+ els[i].classList.remove(className);
+ }
}
/**
@@ -379,17 +113,17 @@ function audioMarkID(className, id) {
audioMarkClass = className
var el = document.getElementById(id);
- goToElement(el);
+ scrollToNodeOrRange(el);
el.classList.add(className)
}
-function setMediaOverlayStyle(style){
+function setMediaOverlayStyle(style) {
document.documentElement.classList.remove("mediaOverlayStyle0", "mediaOverlayStyle1", "mediaOverlayStyle2")
document.documentElement.classList.add(style)
}
function setMediaOverlayStyleColors(color, colorHighlight) {
- var stylesheet = document.styleSheets[document.styleSheets.length-1];
+ var stylesheet = document.styleSheets[document.styleSheets.length - 1];
// stylesheet.insertRule(".mediaOverlayStyle0 span.epub-media-overlay-playing { background: "+colorHighlight+" !important }")
// stylesheet.insertRule(".mediaOverlayStyle1 span.epub-media-overlay-playing { border-color: "+color+" !important }")
// stylesheet.insertRule(".mediaOverlayStyle2 span.epub-media-overlay-playing { color: "+color+" !important }")
@@ -400,7 +134,7 @@ var currentIndex = -1;
function findSentenceWithIDInView(els) {
// @NOTE: is `span` too limiting?
- for(indx in els) {
+ for (indx in els) {
var element = els[indx];
// Horizontal scroll
@@ -413,8 +147,8 @@ function findSentenceWithIDInView(els) {
return element;
}
- // Vertical
- } else if(element.offsetTop > document.body.scrollTop) {
+ // Vertical
+ } else if (element.offsetTop > document.body.scrollTop) {
currentIndex = indx;
return element;
}
@@ -424,8 +158,8 @@ function findSentenceWithIDInView(els) {
}
function findNextSentenceInArray(els) {
- if(currentIndex >= 0) {
- currentIndex ++;
+ if (currentIndex >= 0) {
+ currentIndex++;
return els[currentIndex];
}
@@ -437,7 +171,7 @@ function resetCurrentSentenceIndex() {
}
function rewindCurrentIndex() {
- currentIndex = currentIndex-1;
+ currentIndex = currentIndex - 1;
}
function getSentenceWithIndex(className) {
@@ -452,9 +186,9 @@ function getSentenceWithIndex(className) {
node = sel.anchorNode.parentNode;
if (node.className == "sentence") {
- sentence = node
+ sentence = node;
- for(var i = 0, len = elements.length; i < len; i++) {
+ for (var i = 0, len = elements.length; i < len; i++) {
if (elements[i] === sentence) {
currentIndex = i;
break;
@@ -471,503 +205,736 @@ function getSentenceWithIndex(className) {
var text = sentence.innerText || sentence.textContent;
- goToElement(sentence);
+ scrollToNodeOrRange(sentence);
- if (audioMarkClass){
+ if (audioMarkClass) {
removeAllClasses(audioMarkClass);
}
audioMarkClass = className;
- sentence.classList.add(className)
+ sentence.classList.add(className);
return text;
}
-function wrappingSentencesWithinPTags(){
- currentIndex = -1;
- "use strict";
-
- var rxOpen = new RegExp("<[^\\/].+?>"),
- rxClose = new RegExp("<\\/.+?>"),
- rxSupStart = new RegExp("^]*>"),
- rxSupEnd = new RegExp("<\/sup>"),
- sentenceEnd = [],
- rxIndex;
-
- sentenceEnd.push(new RegExp("[^\\d][\\.!\\?]+"));
- sentenceEnd.push(new RegExp("(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*?$)"));
- sentenceEnd.push(new RegExp("(?![^\\(]*?\\))"));
- sentenceEnd.push(new RegExp("(?![^\\[]*?\\])"));
- sentenceEnd.push(new RegExp("(?![^\\{]*?\\})"));
- sentenceEnd.push(new RegExp("(?![^\\|]*?\\|)"));
- sentenceEnd.push(new RegExp("(?![^\\\\]*?\\\\)"));
- //sentenceEnd.push(new RegExp("(?![^\\/.]*\\/)")); // all could be a problem, but this one is problematic
-
- rxIndex = new RegExp(sentenceEnd.reduce(function (previousValue, currentValue) {
- return previousValue + currentValue.source;
- }, ""));
-
- function indexSentenceEnd(html) {
- var index = html.search(rxIndex);
-
- if (index !== -1) {
- index += html.match(rxIndex)[0].length - 1;
- }
+$(function () {
+ window.ssReader = Class({
+ $singleton: true,
- return index;
- }
+ init: function () {
+ rangy.init();
- function pushSpan(array, className, string, classNameOpt) {
- if (!string.match('[a-zA-Z0-9]+')) {
- array.push(string);
- } else {
- array.push('' + string + '');
- }
- }
+ this.highlighter = rangy.createHighlighter();
- function addSupToPrevious(html, array) {
- var sup = html.search(rxSupStart),
- end = 0,
- last;
-
- if (sup !== -1) {
- end = html.search(rxSupEnd);
- if (end !== -1) {
- last = array.pop();
- end = end + 6;
- array.push(last.slice(0, -7) + html.slice(0, end) + last.slice(-7));
- }
- }
+ this.highlighter.addClassApplier(rangy.createClassApplier("highlight_yellow", {
+ ignoreWhiteSpace: true,
+ tagNames: ["span", "a"]
+ }));
- return html.slice(end);
- }
+ this.highlighter.addClassApplier(rangy.createClassApplier("highlight_green", {
+ ignoreWhiteSpace: true,
+ tagNames: ["span", "a"]
+ }));
- function paragraphIsSentence(html, array) {
- var index = indexSentenceEnd(html);
+ this.highlighter.addClassApplier(rangy.createClassApplier("highlight_blue", {
+ ignoreWhiteSpace: true,
+ tagNames: ["span", "a"]
+ }));
- if (index === -1 || index === html.length) {
- pushSpan(array, "sentence", html, "paragraphIsSentence");
- html = "";
- }
+ this.highlighter.addClassApplier(rangy.createClassApplier("highlight_pink", {
+ ignoreWhiteSpace: true,
+ tagNames: ["span", "a"]
+ }));
- return html;
- }
+ this.highlighter.addClassApplier(rangy.createClassApplier("highlight_underline", {
+ ignoreWhiteSpace: true,
+ tagNames: ["span", "a"]
+ }));
- function paragraphNoMarkup(html, array) {
- var open = html.search(rxOpen),
- index = 0;
+ },
- if (open === -1) {
- index = indexSentenceEnd(html);
- if (index === -1) {
- index = html.length;
- }
+ setFontAndada: function () {
+ this.setFont("andada");
+ },
- pushSpan(array, "sentence", html.slice(0, index += 1), "paragraphNoMarkup");
- }
+ setFontLato: function () {
+ this.setFont("lato");
+ },
- return html.slice(index);
- }
+ setFontPtSerif: function () {
+ this.setFont("pt-serif");
+ },
+
+ setFontPtSans: function () {
+ this.setFont("pt-sans");
+ },
+
+ base64encode: function (str) {
+ return btoa(unescape(encodeURIComponent(str)));
+ },
- function sentenceUncontained(html, array) {
- var open = html.search(rxOpen),
- index = 0,
- close;
+ base64decode: function (str) {
+ return decodeURIComponent(escape(atob(str)));
+ },
- if (open !== -1) {
- index = indexSentenceEnd(html);
- if (index === -1) {
- index = html.length;
+ clearSelection: function () {
+ if (window.getSelection) {
+ if (window.getSelection().empty) { // Chrome
+ window.getSelection().empty();
+ } else if (window.getSelection().removeAllRanges) { // Firefox
+ window.getSelection().removeAllRanges();
+ }
+ } else if (document.selection) { // IE?
+ document.selection.empty();
}
+ },
+
+ // Public methods
+
+ setFont: function (fontName) {
+ $("#ss-wrapper-font").removeClass().addClass("ss-wrapper-" + fontName);
+ },
- close = html.search(rxClose);
- if (index < open || index > close) {
- pushSpan(array, "sentence", html.slice(0, index += 1), "sentenceUncontained");
- } else {
- index = 0;
+ setSize: function (size) {
+ $("#ss-wrapper-size").removeClass().addClass("ss-wrapper-" + size);
+ },
+
+ setTheme: function (theme) {
+ $("body, #ss-wrapper-theme").removeClass().addClass("ss-wrapper-" + theme);
+ },
+
+ setComment: function (comment, inputId) {
+ $("#" + inputId).val(ssReader.base64decode(comment));
+ $("#" + inputId).trigger("input", ["true"]);
+ },
+
+ highlightSelection: function (color) {
+ try {
+
+ this.highlighter.highlightSelection(color, null);
+ var range = window.getSelection().toString();
+ var params = {content: range, rangy: this.getHighlights(), color: color};
+ this.clearSelection();
+ Highlight.onReceiveHighlights(JSON.stringify(params));
+ } catch (err) {
+ console.log("highlightSelection : " + err);
}
- }
+ },
- return html.slice(index);
- }
+ unHighlightSelection: function () {
+ try {
+ this.highlighter.unhighlightSelection();
+ Highlight.onReceiveHighlights(this.getHighlights());
+ } catch (err) {
+ }
+ },
- function sentenceContained(html, array) {
- var open = html.search(rxOpen),
- index = 0,
- close,
- count;
+ getHighlights: function () {
+ try {
+ return this.highlighter.serialize();
+ } catch (err) {
+ }
+ },
- if (open !== -1) {
- index = indexSentenceEnd(html);
- if (index === -1) {
- index = html.length;
+ setHighlights: function (serializedHighlight) {
+ try {
+ this.highlighter.removeAllHighlights();
+ this.highlighter.deserialize(serializedHighlight);
+ } catch (err) {
}
+ },
- close = html.search(rxClose);
- if (index > open && index < close) {
- count = html.match(rxClose)[0].length;
- pushSpan(array, "sentence", html.slice(0, close + count), "sentenceContained");
- index = close + count;
- } else {
- index = 0;
+ removeAll: function () {
+ try {
+ this.highlighter.removeAllHighlights();
+ } catch (err) {
}
+ },
+
+ copy: function () {
+ SSBridge.onCopy(window.getSelection().toString());
+ this.clearSelection();
+ },
+
+ share: function () {
+ SSBridge.onShare(window.getSelection().toString());
+ this.clearSelection();
+ },
+
+ search: function () {
+ SSBridge.onSearch(window.getSelection().toString());
+ this.clearSelection();
}
+ });
- return html.slice(index);
+ if (typeof ssReader !== "undefined") {
+ ssReader.init();
}
- function anythingElse(html, array) {
- pushSpan(array, "sentence", html, "anythingElse");
+ $(".verse").click(function () {
+ SSBridge.onVerseClick(ssReader.base64encode($(this).attr("verse")));
+ });
- return "";
- }
+ $("code").each(function (i) {
+ var textarea = $("").attr("id", "input-" + i).on("input propertychange", function (event, isInit) {
+ $(this).css({'height': 'auto', 'overflow-y': 'hidden'}).height(this.scrollHeight);
+ $(this).next().css({'height': 'auto', 'overflow-y': 'hidden'}).height(this.scrollHeight);
- function guessSenetences() {
- var paragraphs = document.getElementsByTagName("p");
-
- Array.prototype.forEach.call(paragraphs, function (paragraph) {
- var html = paragraph.innerHTML,
- length = html.length,
- array = [],
- safety = 100;
-
- while (length && safety) {
- html = addSupToPrevious(html, array);
- if (html.length === length) {
- if (html.length === length) {
- html = paragraphIsSentence(html, array);
- if (html.length === length) {
- html = paragraphNoMarkup(html, array);
- if (html.length === length) {
- html = sentenceUncontained(html, array);
- if (html.length === length) {
- html = sentenceContained(html, array);
- if (html.length === length) {
- html = anythingElse(html, array);
- }
- }
- }
- }
- }
+ if (!isInit) {
+ var that = this;
+ if (timeout !== null) {
+ clearTimeout(timeout);
}
-
- length = html.length;
- safety -= 1;
+ timeout = setTimeout(function () {
+ SSBridge.onCommentsClick(
+ ssReader.base64encode($(that).val()),
+ $(that).attr("id")
+ );
+ }, 1000);
}
-
- paragraph.innerHTML = array.join("");
});
- }
+ var border = $("");
+ var container = $("");
- guessSenetences();
-}
+ $(textarea).appendTo(container);
+ $(border).appendTo(container);
-function isElementVisible(element, isHorizontal) {
+ $(this).after(container);
+ });
+});
- var rect = element.getBoundingClientRect();
+function array_diff(array1, array2) {
+ var difference = $.grep(array1, function (el) {
+ return $.inArray(el, array2) < 0
+ });
+ return difference.concat($.grep(array2, function (el) {
+ return $.inArray(el, array1) < 0
+ }));
+ ;
+}
- if(isHorizontal)
- return rect.left > 0;
- else
- return rect.top > 0;
+//For testing purpose only
+function sleep(seconds) {
+ var e = new Date().getTime() + (seconds * 1000);
+ while (new Date().getTime() <= e) {
+ }
}
-/**
-Gets the first visible span from the displayed chapter and if it has id then usingId is true with
-value as span id else usingId is false with value as span index. usingId and value is forwarded to
-FolioPageFragment#storeFirstVisibleSpan(boolean, String) JavascriptInterface.
+// Mock objects for testing purpose
+/*var FolioPageFragment = {
-@param {boolean} isHorizontal - scrolling type of DirectionalViewpager#mDirection
-*/
-function getFirstVisibleSpan(isHorizontal) {
+ setHorizontalPageCount : function(pageCount) {
+ console.warn("-> Mock call to FolioPageFragment.setHorizontalPageCount(" + pageCount + ")");
+ },
+
+ storeFirstVisibleSpan : function(usingId, value) {
+ console.warn("-> Mock call to FolioPageFragment.storeFirstVisibleSpan(" + usingId + ", " + value + ")");
+ },
- var spanCollection = document.getElementsByTagName("span");
+ getDirection : function() {
+ //var direction = Direction.VERTICAL;
+ var direction = Direction.HORIZONTAL;
+ console.warn("-> Mock call to FolioPageFragment.getDirection(), return " + direction);
+ return direction;
+ },
+
+ getTopDistraction : function() {
+ console.warn("-> Mock call to FolioPageFragment.getTopDistraction(), return " + 0);
+ return 0;
+ },
- if (spanCollection.length == 0) {
- FolioPageFragment.storeFirstVisibleSpan(false, 0);
- return;
+ getBottomDistraction : function() {
+ console.warn("-> Mock call to FolioPageFragment.getBottomDistraction(), return " + 0);
+ return 0;
}
+};
- var spanIndex = 0;
- var spanElement;
+var FolioWebView = {
- for (var i = 0 ; i < spanCollection.length ; i++) {
- if (isElementVisible(spanCollection[i], isHorizontal)) {
- spanIndex = i;
- spanElement = spanCollection[i];
- break;
- }
+ setCompatMode : function(compatMode) {
+ console.warn("-> Mock call to FolioWebView.setCompatMode(" + compatMode + ")");
}
+};
- var usingId = spanElement.id ? true : false;
- var value = usingId ? spanElement.id : spanIndex;
- FolioPageFragment.storeFirstVisibleSpan(usingId, value);
-}
+var WebViewPager = {
-/**
-Scrolls the web page to particular span using id or index
+ setCurrentPage : function(pageIndex) {
+ console.warn("-> Mock call to WebViewPager.setCurrentPage(" + pageIndex + ")");
+ },
+
+ setPageToLast : function() {
+ console.warn("-> Mock call to WebViewPager.setPageToLast()");
+ },
-@param {boolean} usingId - if span tag has id then true or else false
-@param {number} value - if usingId true then span id else span index
-*/
-function scrollToSpan(usingId, value) {
+ setPageToFirst : function() {
+ console.warn("-> Mock call to WebViewPager.setPageToFirst()");
+ }
+};
- if (usingId) {
- var spanElement = document.getElementById(value);
- if (spanElement)
- goToElement(spanElement);
- } else {
- var spanCollection = document.getElementsByTagName("span");
- if (spanCollection.length == 0 || value < 0 || value >= spanCollection.length
- || value == null)
- return;
- goToElement(spanCollection[value]);
+var LoadingView = {
+
+ show : function() {
+ console.warn("-> Mock call to LoadingView.show()");
+ },
+
+ hide : function() {
+ console.warn("-> Mock call to LoadingView.hide()");
+ },
+
+ visible : function() {
+ console.warn("-> Mock call to LoadingView.visible()");
+ },
+
+ invisible : function() {
+ console.warn("-> Mock call to LoadingView.invisible()");
}
+};*/
+
+function goToHighlight(highlightId) {
+ var element = document.getElementById(highlightId.toString());
+ if (element)
+ scrollToNodeOrRange(element);
+
+ LoadingView.hide();
}
-// Class based onClick listener
+function goToAnchor(anchorId) {
+ var element = document.getElementById(anchorId);
+ if (element)
+ scrollToNodeOrRange(element);
-function addClassBasedOnClickListener(schemeName, querySelector, attributeName, selectAll) {
- if (selectAll) {
- // Get all elements with the given query selector
- var elements = document.querySelectorAll(querySelector);
- for (elementIndex = 0; elementIndex < elements.length; elementIndex++) {
- var element = elements[elementIndex];
- addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
- }
- } else {
- // Get the first element with the given query selector
- var element = document.querySelector(querySelector);
- addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
- }
+ LoadingView.hide();
}
-function addClassBasedOnClickListenerToElement(element, schemeName, attributeName) {
- // Get the content from the given attribute name
- var attributeContent = element.getAttribute(attributeName);
- // Add the on click logic
- element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(attributeContent) + "\");");
+function scrollToLast() {
+ console.log("-> scrollToLast");
+
+ var direction = FolioWebView.getDirection();
+ var scrollingElement = bodyOrHtml();
+
+ switch (direction) {
+ case Direction.VERTICAL:
+ scrollingElement.scrollTop =
+ scrollingElement.scrollHeight - document.documentElement.clientHeight;
+ break;
+ case Direction.HORIZONTAL:
+ scrollingElement.scrollLeft =
+ scrollingElement.scrollWidth - document.documentElement.clientWidth;
+ WebViewPager.setPageToLast();
+ break;
+ }
+
+ LoadingView.hide();
}
-var onClassBasedListenerClick = function(schemeName, attributeContent) {
- // Prevent the browser from performing the default on click behavior
- event.preventDefault();
- // Don't pass the click event to other elemtents
- event.stopPropagation();
- // Create parameters containing the click position inside the web view.
- var positionParameterString = "/clientX=" + event.clientX + "&clientY=" + event.clientY;
- // Set the custom link URL to the event
- window.location = schemeName + "://" + attributeContent + positionParameterString;
+function scrollToFirst() {
+ console.log("-> scrollToFirst");
+
+ var direction = FolioWebView.getDirection();
+ var scrollingElement = bodyOrHtml();
+
+ switch (direction) {
+ case Direction.VERTICAL:
+ scrollingElement.scrollTop = 0;
+ break;
+ case Direction.HORIZONTAL:
+ scrollingElement.scrollLeft = 0;
+ WebViewPager.setPageToFirst();
+ break;
+ }
+
+ LoadingView.hide();
+}
+
+function checkCompatMode() {
+ if (document.compatMode === "BackCompat") {
+ console.error("-> Web page loaded in Quirks mode. Please report to developer " +
+ "for debugging with current EPUB file, as many features might stop working " +
+ "(ex. Horizontal scroll feature).")
+ }
}
-function getHighlightString(style) {
- var range = window.getSelection().getRangeAt(0);
- var selectionContents = range.extractContents();
- var elm = document.createElement("highlight");
- var id = guid();
+function horizontalRecheck() {
- elm.appendChild(selectionContents);
- elm.setAttribute("id", id);
- elm.setAttribute("onclick","callHighlightURL(this);");
- elm.setAttribute("class", style);
+ horizontalIntervalCounter += horizontalIntervalPeriod;
- range.insertNode(elm);
- thisHighlight = elm;
+ if (window.scrollWidth != document.documentElement.scrollWidth) {
+ // Rare condition
+ // This might happen when document.documentElement.scrollWidth gives incorrect value
+ // when the webview is busy re-drawing contents.
+ //console.log("-> horizontalIntervalCounter = " + horizontalIntervalCounter);
+ console.warn("-> scrollWidth changed from " + window.scrollWidth + " to " +
+ document.documentElement.scrollWidth);
+ postInitHorizontalDirection();
+ }
- var params = [];
- params.push({id: id, rect: getRectForSelectedText(elm)});
- Highlight.getHighlightJson(JSON.stringify(params));
+ if (horizontalIntervalCounter >= horizontalIntervalLimit)
+ clearInterval(horizontalInterval);
}
-function gotoHighlight(highlightId){
- var element = document.getElementById(highlightId.toString());
- if(element != null) {
- goToElement(element);
- }
+function initHorizontalDirection() {
+
+ preInitHorizontalDirection();
+ postInitHorizontalDirection();
+
+ horizontalInterval = setInterval(horizontalRecheck, horizontalIntervalPeriod);
}
-$(function(){
- window.ssReader = Class({
- $singleton: true,
+function preInitHorizontalDirection() {
- init: function() {
- rangy.init();
+ //console.log(window);
+ //console.log("-> " + document.getElementsByTagName('title')[0].innerText);
+ var htmlElement = document.getElementsByTagName('html')[0];
+ var bodyElement = document.getElementsByTagName('body')[0];
- this.highlighter = rangy.createHighlighter();
+ // Required when initHorizontalDirection() is called multiple times.
+ // Currently it is called only once per page.
+ htmlElement.style.width = null;
+ bodyElement.style.width = null;
+ htmlElement.style.height = null;
+ bodyElement.style.height = null;
- this.highlighter.addClassApplier(rangy.createClassApplier("highlight_yellow", {
- ignoreWhiteSpace: true,
- tagNames: ["span", "a"]
- }));
+ var bodyStyle = bodyElement.currentStyle || window.getComputedStyle(bodyElement);
+ var paddingTop = parseInt(bodyStyle.paddingTop, 10);
+ var paddingRight = parseInt(bodyStyle.paddingRight, 10);
+ var paddingBottom = parseInt(bodyStyle.paddingBottom, 10);
+ var paddingLeft = parseInt(bodyStyle.paddingLeft, 10);
+ //console.log("-> padding = " + paddingTop + ", " + paddingRight + ", " + paddingBottom + ", " + paddingLeft);
- this.highlighter.addClassApplier(rangy.createClassApplier("highlight_green", {
- ignoreWhiteSpace: true,
- tagNames: ["span", "a"]
- }));
+ //document.documentElement.clientWidth is window.innerWidth excluding x scrollbar width
+ var pageWidth = document.documentElement.clientWidth - (paddingLeft + paddingRight);
+ //document.documentElement.clientHeight is window.innerHeight excluding y scrollbar height
+ var pageHeight = document.documentElement.clientHeight - (paddingTop + paddingBottom);
- this.highlighter.addClassApplier(rangy.createClassApplier("highlight_blue", {
- ignoreWhiteSpace: true,
- tagNames: ["span", "a"]
- }));
+ bodyElement.style.webkitColumnGap = (paddingLeft + paddingRight) + 'px';
+ bodyElement.style.webkitColumnWidth = pageWidth + 'px';
+ bodyElement.style.columnFill = 'auto';
- this.highlighter.addClassApplier(rangy.createClassApplier("highlight_pink", {
- ignoreWhiteSpace: true,
- tagNames: ["span", "a"]
- }));
+ //console.log("-> window.innerWidth = " + window.innerWidth);
+ //console.log("-> window.innerHeight = " + window.innerHeight);
+ //console.log("-> clientWidth = " + document.documentElement.clientWidth);
+ //console.log("-> clientHeight = " + document.documentElement.clientHeight);
+ //console.log("-> bodyElement.offsetWidth = " + bodyElement.offsetWidth);
+ //console.log("-> bodyElement.offsetHeight = " + bodyElement.offsetHeight);
+ //console.log("-> pageWidth = " + pageWidth);
+ //console.log("-> pageHeight = " + pageHeight);
- this.highlighter.addClassApplier(rangy.createClassApplier("highlight_underline", {
- ignoreWhiteSpace: true,
- tagNames: ["span", "a"]
- }));
+ htmlElement.style.height = (pageHeight + (paddingTop + paddingBottom)) + 'px';
+ bodyElement.style.height = pageHeight + 'px';
+}
- },
+function postInitHorizontalDirection() {
- setFontAndada: function(){
- this.setFont("andada");
- },
+ var htmlElement = document.getElementsByTagName('html')[0];
+ var bodyElement = document.getElementsByTagName('body')[0];
+ var bodyStyle = bodyElement.currentStyle || window.getComputedStyle(bodyElement);
+ var paddingTop = parseInt(bodyStyle.paddingTop, 10);
+ var paddingRight = parseInt(bodyStyle.paddingRight, 10);
+ var paddingBottom = parseInt(bodyStyle.paddingBottom, 10);
+ var paddingLeft = parseInt(bodyStyle.paddingLeft, 10);
+ var clientWidth = document.documentElement.clientWidth;
- setFontLato: function(){
- this.setFont("lato");
- },
+ var scrollWidth = document.documentElement.scrollWidth;
+ //console.log("-> document.documentElement.offsetWidth = " + document.documentElement.offsetWidth);
+ if (scrollWidth > clientWidth
+ && scrollWidth > document.documentElement.offsetWidth) {
+ scrollWidth += paddingRight;
+ }
+ var newBodyWidth = scrollWidth - (paddingLeft + paddingRight);
+ window.scrollWidth = scrollWidth;
- setFontPtSerif: function(){
- this.setFont("pt-serif");
- },
+ htmlElement.style.width = scrollWidth + 'px';
+ bodyElement.style.width = newBodyWidth + 'px';
- setFontPtSans: function(){
- this.setFont("pt-sans");
- },
+ // pageCount deliberately rounded instead of ceiling to avoid any unexpected error
+ var pageCount = Math.round(scrollWidth / clientWidth);
+ var pageCountFloat = scrollWidth / clientWidth;
- base64encode: function(str){
- return btoa(unescape(encodeURIComponent(str)));
- },
+ if (pageCount != pageCountFloat) {
+ console.warn("-> pageCount = " + pageCount + ", pageCountFloat = " + pageCountFloat
+ + ", Something wrong in pageCount calculation");
+ }
- base64decode: function(str){
- return decodeURIComponent(escape(atob(str)));
- },
+ //console.log("-> scrollWidth = " + scrollWidth);
+ //console.log("-> newBodyWidth = " + newBodyWidth);
+ //console.log("-> pageCount = " + pageCount);
+
+ FolioPageFragment.setHorizontalPageCount(pageCount);
+}
+
+// TODO -> Check if this is required?
+function bodyOrHtml() {
+ if ('scrollingElement' in document) {
+ return document.scrollingElement;
+ }
+ // Fallback for legacy browsers
+ if (navigator.userAgent.indexOf('WebKit') != -1) {
+ return document.body;
+ }
+ return document.documentElement;
+}
+
+/**
+ * @param {(Element|Text|Range)} nodeOrRange
+ * @returns {(Element|Text|Range)} nodeOrRange
+ */
+function scrollToNodeOrRange(nodeOrRange) {
+
+ var scrollingElement = bodyOrHtml();
+ var direction = FolioWebView.getDirection();
- clearSelection: function(){
- if (window.getSelection) {
- if (window.getSelection().empty) { // Chrome
- window.getSelection().empty();
- } else if (window.getSelection().removeAllRanges) { // Firefox
- window.getSelection().removeAllRanges();
+ // For Direction.VERTICAL
+ var nodeOffsetTop, nodeOffsetHeight;
+
+ // For Direction.HORIZONTAL
+ var nodeOffsetLeft;
+
+ if (nodeOrRange instanceof Range || nodeOrRange.nodeType === Node.TEXT_NODE) {
+
+ var rect;
+ if (nodeOrRange.nodeType && nodeOrRange.nodeType === Node.TEXT_NODE) {
+ var range = document.createRange();
+ range.selectNode(nodeOrRange);
+ rect = RangeFix.getBoundingClientRect(range);
+ } else {
+ rect = RangeFix.getBoundingClientRect(nodeOrRange);
}
- } else if (document.selection) { // IE?
- document.selection.empty();
- }
- },
+ nodeOffsetTop = scrollingElement.scrollTop + rect.top;
+ nodeOffsetHeight = rect.height;
+ nodeOffsetLeft = scrollingElement.scrollLeft + rect.left;
- // Public methods
+ } else if (nodeOrRange.nodeType === Node.ELEMENT_NODE) {
- setFont: function(fontName){
- $("#ss-wrapper-font").removeClass().addClass("ss-wrapper-"+fontName);
- },
+ nodeOffsetTop = nodeOrRange.offsetTop;
+ nodeOffsetHeight = nodeOrRange.offsetHeight;
+ nodeOffsetLeft = nodeOrRange.offsetLeft;
- setSize: function(size){
- $("#ss-wrapper-size").removeClass().addClass("ss-wrapper-"+size);
- },
+ } else {
+ throw("-> Illegal Argument Exception, nodeOrRange -> " + nodeOrRange);
+ }
- setTheme: function(theme){
- $("body, #ss-wrapper-theme").removeClass().addClass("ss-wrapper-"+theme);
- },
+ switch (direction) {
+
+ case Direction.VERTICAL:
+ var topDistraction = FolioWebView.getTopDistraction(DisplayUnit.DP);
+ var pageTop = scrollingElement.scrollTop + topDistraction;
+ var pageBottom = scrollingElement.scrollTop + document.documentElement.clientHeight
+ - FolioWebView.getBottomDistraction(DisplayUnit.DP);
+
+ var elementTop = nodeOffsetTop - 20;
+ elementTop = elementTop < 0 ? 0 : elementTop;
+ var elementBottom = nodeOffsetTop + nodeOffsetHeight + 20;
+ var needToScroll = (elementTop < pageTop || elementBottom > pageBottom);
+
+ //console.log("-> topDistraction = " + topDistraction);
+ //console.log("-> pageTop = " + pageTop);
+ //console.log("-> elementTop = " + elementTop);
+ //console.log("-> pageBottom = " + pageBottom);
+ //console.log("-> elementBottom = " + elementBottom);
+
+ if (needToScroll) {
+ var newScrollTop = elementTop - topDistraction;
+ newScrollTop = newScrollTop < 0 ? 0 : newScrollTop;
+ //console.log("-> Scrolled to = " + newScrollTop);
+ scrollingElement.scrollTop = newScrollTop;
+ }
+ break;
- setComment: function(comment, inputId){
- $("#"+inputId).val(ssReader.base64decode(comment));
- $("#"+inputId).trigger("input", ["true"]);
- },
+ case Direction.HORIZONTAL:
+ var clientWidth = document.documentElement.clientWidth;
+ var pageIndex = Math.floor(nodeOffsetLeft / clientWidth);
+ var newScrollLeft = clientWidth * pageIndex;
+ //console.log("-> newScrollLeft = " + newScrollLeft);
+ scrollingElement.scrollLeft = newScrollLeft;
+ WebViewPager.setCurrentPage(pageIndex);
+ break;
+ }
- highlightSelection: function(color){
- try {
-
- this.highlighter.highlightSelection("highlight_" + color, null);
- var range = window.getSelection().toString();
- var params = {content: range,rangy: this.getHighlights(),color: color};
- this.clearSelection();
- Highlight.onReceiveHighlights(JSON.stringify(params));
- } catch(err){
- console.log("highlightSelection : " + err);
- }
- },
+ return nodeOrRange;
+}
- unHighlightSelection: function(){
- try {
- this.highlighter.unhighlightSelection();
- Highlight.onReceiveHighlights(this.getHighlights());
- } catch(err){}
- },
+function highlightSearchLocator(rangeCfi) {
- getHighlights: function(){
- try {
- return this.highlighter.serialize();
- } catch(err){}
- },
+ try {
+ var $obj = EPUBcfi.Interpreter.getRangeTargetElements(rangeCfi, document);
- setHighlights: function(serializedHighlight){
- try {
- this.highlighter.removeAllHighlights();
- this.highlighter.deserialize(serializedHighlight);
- } catch(err){}
- },
+ var range = document.createRange();
+ range.setStart($obj.startElement, $obj.startOffset);
+ range.setEnd($obj.endElement, $obj.endOffset);
- removeAll: function(){
- try {
- this.highlighter.removeAllHighlights();
- } catch(err){}
- },
+ window.getSelection().removeAllRanges();
+ window.getSelection().addRange(range);
+ scrollToNodeOrRange(range);
+ } catch (e) {
+ console.error("-> " + e);
+ }
- copy: function(){
- SSBridge.onCopy(window.getSelection().toString());
- this.clearSelection();
- },
+ LoadingView.hide();
+}
- share: function(){
- SSBridge.onShare(window.getSelection().toString());
- this.clearSelection();
- },
+/**
+ * Returns JSON of selection rect
+ * @param {Element} [element]
+ * @returns {object} JSON of {@link DOMRect}
+ */
+function getSelectionRect(element) {
+ console.log("-> getSelectionRect");
- search: function(){
- SSBridge.onSearch(window.getSelection().toString());
- this.clearSelection();
+ var range;
+ if (element !== undefined) {
+ range = document.createRange();
+ range.selectNodeContents(element);
+ } else {
+ range = window.getSelection().getRangeAt(0);
}
- });
- if(typeof ssReader !== "undefined"){
- ssReader.init();
+ //var rect = range.getBoundingClientRect();
+ var rect = RangeFix.getBoundingClientRect(range);
+ return {
+ left: rect.left,
+ top: rect.top,
+ right: rect.right,
+ bottom: rect.bottom
+ };
+}
+
+function clearSelection() {
+ console.log("-> clearSelection");
+ window.getSelection().removeAllRanges();
+}
+
+// onClick method set for highlights
+function onClickHighlight(element) {
+ console.log("-> onClickHighlight");
+ event.stopPropagation();
+ thisHighlight = element;
+ var rectJson = getSelectionRect(element);
+ FolioWebView.setSelectionRect(rectJson.left, rectJson.top, rectJson.right, rectJson.bottom);
+}
+
+function deleteThisHighlight() {
+ if (thisHighlight !== undefined)
+ FolioWebView.deleteThisHighlight(thisHighlight.id);
+}
+
+function onTextSelectionItemClicked(id) {
+ var selectionType = window.getSelection().type;
+ var selectedText = "";
+ if (selectionType == "Range") {
+ selectedText = window.getSelection().toString();
+ } else {
+ selectedText = thisHighlight.textContent;
}
+ FolioWebView.onTextSelectionItemClicked(id, selectedText);
+}
- $(".verse").click(function(){
- SSBridge.onVerseClick(ssReader.base64encode($(this).attr("verse")));
- });
+function onClickHtml() {
+ console.debug("-> onClickHtml");
+ if (FolioWebView.isPopupShowing()) {
+ FolioWebView.dismissPopupWindow();
+ } else {
+ FolioWebView.toggleSystemUI();
+ }
+}
+
+function computeLastReadCfi() {
+
+ viewportRect = constructDOMRect(FolioWebView.getViewportRect(DisplayUnit.CSS_PX));
+ var node = getFirstVisibleNode(document.body) || document.body;
- $("code").each(function(i){
- var textarea = $("").attr("id", "input-"+i).on("input propertychange", function(event, isInit) {
- $(this).css({'height': 'auto', 'overflow-y': 'hidden'}).height(this.scrollHeight);
- $(this).next().css({'height': 'auto', 'overflow-y': 'hidden'}).height(this.scrollHeight);
-
- if (!isInit) {
- var that = this;
- if (timeout !== null) {
- clearTimeout(timeout);
- }
- timeout = setTimeout(function () {
- SSBridge.onCommentsClick(
- ssReader.base64encode($(that).val()),
- $(that).attr("id")
- );
- }, 1000);
+ var cfi;
+ if (node.nodeType === Node.TEXT_NODE) {
+ cfi = EPUBcfi.Generator.generateCharacterOffsetCFIComponent(node, 0);
+ } else {
+ cfi = EPUBcfi.Generator.generateElementCFIComponent(node);
+ }
+
+ cfi = EPUBcfi.Generator.generateCompleteCFI("/0!", cfi);
+ viewportRect = null;
+ FolioPageFragment.storeLastReadCfi(cfi);
+}
+
+function constructDOMRect(rectJsonString) {
+ var rectJson = JSON.parse(rectJsonString);
+ return new DOMRect(rectJson.x, rectJson.y, rectJson.width, rectJson.height);
+}
+
+/**
+ * Gets the first partially or completely visible node in viewportRect
+ * @param {Node} node Accepts {@link Element} or {@link Text}
+ * @returns {(Node|null)} Returns {@link Element} or {@link Text} or null
+ */
+function getFirstVisibleNode(node) {
+
+ var range = document.createRange();
+ range.selectNode(node);
+ var rect = RangeFix.getBoundingClientRect(range);
+ if (rect == null)
+ return null;
+
+ var intersects = rectIntersects(viewportRect, rect);
+ var contains = rectContains(viewportRect, rect);
+
+ if (contains) {
+ // node's rect is completely inside viewportRect.
+ return node;
+
+ } else if (intersects) {
+
+ var childNodes = node.childNodes;
+ for (var i = 0; i < childNodes.length; i++) {
+
+ // EPUB CFI ignores nodes other than ELEMENT_NODE and TEXT_NODE
+ // http://www.idpf.org/epub/linking/cfi/epub-cfi.html#sec-path-child-ref
+
+ if (childNodes[i].nodeType === Node.ELEMENT_NODE || childNodes[i].nodeType === Node.TEXT_NODE) {
+ var childNode = getFirstVisibleNode(childNodes[i]);
+ if (childNode) {
+ return childNode;
+ }
+ }
}
- });
- var border = $("");
- var container = $("");
- $(textarea).appendTo(container);
- $(border).appendTo(container);
+ // No children found or no child's rect completely inside viewportRect,
+ // so returning this node as it's rect intersected with viewportRect.
+ return node;
+ }
+ return null;
+}
- $(this).after(container);
- });
- });
+function scrollToCfi(cfi) {
+
+ try {
+ var $node = EPUBcfi.Interpreter.getTargetElement(cfi, document);
+ scrollToNodeOrRange($node[0]);
+ } catch (e) {
+ console.error("-> " + e);
+ }
+ LoadingView.hide();
+}
-function array_diff(array1, array2){
- var difference = $.grep(array1, function(el) { return $.inArray(el,array2) < 0});
- return difference.concat($.grep(array2, function(el) { return $.inArray(el,array1) < 0}));;
+/**
+ * Returns true iff the two specified rectangles intersect. In no event are
+ * either of the rectangles modified.
+ *
+ * @param {DOMRect} a The first rectangle being tested for intersection
+ * @param {DOMRect} b The second rectangle being tested for intersection
+ * @returns {boolean} returns true iff the two specified rectangles intersect.
+ */
+function rectIntersects(a, b) {
+ return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
+}
+
+/**
+ * Returns true iff the specified rectangle b is inside or equal to
+ * rectangle b. An empty rectangle never contains another rectangle.
+ *
+ * @param {DOMRect} a The rectangle being tested whether rectangle b is inside this or not.
+ * @param {DOMRect} b The rectangle being tested for containment.
+ * @returns {boolean} returns true iff the specified rectangle r is inside or equal to this rectangle
+ */
+function rectContains(a, b) {
+ // check for empty first
+ return a.left < a.right && a.top < a.bottom
+ // now check for containment
+ && a.left <= b.left && a.top <= b.top && a.right >= b.right && a.bottom >= b.bottom;
}
diff --git a/folioreader/src/main/assets/js/jquery-3.1.1.min.js b/folioreader/src/main/assets/js/jquery-3.1.1.min.js
deleted file mode 100644
index 4c5be4c0f..000000000
--- a/folioreader/src/main/assets/js/jquery-3.1.1.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */
-!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),
-a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras pellentesque, dolor nec luctus ullamcorper, massa quam interdum metus, ac ultricies mauris risus nec purus. Fusce et nunc mi, ut consequat velit. Cras orci sapien, tincidunt sed iaculis ac, commodo sed neque. Sed gravida, quam id imperdiet venenatis, odio erat faucibus nisi, sed imperdiet velit nulla nec risus. Duis eget vehicula nibh. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt, augue vitae feugiat cursus, nunc nunc volutpat ligula, ac molestie purus lectus sit amet mauris. Nunc nec felis tortor, a dignissim eros. Vivamus diam mauris, scelerisque molestie bibendum ut, dignissim quis neque. Aliquam ac turpis mi. Sed pretium interdum orci, sed semper lorem viverra id. Vivamus diam eros, convallis sit amet facilisis in, facilisis eget nibh. Aenean porttitor, neque nec ultrices laoreet, dolor metus cursus purus, non auctor lectus urna a enim. In nec lectus nunc, ac commodo arcu. Proin bibendum ligula non mi bibendum porta. Nam ac metus a magna tristique euismod eget vitae purus.
-
-
-Vestibulum tincidunt, lectus at bibendum commodo, lectus velit commodo libero, et fermentum odio arcu sit amet dui. Quisque feugiat augue in erat scelerisque non mollis arcu facilisis. Nunc a dui sapien. Quisque quis nisi eu velit tincidunt placerat. Nunc vitae eros erat. Duis posuere diam ut orci adipiscing a ullamcorper est sollicitudin. Cras ipsum dolor, commodo viverra aliquet vel, pulvinar at sapien. Suspendisse eget justo et neque bibendum tempor. Morbi aliquet enim id arcu convallis cursus. Curabitur pellentesque condimentum dolor non sagittis. Ut tempor, erat in ullamcorper porta, purus erat tincidunt felis, ac auctor mauris libero nec purus. Praesent nisl justo, rutrum at rhoncus at, accumsan at mi. Fusce hendrerit imperdiet nulla a accumsan. Maecenas ut mi ac libero bibendum ullamcorper.
-
-
-Nam ac est nunc. Suspendisse faucibus dictum lacus, sed tincidunt erat laoreet id. Cras leo dui, sodales vitae blandit quis, placerat in arcu. Nunc semper odio id dolor bibendum vel euismod sapien malesuada. Vestibulum metus purus, consequat sed varius ornare, feugiat ac lectus. Suspendisse consectetur ipsum a enim aliquet a pellentesque ipsum condimentum. Etiam molestie, augue id consectetur bibendum, tellus ante vestibulum tellus, vitae aliquam turpis ipsum et elit. Aenean ipsum ante, eleifend in mollis at, molestie ac augue. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dui justo, consequat semper mollis sit amet, scelerisque vitae enim. Nullam nec odio et tortor sodales posuere. Mauris urna lorem, suscipit eu venenatis eu, consectetur ut purus. Vestibulum pharetra interdum convallis. Donec ac leo felis.
-
-
-Mauris vulputate magna eget ligula fermentum eu molestie justo vulputate. Pellentesque condimentum, sem a sollicitudin luctus, lorem nunc dictum risus, a tincidunt tortor metus nec risus. Nunc ultricies consectetur accumsan. Etiam placerat aliquam tortor id lacinia. Vestibulum quis sem non urna venenatis condimentum. Vestibulum tempor sapien quam, et vulputate elit. Sed convallis mauris sed turpis elementum vitae pretium est tempus. Praesent congue viverra pharetra. Etiam accumsan congue sapien eu molestie.
-
-
-Maecenas laoreet egestas tellus vestibulum gravida. Nulla rutrum dui at purus viverra et pretium diam fringilla. Sed nibh tortor, interdum ac egestas in, aliquam eu erat. Nunc convallis est posuere ipsum blandit luctus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur quam eros, hendrerit quis iaculis ut, rhoncus vel elit. Aenean sed ante et urna semper eleifend. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi mollis lacus eu nulla vestibulum consectetur lobortis risus pretium.
-
-
-Mauris scelerisque imperdiet venenatis. Suspendisse bibendum ullamcorper massa, vitae aliquam ligula sagittis at. Nullam rutrum justo nec tellus egestas eleifend. Suspendisse euismod quam in neque laoreet aliquam. Nunc non massa quis magna gravida ornare sed vitae tortor. Donec fringilla euismod accumsan. Mauris condimentum libero eget nisi sodales fringilla.
-
-
-Suspendisse eget tellus nibh, eu accumsan nibh. Aenean a metus ut leo dapibus lacinia. Vestibulum vitae ante nec urna aliquet posuere sollicitudin vitae mauris. Pellentesque dapibus dapibus nisi, fringilla placerat metus aliquet id. Mauris imperdiet ornare tellus sed ornare. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus elementum fringilla molestie. Cras ac enim at libero condimentum eleifend. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
-
-
-Maecenas vel hendrerit massa. Maecenas quis augue turpis. Fusce scelerisque gravida nisl vitae auctor. Donec porta, diam id fringilla convallis, risus elit ultrices enim, nec auctor mi justo ut urna. Integer consectetur, sem sed molestie adipiscing, ligula lectus convallis nisl, eget pretium ante nulla non sem. Nulla magna metus, ornare at facilisis sit amet, ultrices id erat. Mauris gravida risus ac augue vulputate imperdiet porta tortor fringilla. Pellentesque cursus aliquam velit at mattis. Aliquam molestie fringilla urna, eget convallis sem hendrerit in. Cras pretium, nunc et aliquam ullamcorper, justo augue cursus turpis, vitae volutpat urna mi sit amet nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
-
-Donec iaculis aliquam nulla sodales sodales. Nunc quis egestas ipsum. Suspendisse accumsan tortor eu lacus sodales eu venenatis lectus varius. Vestibulum nisl mi, vulputate quis pulvinar eu, malesuada vitae dui. Ut eget nulla ipsum. Donec in risus laoreet arcu venenatis aliquam. Curabitur orci augue, pulvinar quis sollicitudin et, laoreet a turpis. Donec a nunc id elit volutpat euismod sed at lorem.
-
-
-Phasellus arcu augue, rhoncus nec luctus quis, pharetra sit amet ante. Phasellus sed dui sit amet lacus auctor varius ac a arcu. Nullam eu congue ligula. Duis pretium nisi et tellus faucibus commodo. Vivamus dapibus imperdiet condimentum. Fusce at velit arcu, ac imperdiet mauris. Donec sit amet metus libero. Integer ac enim elit. Nulla quis erat urna. Morbi lobortis ligula vel sem placerat porttitor. Vestibulum in velit nulla, in malesuada sapien. Pellentesque sed sapien urna. Maecenas sodales felis sed nisl dictum euismod. Cras vitae sagittis elit.
-