Skip to content
This repository has been archived by the owner on Jul 29, 2022. It is now read-only.

Add a PDF navigator #130

Merged
merged 14 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions r2-navigator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,40 @@ dependencies {
implementation 'androidx.webkit:webkit:1.1.0'
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "com.google.android.material:material:1.2.0-alpha03"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'joda-time:joda-time:2.9.9'
implementation "androidx.legacy:legacy-support-core-ui:1.0.0"

implementation 'com.duolingo.open:rtl-viewpager:1.0.3'
implementation 'androidx.viewpager2:viewpager2:1.0.0'

implementation 'com.jakewharton.timber:timber:4.7.1'

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'

implementation 'org.zeroturnaround:zt-zip:1.13'

implementation 'org.jsoup:jsoup:1.10.3'
api 'com.github.barteksc:android-pdf-viewer:2.8.2'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"

implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.appcompat:appcompat:1.2.0-beta01"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.fragment:fragment-ktx:1.2.4"
implementation "androidx.legacy:legacy-support-core-ui:1.0.0"
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'androidx.webkit:webkit:1.1.0'

// ChrisBane/PhotoView ( for the Zoom handling )
implementation 'com.github.chrisbanes:PhotoView:2.1.4'
}
3 changes: 3 additions & 0 deletions r2-navigator/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<activity
android:name=".divina.R2DiViNaActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".pdf.R2PdfActivity"
android:theme="@style/AppTheme" />

</application>

Expand Down
67 changes: 16 additions & 51 deletions r2-navigator/src/main/java/org/readium/r2/navigator/IR2Activity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
package org.readium.r2.navigator

import android.content.SharedPreferences
import android.graphics.PointF
import android.view.View
import androidx.lifecycle.LiveData
import org.readium.r2.navigator.pager.R2ViewPager
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.Locator
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.ReadingProgression
import java.net.URL

