Compare commits

...

3 Commits

Author SHA1 Message Date
f17a7fcaa2 WebViewAssetLoader 2025-04-09 14:25:05 -04:00
5713b6572e upgrade AGP and Gradlee 2025-04-09 13:00:34 -04:00
4381016e57 remove unused tests 2025-04-08 14:37:44 -04:00
10 changed files with 97 additions and 40 deletions

26
.idea/appInsightsSettings.xml generated Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AppInsightsSettings">
<option name="tabSettings">
<map>
<entry key="Firebase Crashlytics">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="PLACEHOLDER" />
<option name="mobileSdkAppId" value="" />
<option name="projectId" value="" />
<option name="projectNumber" value="" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="THIRTY_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-04-03T16:08:16.340902800Z"> <DropdownSelection timestamp="2025-04-09T17:43:14.816533500Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="Default" identifier="serial=192.168.11.180:37459;connection=024bbef4" /> <DeviceId pluginId="Default" identifier="serial=192.168.11.180:43825;connection=7831d20c" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

View File

@@ -46,6 +46,7 @@ dependencies {
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui) implementation(libs.androidx.ui)
implementation(libs.androidx.webkit)
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) implementation(libs.androidx.material3)

View File

@@ -1,24 +0,0 @@
package com.zefie.zkiosk
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.zefie.zkiosk", appContext.packageName)
}
}

View File

@@ -111,6 +111,28 @@
} }
.calendar-day.today { .calendar-day.today {
background-color: #8d2b68; /* Highlight color */ background-color: #8d2b68; /* Highlight color */
}
#gotify-container {
position: relative;
top: -550px;
left: 1415px;
width: 500px;
height: 300px;
border: 1px solid #ccc;
overflow-y: auto;
padding: 10px;
color: #eee;
font-family: Arial, sans-serif;
border: 1px solid;
}
/* Style each notification block */
.notification {
border-bottom: 1px solid #eee;
padding: 5px 0;
margin-bottom: 5px;
}
.notification:last-child {
border-bottom: none;
} }
</style> </style>

View File

@@ -1,22 +1,26 @@
package com.zefie.zkiosk package com.zefie.zkiosk
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.videolan.libvlc.util.VLCVideoLayout
import android.os.Build
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.webkit.WebViewAssetLoader
import android.widget.PopupWindow import android.widget.PopupWindow
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView import android.widget.TextView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.videolan.libvlc.util.VLCVideoLayout
import java.util.Locale import java.util.Locale
@@ -51,8 +55,8 @@ class MainActivity : AppCompatActivity() {
videoLayout1 = findViewById(R.id.videoLayout1) videoLayout1 = findViewById(R.id.videoLayout1)
videoLayout2 = findViewById(R.id.videoLayout2) videoLayout2 = findViewById(R.id.videoLayout2)
restartButton = findViewById(R.id.restartButton) restartButton = findViewById(R.id.restartButton)
vlcPlayer1 = VLCPlayer(this, videoLayout1) vlcPlayer1 = VLCPlayer(this, videoLayout1, 1)
vlcPlayer2 = VLCPlayer(this, videoLayout2) vlcPlayer2 = VLCPlayer(this, videoLayout2, 2)
val seekBar = findViewById<SeekBar>(R.id.durationSlider) val seekBar = findViewById<SeekBar>(R.id.durationSlider)
setupSliderPopup(seekBar) setupSliderPopup(seekBar)
@@ -75,8 +79,31 @@ class MainActivity : AppCompatActivity() {
clockWebView.settings.displayZoomControls = false clockWebView.settings.displayZoomControls = false
clockWebView.settings.javaScriptEnabled = true clockWebView.settings.javaScriptEnabled = true
wxWebView.loadUrl("file:///android_asset/radar.html") val assetLoader = WebViewAssetLoader.Builder()
clockWebView.loadUrl("file:///android_asset/clock.html") .addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(this))
.addPathHandler("/res/", WebViewAssetLoader.ResourcesPathHandler(this))
.build()
wxWebView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
return assetLoader.shouldInterceptRequest(request.url)
}
}
clockWebView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
return assetLoader.shouldInterceptRequest(request.url)
}
}
wxWebView.loadUrl("https://appassets.androidplatform.net/assets/radar.html")
clockWebView.loadUrl("https://appassets.androidplatform.net/assets/clock.html")
startPlayer(1) startPlayer(1)
startPlayer(2) startPlayer(2)

View File

@@ -4,12 +4,13 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.widget.Toast
import org.videolan.libvlc.LibVLC import org.videolan.libvlc.LibVLC
import org.videolan.libvlc.Media import org.videolan.libvlc.Media
import org.videolan.libvlc.MediaPlayer import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.util.VLCVideoLayout import org.videolan.libvlc.util.VLCVideoLayout
class VLCPlayer(context: Context, private val videoLayout: VLCVideoLayout) { class VLCPlayer(private val context: Context, private val videoLayout: VLCVideoLayout, private val streamId: Int) {
private val libVLC: LibVLC = LibVLC(context, arrayListOf("--rtsp-tcp", private val libVLC: LibVLC = LibVLC(context, arrayListOf("--rtsp-tcp",
"--quiet", "--quiet",
"--no-drop-late-frames", "--no-drop-late-frames",
@@ -37,7 +38,7 @@ class VLCPlayer(context: Context, private val videoLayout: VLCVideoLayout) {
streamUrl = url streamUrl = url
val media = Media(libVLC, Uri.parse(url)) val media = Media(libVLC, Uri.parse(url))
media.setHWDecoderEnabled(true, false) media.setHWDecoderEnabled(true, false)
media.addOption(":network-caching=250") // Adjust caching to handle jitter media.addOption(":network-caching=500") // Adjust caching to handle jitter
mediaPlayer.media = media mediaPlayer.media = media
media.release() media.release()
mediaPlayer.play() mediaPlayer.play()
@@ -65,6 +66,9 @@ class VLCPlayer(context: Context, private val videoLayout: VLCVideoLayout) {
if (stalled) { if (stalled) {
val freezeDuration = now - lastProgressCheck val freezeDuration = now - lastProgressCheck
if (freezeDuration > 8000) { if (freezeDuration > 8000) {
handler.post {
Toast.makeText(context, "Watchdog triggered: Stream ${streamId} stalled, restarting...", Toast.LENGTH_SHORT).show()
}
restartStream() restartStream()
return return
} }

View File

@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.8.2" agp = "8.9.1"
appcompat = "1.7.0" appcompat = "1.7.0"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.15.0" coreKtx = "1.15.0"
@@ -11,10 +11,12 @@ lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.1" activityCompose = "1.10.1"
composeBom = "2025.03.01" composeBom = "2025.03.01"
material = "1.12.0" material = "1.12.0"
webkit = "1.13.0"
[libraries] [libraries]
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }

View File

@@ -1,6 +1,6 @@
#Sun Mar 02 16:14:47 EST 2025 #Sun Mar 02 16:14:47 EST 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists