Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Audio Player #72

Merged
merged 7 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies {
implementation(Libs.kotlin_stdlib_jdk8)
implementation(Libs.browser)

implementation("com.google.android.exoplayer:exoplayer:2.11.4")

testImplementation(Libs.junit)
androidTestImplementation(Libs.androidx_test_runner)
androidTestImplementation(Libs.espresso_core)
Expand Down
68 changes: 66 additions & 2 deletions app/src/main/java/to/dev/dev_android/util/AndroidWebViewBridge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,37 @@ package to.dev.dev_android.util
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.net.Uri
import android.os.Handler
import android.util.Log
import android.webkit.JavascriptInterface
import android.widget.Toast
import to.dev.dev_android.BuildConfig
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import to.dev.dev_android.base.BuildConfig
import to.dev.dev_android.view.main.view.CustomWebViewClient
import java.util.*


/**
* This class currently is empty because more methods would be added to it
* when new bridge functionalities are added.
*/
class AndroidWebViewBridge(private val context: Context) {
class AndroidWebViewBridge(private val context: Context) : Player.EventListener {

var webViewClient: CustomWebViewClient? = null
private var player: SimpleExoPlayer? = null
private val timer = java.util.Timer()
private val timeUpdateTask = object: TimerTask() {
override fun run() {
val mainHandler = Handler(context.mainLooper)
mainHandler.post(Runnable { podcastTimeUpdate() })
}
}

/**
* Every method that has to be accessed from web-view needs to be marked with
* `@JavascriptInterface`.
Expand All @@ -34,4 +55,47 @@ class AndroidWebViewBridge(private val context: Context) {
fun showToast(message: String) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}

@JavascriptInterface
fun loadPodcast(url: String) {
try {
if (player == null) {
player = SimpleExoPlayer.Builder(context).build()
player?.addListener(this)
}

var dataSourceFactory = DefaultDataSourceFactory(context, BuildConfig.userAgent)
var streamUri = Uri.parse(url)
var mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(streamUri)
player?.prepare(mediaSource)
} catch (e: Exception) {
Log.e("PODCAST", e.toString())
}
}

@JavascriptInterface
fun playPodcast(seconds: String) {
player?.setPlayWhenReady(true)
timer.schedule(timeUpdateTask, 0, 1000)
}

@JavascriptInterface
fun pausePodcast() {
player?.setPlayWhenReady(false)
}

@JavascriptInterface
fun terminatePodcast() {
timer.cancel()
player?.release()
player = null
}

fun podcastTimeUpdate() {
val position = (player?.contentPosition ?: 0 / 1000.0).toString()
val duration = (player?.duration ?: 0 / 1000.0).toString()
val message = mapOf("action" to "tick", "duration" to duration, "currentTime" to position)
Log.i("PODCAST", message.toString())
webViewClient?.sendPodcastMessage(message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ import android.content.Context
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.View
import android.webkit.CookieManager
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.browser.customtabs.CustomTabsIntent
import org.json.JSONObject
import to.dev.dev_android.R

class CustomWebViewClient(
private val context: Context,
private val view: WebView,
private val onPageFinish: () -> Unit
) : WebViewClient() {

private val overrideUrlList = listOf(
"://dev.to",
"://fdoxyz.ngrok.io",
fdocr marked this conversation as resolved.
Show resolved Hide resolved
"api.twitter.com/oauth",
"api.twitter.com/account/login_verification",
"github.com/login",
Expand Down Expand Up @@ -52,4 +57,16 @@ class CustomWebViewClient(

return true
}

fun sendPodcastMessage(message: Map<String, String>) {
val jsonMessage = JSONObject(message).toString()
val javascript = "document.getElementById('audiocontent').setAttribute('data-podcast', '$jsonMessage')"
view?.evaluateJavascript(javascript) { result ->
if (result != "null") {
Log.i("PODCAST", "Message sent successfully")
} else {
Log.w("PODCAST", "Message failed to be sent")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), CustomWebChromeClient.
binding.webView.settings.userAgentString = BuildConfig.userAgent

binding.webView.addJavascriptInterface(webViewBridge, "AndroidBridge")
binding.webView.webViewClient = CustomWebViewClient(this@MainActivity) {
binding.webView.webViewClient = CustomWebViewClient(this@MainActivity, binding.webView) {
binding.splash.visibility = View.GONE
}
binding.webView.webChromeClient = CustomWebChromeClient(BuildConfig.baseUrl, this)
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ org.gradle.jvmargs=-Xmx1536m
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.setJvmTargetFromAndroidCompileOptions = true
android.useAndroidX=true
android.enableJetifier=true
studio.projectview=true
Expand Down