Skip to content

Commit

Permalink
add server
Browse files Browse the repository at this point in the history
  • Loading branch information
lizongying committed Apr 30, 2024
1 parent 233e01f commit 1a99863
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 27 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
## 更新日志
### v1.1.5
* 可以指定默认频道
* 内置服务器,局域网内可配置
### v1.1.4
* 默认使用上次缓存视频源
Expand Down
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,6 @@ dependencies {
implementation(files("libs/lib-decoder-ffmpeg-release.aar"))

implementation("io.github.lizongying:gua64:1.4.3")

implementation("org.nanohttpd:nanohttpd:2.3.1")
}
25 changes: 22 additions & 3 deletions app/src/main/java/com/lizongying/mytv0/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class MainActivity : FragmentActivity() {

private lateinit var gestureDetector: GestureDetector

var server: SimpleServer? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Expand Down Expand Up @@ -96,13 +98,25 @@ class MainActivity : FragmentActivity() {
}
}

if (!TVList.setPosition(SP.position)) {
Log.i(TAG, "setPosition 0")
TVList.setPosition(0)
if (SP.channel > 0 && SP.channel < TVList.listModel.size) {
TVList.setPosition(SP.channel - 1)
} else {
if (!TVList.setPosition(SP.position)) {
Log.i(TAG, "setPosition 0")
TVList.setPosition(0)
}
}
val port = PortUtil.findFreePort()
if (port != -1) {
server = SimpleServer(this, port)
}
}
}

fun setServer(server: String) {
settingFragment.setServer(server)
}