interface IR2Activity {

Expand Down Expand Up @@ -51,59 +52,32 @@ interface IR2TTS {


interface Navigator {
val currentLocation: Locator?
fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean
fun go(link: Link, animated: Boolean, completion: () -> Unit): Boolean
fun goForward(animated: Boolean, completion: () -> Unit): Boolean
fun goBackward(animated: Boolean, completion: () -> Unit): Boolean

}

fun Navigator.go(locator: Locator, animated: Boolean = false, completion: () -> Unit = {}): Boolean =
go(locator = locator, animated = animated, completion = completion)
val currentLocator: LiveData<Locator?>

fun Navigator.go(link: Link, animated: Boolean = false, completion: () -> Unit = {}): Boolean =
go(link = link, animated = animated, completion = completion)
fun go(locator: Locator, animated: Boolean = false, completion: () -> Unit = {}): Boolean
fun go(link: Link, animated: Boolean = false, completion: () -> Unit = {}): Boolean
fun goForward(animated: Boolean = false, completion: () -> Unit = {}): Boolean
fun goBackward(animated: Boolean = false, completion: () -> Unit = {}): Boolean

fun Navigator.goForward(animated: Boolean = false, completion: () -> Unit = {}): Boolean =
goForward(animated = animated, completion = completion)
@Deprecated("Use [currentLocator] instead", ReplaceWith("currentLocator.value"))
val currentLocation: Locator? get() = currentLocator.value

fun Navigator.goBackward(animated: Boolean = false, completion: () -> Unit = {}): Boolean =
goBackward(animated = animated, completion = completion)
interface Listener {
}

interface VisualListener : Listener {
fun onTap(point: PointF): Boolean = false
}
}

interface NavigatorDelegate {
@Deprecated("Observe [currentLocator] instead")
fun locationDidChange(navigator: Navigator? = null, locator: Locator)

// present error message
fun presentError(navigator: Navigator? = null, error: NavigatorError) {}

// present external url
fun presentExternalURL(navigator: Navigator? = null, url: URL) {}
}


//public fun NavigatorDelegate.navigator(navigator: Navigator, url: URL) {
// if (UIApplication.shared.canOpenURL(url)) {
// UIApplication.shared.openURL(url)
// }
//}


sealed class NavigatorError : Exception() {
object copyForbidden : NavigatorError()

val errorDescription: String?
get() {
return when (this) {
is copyForbidden -> "NavigatorError.copyForbidden"
}
}
}


interface VisualNavigator : Navigator {
// val view: UIView
val readingProgression: ReadingProgression

fun goLeft(animated: Boolean, completion: () -> Unit): Boolean
Expand All @@ -130,12 +104,3 @@ fun VisualNavigator.goRight(animated: Boolean = false, completion: () -> Unit =
goBackward(animated = animated, completion = completion)
}
}


//public interface VisualNavigatorDelegate: NavigatorDelegate {
// fun navigator(navigator: VisualNavigator, point: CGPoint)
//}

//public fun VisualNavigatorDelegate.navigator(navigator: VisualNavigator, point: CGPoint) {}


Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Module: r2-navigator-kotlin
* Developers: Mickaël Menu
*
* Copyright (c) 2020. Readium Foundation. All rights reserved.
* Use of this source code is governed by a BSD-style license which is detailed in the
* LICENSE file present in the project repository where this source code is maintained.
*/

package org.readium.r2.navigator

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import org.readium.r2.navigator.pdf.PdfNavigatorFragment
import org.readium.r2.shared.publication.Locator
import org.readium.r2.shared.publication.Publication

class NavigatorFragmentFactory(
private val publication: Publication,
private val initialLocator: Locator? = null,
private val listener: Navigator.Listener? = null
) : FragmentFactory() {

override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
when (className) {
PdfNavigatorFragment::class.java.name -> PdfNavigatorFragment(publication, initialLocator, listener)
else -> super.instantiate(classLoader, className)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.android.synthetic.main.activity_r2_audiobook.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.readium.r2.navigator.*
import org.readium.r2.navigator.BuildConfig.*
import org.readium.r2.navigator.BuildConfig.DEBUG
import org.readium.r2.navigator.IR2Activity
import org.readium.r2.navigator.NavigatorDelegate
import org.readium.r2.navigator.R
import org.readium.r2.navigator.VisualNavigator
import org.readium.r2.shared.extensions.destroyPublication
import org.readium.r2.shared.extensions.getPublication
import org.readium.r2.shared.publication.*
Expand All @@ -24,8 +29,11 @@ import kotlin.coroutines.CoroutineContext

open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activity, MediaPlayerCallback, VisualNavigator {

override val currentLocation: Locator? get() =
publication.readingOrder[currentResource].let { resource ->
override val currentLocator: LiveData<Locator?> get() = _currentLocator
private val _currentLocator = MutableLiveData<Locator?>(null)

private fun notifyCurrentLocation() {
val locator = publication.readingOrder[currentResource].let { resource ->
val progression = mediaPlayer
?.let { it.currentPosition / it.duration }
?: 0.0
Expand All @@ -44,6 +52,14 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit
)
}

if (locator == currentLocator.value) {
return
}

_currentLocator.postValue(locator)
navigatorDelegate?.locationDidChange(navigator = this, locator = locator)
}

override fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean {
val resourceIndex = publication.readingOrder.indexOfFirstWithHref(locator.href)
?: return false
Expand Down Expand Up @@ -135,9 +151,9 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit

mediaPlayer?.goTo(currentResource)

currentLocation?.locations?.progression?.let { progression ->
currentLocator.value?.locations?.progression?.let { progression ->
mediaPlayer?.seekTo(progression)
seekLocation = currentLocation?.locations
seekLocation = currentLocator.value?.locations
isSeekNeeded = true
}

Expand Down Expand Up @@ -270,12 +286,7 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit

seekBar!!.progress = startTime.toInt()

val resource = publication.readingOrder[currentResource]
val resourceHref = resource.href
val resourceType = resource.type ?: ""

navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locator.Locations(progression = seekBar!!.progress.toDouble())))

notifyCurrentLocation()
}

private var seekLocation: Locator.Locations? = null
Expand Down Expand Up @@ -337,11 +348,7 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit
TimeUnit.MILLISECONDS.toSeconds(startTime.toLong()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(startTime.toLong())))
seekBar!!.progress = startTime.toInt()

val resource = publication.readingOrder[currentResource]
val resourceHref = resource.href
val resourceType = resource.type ?: ""

navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locator.Locations(progression = seekBar!!.progress.toDouble())))
notifyCurrentLocation()

Handler().postDelayed(this, 100)
}
Expand Down Expand Up @@ -375,9 +382,6 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit
if (data != null) {
val locator = data.getParcelableExtra("locator") as Locator

// Set the progression fetched
navigatorDelegate?.locationDidChange(locator = locator)

// href is the link to the page in the toc
var href = locator.href

Expand All @@ -403,6 +407,7 @@ open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activit

chapterView!!.text = publication.readingOrder[currentResource].title

notifyCurrentLocation()
}
}

Expand Down
Loading