mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-21 11:43:41 +08:00
Fix Android TV APK distribution (#139)
* Fix Android TV APK distribution * Fix Android TV Gradle settings * Run Android TV APK checks on branch pushes * Add Android TV launcher assets
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.kvideo.tv
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
@@ -9,23 +11,30 @@ import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.activity.ComponentActivity
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The URL of your deployed KVideo instance.
|
||||
* Change this to your own domain or IP address.
|
||||
*/
|
||||
private const val KVIDEO_URL = "https://kvideo.example.com"
|
||||
private const val PREFS_NAME = "kvideo_tv_settings"
|
||||
private const val PREF_SERVER_URL = "server_url"
|
||||
}
|
||||
|
||||
private lateinit var webView: WebView
|
||||
private lateinit var setupContainer: View
|
||||
private lateinit var urlInput: EditText
|
||||
private lateinit var statusText: TextView
|
||||
private lateinit var openButton: Button
|
||||
private lateinit var saveButton: Button
|
||||
private lateinit var prefs: android.content.SharedPreferences
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
// Fullscreen immersive mode
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
@@ -41,6 +50,15 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
webView = findViewById(R.id.webview)
|
||||
setupContainer = findViewById(R.id.setup_container)
|
||||
urlInput = findViewById(R.id.url_input)
|
||||
statusText = findViewById(R.id.status_text)
|
||||
openButton = findViewById(R.id.open_button)
|
||||
saveButton = findViewById(R.id.save_button)
|
||||
|
||||
saveButton.setOnClickListener {
|
||||
openConfiguredUrl()
|
||||
}
|
||||
|
||||
webView.apply {
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
@@ -58,14 +76,25 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
webViewClient = WebViewClient()
|
||||
webChromeClient = WebChromeClient()
|
||||
}
|
||||
|
||||
loadUrl(KVIDEO_URL)
|
||||
val configuredUrl = getConfiguredUrl()
|
||||
if (configuredUrl.isNotEmpty()) {
|
||||
urlInput.setText(configuredUrl)
|
||||
loadConfiguredUrl(configuredUrl)
|
||||
} else {
|
||||
showSetup(getString(R.string.status_first_launch))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (!isSetupVisible() && (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_SETTINGS)) {
|
||||
showSetup(getString(R.string.status_settings_hint))
|
||||
return true
|
||||
}
|
||||
|
||||
// Map D-pad center to Enter for spatial navigation
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
if (!isSetupVisible() && keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
webView.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
|
||||
return true
|
||||
}
|
||||
@@ -73,7 +102,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
if (!isSetupVisible() && keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
webView.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
|
||||
return true
|
||||
}
|
||||
@@ -82,11 +111,16 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Deprecated("Use OnBackPressedDispatcher")
|
||||
override fun onBackPressed() {
|
||||
if (isSetupVisible()) {
|
||||
@Suppress("DEPRECATION")
|
||||
super.onBackPressed()
|
||||
return
|
||||
}
|
||||
|
||||
if (webView.canGoBack()) {
|
||||
webView.goBack()
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
super.onBackPressed()
|
||||
showSetup(getString(R.string.status_settings_hint))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,4 +128,86 @@ class MainActivity : ComponentActivity() {
|
||||
webView.destroy()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun openConfiguredUrl() {
|
||||
val normalizedUrl = normalizeUrl(urlInput.text.toString())
|
||||
if (!isValidUrl(normalizedUrl)) {
|
||||
statusText.text = getString(R.string.status_invalid_url)
|
||||
urlInput.requestFocus()
|
||||
return
|
||||
}
|
||||
|
||||
prefs.edit().putString(PREF_SERVER_URL, normalizedUrl).apply()
|
||||
loadConfiguredUrl(normalizedUrl)
|
||||
}
|
||||
|
||||
private fun loadConfiguredUrl(url: String) {
|
||||
setupContainer.visibility = View.GONE
|
||||
statusText.text = getString(R.string.status_ready)
|
||||
webView.loadUrl(url)
|
||||
}
|
||||
|
||||
private fun showSetup(message: String) {
|
||||
val currentUrl = getConfiguredUrl()
|
||||
if (currentUrl.isNotEmpty() && urlInput.text.toString().isBlank()) {
|
||||
urlInput.setText(currentUrl)
|
||||
}
|
||||
|
||||
if (currentUrl.isNotEmpty()) {
|
||||
openButton.text = getString(R.string.button_open_saved)
|
||||
openButton.setOnClickListener {
|
||||
urlInput.setText(currentUrl)
|
||||
loadConfiguredUrl(currentUrl)
|
||||
}
|
||||
} else {
|
||||
openButton.text = getString(R.string.button_exit)
|
||||
openButton.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
statusText.text = message
|
||||
setupContainer.visibility = View.VISIBLE
|
||||
urlInput.requestFocus()
|
||||
}
|
||||
|
||||
private fun getConfiguredUrl(): String {
|
||||
val savedUrl = prefs.getString(PREF_SERVER_URL, null)?.trim().orEmpty()
|
||||
if (isValidUrl(savedUrl)) {
|
||||
return savedUrl
|
||||
}
|
||||
|
||||
val defaultUrl = normalizeUrl(BuildConfig.DEFAULT_KVIDEO_URL)
|
||||
return if (isValidUrl(defaultUrl)) defaultUrl else ""
|
||||
}
|
||||
|
||||
private fun isSetupVisible(): Boolean = setupContainer.visibility == View.VISIBLE
|
||||
|
||||
private fun normalizeUrl(rawUrl: String): String {
|
||||
val trimmed = rawUrl.trim()
|
||||
if (trimmed.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
val withScheme = if (
|
||||
trimmed.startsWith("http://", ignoreCase = true) ||
|
||||
trimmed.startsWith("https://", ignoreCase = true)
|
||||
) {
|
||||
trimmed
|
||||
} else {
|
||||
"https://$trimmed"
|
||||
}
|
||||
|
||||
return withScheme.removeSuffix("/")
|
||||
}
|
||||
|
||||
private fun isValidUrl(url: String): Boolean {
|
||||
if (url.isBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val uri = Uri.parse(url)
|
||||
val scheme = uri.scheme?.lowercase()
|
||||
return (scheme == "http" || scheme == "https") && !uri.host.isNullOrBlank()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user