private fun watch() {
TVList.listModel.forEach { tvModel ->
tvModel.errInfo.observe(this) { _ ->
Expand Down Expand Up @@ -636,6 +650,11 @@ class MainActivity : FragmentActivity() {
return super.onKeyDown(keyCode, event)
}

override fun onDestroy() {
super.onDestroy()
server?.stop()
}

companion object {
private const val TAG = "MainActivity"
}
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/com/lizongying/mytv0/PortUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.lizongying.mytv0

import java.io.IOException
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.ServerSocket

object PortUtil {

fun findFreePort(): Int {
var port = -1
try {
ServerSocket(0).use { socket ->
port = socket.localPort
}
} catch (e: IOException) {
e.printStackTrace()
}
return port
}

fun lan(): String? {
val networkInterfaces = NetworkInterface.getNetworkInterfaces()
while (networkInterfaces.hasMoreElements()) {
val inetAddresses = networkInterfaces.nextElement().inetAddresses
while (inetAddresses.hasMoreElements()) {
val inetAddress = inetAddresses.nextElement()
if (inetAddress is Inet4Address) {
return inetAddress.hostAddress
}
}
}
return null
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/lizongying/mytv0/SP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ object SP {

private const val KEY_CONFIG_AUTO_LOAD = "config_auto_load"

private const val KEY_CHANNEL = "channel"

private lateinit var sp: SharedPreferences

/**
Expand Down Expand Up @@ -80,4 +82,8 @@ object SP {
var configAutoLoad: Boolean
get() = sp.getBoolean(KEY_CONFIG_AUTO_LOAD, false)
set(value) = sp.edit().putBoolean(KEY_CONFIG_AUTO_LOAD, value).apply()

var channel: Int
get() = sp.getInt(KEY_CHANNEL, 0)
set(value) = sp.edit().putInt(KEY_CHANNEL, value).apply()
}
83 changes: 63 additions & 20 deletions app/src/main/java/com/lizongying/mytv0/SettingFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,37 @@ class SettingFragment : Fragment() {
(activity as MainActivity).settingActive()
}

val uriEditText = binding.myEditText
uriEditText.text = SP.config?.let { Editable.Factory.getInstance().newEditable(it) }
val config = binding.config
config.text = SP.config?.let { Editable.Factory.getInstance().newEditable(it) }
?: Editable.Factory.getInstance().newEditable("")
binding.confirmButton.setOnClickListener {
var uri = uriEditText.text.toString().trim()
binding.confirmConfig.setOnClickListener {
var uri = config.text.toString().trim()
uri = Utils.formatUrl(uri)
if (Uri.parse(uri).isAbsolute) {
TVList.update(uri)
SP.config = uri
} else {
uriEditText.error = "无效的地址"
config.error = "无效的地址"
}
(activity as MainActivity).settingActive()
}

val defaultChannel = binding.defaultChannel
defaultChannel.text =
SP.channel.let { Editable.Factory.getInstance().newEditable(it.toString()) }
?: Editable.Factory.getInstance().newEditable("")
binding.confirmDefaultChannel.setOnClickListener {
val c = defaultChannel.text.toString().trim()
var channel = 0
try {
channel = c.toInt()
} catch (e: NumberFormatException) {
println(e)
}
if (channel > 0 && channel <= TVList.listModel.size) {
SP.channel = channel
} else {
defaultChannel.error = "无效的频道"
}
(activity as MainActivity).settingActive()
}
Expand Down Expand Up @@ -130,6 +150,11 @@ class SettingFragment : Fragment() {
layoutParamsVersion.topMargin = application.px2Px(binding.version.marginTop)
binding.version.layoutParams = layoutParamsVersion

binding.server.textSize = application.px2PxFont(binding.server.textSize)
val layoutParamsServer = binding.server.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsServer.topMargin = application.px2Px(binding.server.marginTop)
binding.server.layoutParams = layoutParamsServer

binding.checkVersion.layoutParams.width =
application.px2Px(binding.checkVersion.layoutParams.width)
binding.checkVersion.layoutParams.height =
Expand All @@ -142,21 +167,35 @@ class SettingFragment : Fragment() {

binding.versionName.textSize = application.px2PxFont(binding.versionName.textSize)


binding.confirmButton.layoutParams.width =
application.px2Px(binding.confirmButton.layoutParams.width)
binding.confirmButton.layoutParams.height =
application.px2Px(binding.confirmButton.layoutParams.height)
binding.confirmButton.textSize = application.px2PxFont(binding.confirmButton.textSize)
val layoutParamsConfirmButton =
binding.confirmButton.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsConfirmButton.marginEnd = application.px2Px(binding.confirmButton.marginEnd)
binding.confirmButton.layoutParams = layoutParamsConfirmButton

binding.myEditText.layoutParams.width =
application.px2Px(binding.myEditText.layoutParams.width)
binding.myEditText.textSize = application.px2PxFont(binding.myEditText.textSize)

binding.confirmConfig.layoutParams.width =
application.px2Px(binding.confirmConfig.layoutParams.width)
binding.confirmConfig.layoutParams.height =
application.px2Px(binding.confirmConfig.layoutParams.height)
binding.confirmConfig.textSize = application.px2PxFont(binding.confirmConfig.textSize)
val layoutParamsConfirmConfig =
binding.confirmConfig.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsConfirmConfig.marginEnd = application.px2Px(binding.confirmConfig.marginEnd)
binding.confirmConfig.layoutParams = layoutParamsConfirmConfig

binding.config.layoutParams.width =
application.px2Px(binding.config.layoutParams.width)
binding.config.textSize = application.px2PxFont(binding.config.textSize)

binding.confirmDefaultChannel.layoutParams.width =
application.px2Px(binding.confirmDefaultChannel.layoutParams.width)
binding.confirmDefaultChannel.layoutParams.height =
application.px2Px(binding.confirmDefaultChannel.layoutParams.height)
binding.confirmDefaultChannel.textSize =
application.px2PxFont(binding.confirmDefaultChannel.textSize)
val layoutParamsConfirmDefaultChannel =
binding.confirmDefaultChannel.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsConfirmDefaultChannel.marginEnd =
application.px2Px(binding.confirmDefaultChannel.marginEnd)
binding.confirmDefaultChannel.layoutParams = layoutParamsConfirmDefaultChannel

binding.defaultChannel.layoutParams.width =
application.px2Px(binding.defaultChannel.layoutParams.width)
binding.defaultChannel.textSize = application.px2PxFont(binding.defaultChannel.textSize)

binding.appreciate.layoutParams.width =
application.px2Px(binding.appreciate.layoutParams.width)
Expand Down Expand Up @@ -235,6 +274,10 @@ class SettingFragment : Fragment() {
}
}

fun setServer(server: String) {
binding.server.text = "本机配置 http://$server"
}

fun setVersionName(versionName: String) {
binding.versionName.text = versionName
}
Expand Down
79 changes: 79 additions & 0 deletions app/src/main/java/com/lizongying/mytv0/SimpleServer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.lizongying.mytv0


import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.lizongying.mytv0.models.TVList
import fi.iki.elonen.NanoHTTPD
import java.io.IOException
import java.nio.charset.StandardCharsets

class SimpleServer(private val context: Context, port: Int) : NanoHTTPD(port) {
private val handler = Handler(Looper.getMainLooper())

init {
try {
start()
val host = PortUtil.lan()
(context as MainActivity).setServer("$host:$port")
println("Server running on $host:$port")
} catch (e: IOException) {
e.printStackTrace()
}
}

override fun serve(session: IHTTPSession): Response {
return when (session.uri) {
"/api/hello" -> handleHelloRequest(session)
"/api/channels" -> handleChannelsRequest(session)
else -> handleStaticContent(session)
}
}

private fun handleHelloRequest(session: IHTTPSession): Response {
val response = "Hello from NanoHTTPD API!"
return newFixedLengthResponse(Response.Status.OK, "text/plain", response)
}

private fun handleChannelsRequest(session: IHTTPSession): Response {
try {
val inputStream = session.inputStream
val buf = ByteArray(1024)
var read: Int
val sb = StringBuilder()
while (inputStream.available() > 0) {
read = inputStream.read(buf, 0, Math.min(buf.size, inputStream.available()))
sb.append(String(buf, 0, read))
}
val requestBody = sb.toString()
Log.i(TAG, requestBody)
handler.post {
TVList.str2List(requestBody)
}
} catch (e: IOException) {
return newFixedLengthResponse(
Response.Status.INTERNAL_ERROR,
MIME_PLAINTEXT,
"SERVER INTERNAL ERROR: IOException: " + e.message
)
}
val response = "Success!"
return newFixedLengthResponse(Response.Status.OK, "text/plain", response)
}

private fun handleStaticContent(session: IHTTPSession): Response {
val html = loadHtmlFromResource(R.raw.index)
return newFixedLengthResponse(Response.Status.OK, "text/html", html)
}

private fun loadHtmlFromResource(resourceId: Int): String {
val inputStream = context.resources.openRawResource(resourceId)
return inputStream.bufferedReader(StandardCharsets.UTF_8).use { it.readText() }
}

companion object {
const val TAG = "SimpleServer"
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/com/lizongying/mytv0/models/TVList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ object TVList {
update()
}

private fun str2List(str: String) {
fun str2List(str: String) {
var string = str
val g = Gua()
if (g.verify(str)) {
Expand Down
33 changes: 31 additions & 2 deletions app/src/main/res/layout/setting.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
android:text=""
android:textSize="14sp"
/>
<TextView
android:id="@+id/server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text=""
android:textSize="14sp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down Expand Up @@ -67,14 +75,14 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/confirm_button"
android:id="@+id/confirm_config"
android:layout_width="100dp"
android:layout_height="45dp"
android:layout_marginEnd="5dp"
android:textSize="14sp"
android:text="@string/confirm" />
<EditText
android:id="@+id/my_edit_text"
android:id="@+id/config"
android:layout_width="245dp"
android:layout_height="wrap_content"
android:textSize="14sp"
Expand All @@ -83,6 +91,27 @@
android:scrollbars="vertical"
android:inputType="textMultiLine" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/confirm_default_channel"
android:layout_width="100dp"
android:layout_height="45dp"
android:layout_marginEnd="5dp"
android:textSize="14sp"
android:text="@string/confirm" />
<EditText
android:id="@+id/default_channel"
android:layout_width="245dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:autofillHints=""
android:hint="@string/default_channel"
android:scrollbars="vertical"
android:inputType="textMultiLine" />
</LinearLayout>
<Button
android:id="@+id/appreciate"
android:layout_width="100dp"
Expand Down
Loading

0 comments on commit 1a99863

Please sign in to comment.