diff --git a/app/build.gradle b/app/build.gradle
index f053c9d9..0d948ff6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,6 @@ android {
targetSdk 33
versionCode 1
versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -32,16 +30,18 @@ android {
}
}
+def mediaVersion = "1.0.1"
+
dependencies {
implementation 'androidx.core:core-ktx:1.10.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- implementation 'androidx.navigation:navigation-fragment:2.5.3'
- implementation 'androidx.navigation:navigation-ui:2.5.3'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.exifinterface:exifinterface:1.3.6'
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ implementation "androidx.media3:media3-exoplayer:$mediaVersion"
+ implementation "androidx.media3:media3-ui:$mediaVersion"
+ implementation "androidx.media3:media3-exoplayer-dash:$mediaVersion"
}
\ No newline at end of file
diff --git a/app/src/main/java/gallery/memories/MainActivity.kt b/app/src/main/java/gallery/memories/MainActivity.kt
index 881471f3..5093e302 100644
--- a/app/src/main/java/gallery/memories/MainActivity.kt
+++ b/app/src/main/java/gallery/memories/MainActivity.kt
@@ -1,21 +1,36 @@
package gallery.memories
import android.annotation.SuppressLint
+import android.net.Uri
import android.os.Bundle
+import android.view.View
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
+import androidx.media3.common.MediaItem
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.common.util.Util
+import androidx.media3.exoplayer.ExoPlayer
import gallery.memories.databinding.ActivityMainBinding
-class MainActivity : AppCompatActivity() {
- private lateinit var binding: ActivityMainBinding
+@UnstableApi class MainActivity : AppCompatActivity() {
+ private val binding by lazy(LazyThreadSafetyMode.NONE) {
+ ActivityMainBinding.inflate(layoutInflater)
+ }
+
private lateinit var mNativeX: NativeX
+ private var player: ExoPlayer? = null
+ private var playerUri: Uri? = null
+ private var playerUid: String? = null
+ private var playWhenReady = true
+ private var mediaItemIndex = 0
+ private var playbackPosition = 0L
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Initialize services
@@ -25,8 +40,35 @@ class MainActivity : AppCompatActivity() {
initializeWebView()
}
- @SuppressLint("SetJavaScriptEnabled")
+ override fun onDestroy() {
+ super.onDestroy()
+ mNativeX.destroy()
+ }
+
+ public override fun onResume() {
+ super.onResume()
+ if (playerUri != null && (Util.SDK_INT <= 23 || player == null)) {
+ initializePlayer(playerUri!!, playerUid!!)
+ }
+ }
+
+ public override fun onPause() {
+ super.onPause()
+ if (Util.SDK_INT <= 23) {
+ releasePlayer()
+ }
+ }
+
+ public override fun onStop() {
+ super.onStop()
+ if (Util.SDK_INT > 23) {
+ releasePlayer()
+ }
+ }
+
+ @SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility")
private fun initializeWebView() {
+ // Intercept local APIs
binding.webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
view.loadUrl(request.url.toString())
@@ -39,6 +81,15 @@ class MainActivity : AppCompatActivity() {
} else null
}
}
+
+ // Pass through touch events
+ binding.webview.setOnTouchListener { _, event ->
+ if (player != null) {
+ binding.videoView.dispatchTouchEvent(event)
+ }
+ false
+ }
+
val webSettings = binding.webview.settings
webSettings.javaScriptEnabled = true
webSettings.javaScriptCanOpenWindowsAutomatically = true
@@ -49,11 +100,52 @@ class MainActivity : AppCompatActivity() {
binding.webview.clearCache(true)
binding.webview.addJavascriptInterface(mNativeX, "nativex")
binding.webview.loadUrl("http://10.0.2.2:8035/index.php/apps/memories/")
+ binding.webview.setBackgroundColor(0x00000000)
}
- // Cleanup
- override fun onDestroy() {
- super.onDestroy()
- mNativeX.destroy()
+ fun initializePlayer(uri: Uri, uid: String) {
+ if (player != null) {
+ if (playerUid.equals(uid)) return
+ player?.release()
+ player = null
+ }
+
+ playerUri = uri
+ playerUid = uid
+
+ player = ExoPlayer.Builder(this)
+ .build()
+ .also { exoPlayer ->
+ binding.videoView.player = exoPlayer
+ binding.videoView.visibility = View.VISIBLE
+ val mediaItem = MediaItem.fromUri(uri)
+ exoPlayer.setMediaItems(listOf(mediaItem), mediaItemIndex, playbackPosition)
+ exoPlayer.playWhenReady = playWhenReady
+ exoPlayer.prepare()
+ }
+ }
+
+ fun destroyPlayer(uid: String) {
+ if (playerUid.equals(uid)) {
+ releasePlayer()
+
+ // Reset vars
+ playWhenReady = true
+ mediaItemIndex = 0
+ playbackPosition = 0L
+ playerUri = null
+ playerUid = null
+ }
+ }
+
+ private fun releasePlayer() {
+ player?.let { exoPlayer ->
+ playbackPosition = exoPlayer.currentPosition
+ mediaItemIndex = exoPlayer.currentMediaItemIndex
+ playWhenReady = exoPlayer.playWhenReady
+ exoPlayer.release()
+ }
+ player = null
+ binding.videoView.visibility = View.GONE
}
}
\ No newline at end of file
diff --git a/app/src/main/java/gallery/memories/NativeX.kt b/app/src/main/java/gallery/memories/NativeX.kt
index 7ca1813b..fa951b1f 100644
--- a/app/src/main/java/gallery/memories/NativeX.kt
+++ b/app/src/main/java/gallery/memories/NativeX.kt
@@ -9,14 +9,15 @@ import android.view.WindowInsetsController
import android.webkit.JavascriptInterface
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
-import androidx.appcompat.app.AppCompatActivity
+import androidx.media3.common.util.UnstableApi
+import gallery.memories.mapper.SystemImage
import gallery.memories.service.DownloadService
import gallery.memories.service.ImageService
import gallery.memories.service.TimelineQuery
import java.io.ByteArrayInputStream
import java.net.URLDecoder
-class NativeX(private val mActivity: AppCompatActivity) {
+@UnstableApi class NativeX(private val mActivity: MainActivity) {
val TAG = "NativeX"
private val mImageService: ImageService = ImageService(mActivity)
@@ -99,6 +100,31 @@ class NativeX(private val mActivity: AppCompatActivity) {
mDlService!!.queue(url, filename)
}
+ @JavascriptInterface
+ fun playVideoLocal(fileId: String?) {
+ if (fileId == null) return;
+
+ Thread {
+ // Get URI of local video
+ val videos = SystemImage.getByIds(mActivity, arrayListOf(fileId.toLong()))
+ if (videos.isEmpty()) return@Thread
+ val video = videos[0]
+
+ // Play with exoplayer
+ mActivity.runOnUiThread {
+ mActivity.initializePlayer(video.uri, fileId)
+ }
+ }.start()
+ }
+
+ @JavascriptInterface
+ fun destroyVideo(fileId: String?) {
+ if (fileId == null) return;
+ mActivity.runOnUiThread {
+ mActivity.destroyPlayer(fileId)
+ }
+ }
+
@Throws(Exception::class)
private fun routerGet(path: String): WebResourceResponse {
val parts = path.split("/").toTypedArray()
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b5f398bf..5f2a4f07 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,10 +6,18 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
+
+
+ />
\ No newline at end of file