新增:HttpServer允许自主指定Web客户端目录(/sdcard/Download/目录下) #191

This commit is contained in:
pppscn 2022-08-12 10:37:00 +08:00
parent 91b3cd11dc
commit fdc7b0399d
7 changed files with 135 additions and 20 deletions

View File

@ -1,6 +1,7 @@
package com.idormy.sms.forwarder.fragment
import android.content.Intent
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.text.Editable
@ -25,11 +26,14 @@ import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.button.SmoothCheckBox
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xutil.app.ServiceUtils
import com.xuexiang.xutil.net.NetworkUtils
import com.xuexiang.xutil.system.ClipboardUtils
import java.io.File
import java.net.InetAddress
@Suppress("PrivatePropertyName")
@Page(name = "主动控制·服务端")
class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListener {
@ -66,7 +70,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
override fun initListeners() {
binding!!.tvServerTips.setOnClickListener(this)
binding!!.ivCopy.setOnClickListener(this)
binding!!.toggleServerBtn.setOnClickListener(this)
binding!!.btnToggleServer.setOnClickListener(this)
binding!!.scbServerAutorun.isChecked = HttpServerUtils.enableServerAutorun
binding!!.scbServerAutorun.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
@ -76,6 +80,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
handler.post(runnable)
binding!!.btnSignKey.setOnClickListener(this)
binding!!.btnPathPicker.setOnClickListener(this)
binding!!.etSignKey.setText(HttpServerUtils.serverSignKey)
binding!!.etSignKey.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
@ -84,6 +89,14 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
HttpServerUtils.serverSignKey = binding!!.etSignKey.text.toString().trim()
}
})
binding!!.etWebPath.setText(HttpServerUtils.serverWebPath)
binding!!.etWebPath.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
HttpServerUtils.serverWebPath = binding!!.etWebPath.text.toString().trim()
}
})
binding!!.sbApiClone.isChecked = HttpServerUtils.enableApiClone
binding!!.sbApiClone.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
@ -129,7 +142,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
@SingleClick
override fun onClick(v: View) {
when (v.id) {
R.id.toggle_server_btn -> {
R.id.btn_toggle_server -> {
//检查权限是否获取
checkSendSmsPermission()
checkReadSmsPermission()
@ -153,6 +166,36 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
ClipboardUtils.copyText(url)
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), url))
}
R.id.btn_path_picker -> {
val downloadPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
val dirList = listSubDir(downloadPath)
if (dirList.isEmpty()) {
XToastUtils.error(String.format(getString(R.string.download_first), downloadPath))
return
}
MaterialDialog.Builder(requireContext())
.title(getString(R.string.select_web_client_directory))
.content(String.format(getString(R.string.root_directory), downloadPath))
.items(dirList)
.itemsCallbackSingleChoice(0) { _: MaterialDialog?, _: View?, _: Int, text: CharSequence ->
val webPath = "$downloadPath/$text"
binding!!.etWebPath.setText(webPath)
HttpServerUtils.serverWebPath = webPath
XToastUtils.info(getString(R.string.restarting_httpserver))
if (ServiceUtils.isServiceRunning("com.idormy.sms.forwarder.service.HttpService")) {
appContext?.stopService(Intent(appContext, HttpService::class.java))
appContext?.startService(Intent(appContext, HttpService::class.java))
} else {
appContext?.startService(Intent(appContext, HttpService::class.java))
}
refreshButtonText()
true // allow selection
}
.positiveText(R.string.select)
.negativeText(R.string.cancel)
.show()
}
else -> {}
}
}
@ -160,7 +203,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
//刷新按钮
private fun refreshButtonText() {
if (ServiceUtils.isServiceRunning("com.idormy.sms.forwarder.service.HttpService")) {
binding!!.toggleServerBtn.text = resources.getText(R.string.stop_server)
binding!!.btnToggleServer.text = resources.getText(R.string.stop_server)
binding!!.ivCopy.visibility = View.VISIBLE
try {
inetAddress = NetworkUtils.getLocalInetAddress()
@ -171,7 +214,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
binding!!.tvServerTips.text = getString(R.string.http_server_running, "127.0.0.1", HTTP_SERVER_PORT)
}
} else {
binding!!.toggleServerBtn.text = resources.getText(R.string.start_server)
binding!!.btnToggleServer.text = resources.getText(R.string.start_server)
binding!!.tvServerTips.text = getString(R.string.http_server_stopped)
binding!!.ivCopy.visibility = View.GONE
}
@ -282,4 +325,18 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
handler.removeCallbacks(runnable)
}
//获取Download的子目录
private fun listSubDir(downloadPath: String): List<String> {
val dirList = mutableListOf<String>()
val downloadDir = File(downloadPath)
val files = downloadDir.listFiles() ?: return dirList
for (file in files) {
if (file.isDirectory && !file.name.startsWith(".") && !file.name.startsWith("leakcanary-")) {
dirList.add(file.name)
}
}
return dirList
}
}

View File

@ -1,26 +1,35 @@
package com.idormy.sms.forwarder.server.component
import android.content.Context
import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.xuexiang.xrouter.utils.TextUtils
import com.yanzhenjie.andserver.annotation.Config
import com.yanzhenjie.andserver.framework.config.Multipart
import com.yanzhenjie.andserver.framework.config.WebConfig
import com.yanzhenjie.andserver.framework.website.AssetsWebsite
import com.yanzhenjie.andserver.framework.website.StorageWebsite
@Config
class AppConfig : WebConfig {
override fun onConfig(context: Context, delegate: WebConfig.Delegate) {
// 增加一个位于assert的Web目录网站
delegate.addWebsite(AssetsWebsite(context, "/web/"))
delegate.setMultipart(
val serverWebPath = HttpServerUtils.serverWebPath
if (!TextUtils.isEmpty(serverWebPath)) {
// 增加一个位于/sdcard/Download/目录下的网站
delegate.addWebsite(StorageWebsite(serverWebPath.toString()))
} else {
// 增加一个位于assets的web目录的网站
delegate.addWebsite(AssetsWebsite(context, "/web/"))
}
/*delegate.setMultipart(
Multipart.newBuilder()
.allFileMaxSize(1024 * 1024 * 20) // 单个请求所有文件总大小
.fileMaxSize(1024 * 1024 * 5) // 单个请求每个文件大小
.maxInMemorySize(1024 * 20) // 内存缓存大小
.uploadTempDir(context.cacheDir) // 上传文件保存目录
.build()
)
)*/
}
}

View File

@ -235,6 +235,7 @@ const val HTTP_FAILURE_CODE: Int = 500
const val SP_ENABLE_SERVER = "enable_server"
const val SP_ENABLE_SERVER_AUTORUN = "enable_server_autorun"
const val SP_SERVER_SIGN_KEY = "server_sign_key"
const val SP_SERVER_WEB_PATH = "server_web_path"
const val SP_ENABLE_API_CLONE = "enable_api_clone"
const val SP_ENABLE_API_SMS_SEND = "enable_api_sms_send"
const val SP_ENABLE_API_SMS_QUERY = "enable_api_sms_query"

View File

@ -40,6 +40,14 @@ class HttpServerUtils private constructor() {
MMKVUtils.put(SP_SERVER_SIGN_KEY, serverSignKey)
}
//自定义web客户端目录
@JvmStatic
var serverWebPath: String?
get() = MMKVUtils.getString(SP_SERVER_WEB_PATH, "")
set(serverWebPath) {
MMKVUtils.put(SP_SERVER_WEB_PATH, serverWebPath)
}
//服务地址
@JvmStatic
var serverAddress: String?

View File

@ -95,16 +95,6 @@
android:textSize="10sp"
tools:ignore="SmallSp" />
<!--<ImageView
android:id="@+id/iv_copy"
android:layout_width="@dimen/config_margin_16dp"
android:layout_height="@dimen/config_margin_16dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_copy"
android:visibility="gone"
app:tint="@color/colorStart"
tools:ignore="ContentDescription" />-->
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/iv_copy"
android:layout_width="wrap_content"
@ -126,7 +116,7 @@
</LinearLayout>
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/toggle_server_btn"
android:id="@+id/btn_toggle_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
@ -181,6 +171,44 @@
</LinearLayout>
<LinearLayout
style="@style/settingBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="15dp"
tools:ignore="RtlSymmetry">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/web_client"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_web_path"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_path_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select_directory"
android:textColor="@color/white"
android:textSize="10sp"
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -901,4 +901,10 @@
<string name="wol_port_hint">WOL is generally sent over port 7 or port 9</string>
<string name="wol_port_error">Port number value range: 1~65535</string>
<string name="wol_port_regex">^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$</string>
<string name="select_directory">Select Dir</string>
<string name="web_client">Web Client</string>
<string name="restarting_httpserver">Restarting HttpServer</string>
<string name="download_first">Download and unzip to:\n%s</string>
<string name="root_directory">Root Directory:\n%s</string>
<string name="select_web_client_directory">Select WebClient Directory</string>
</resources>

View File

@ -902,4 +902,10 @@
<string name="wol_port_hint">可选WOL一般透过端口7或端口9进行发送</string>
<string name="wol_port_error">端口号取值范围1~65535</string>
<string name="wol_port_regex">^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$</string>
<string name="select_directory">选择目录</string>
<string name="web_client">Web客户端</string>
<string name="restarting_httpserver">正在重启HttpServer</string>
<string name="download_first">请先下载Web客户端并解压到\n%s</string>
<string name="root_directory">根目录:\n%s</string>
<string name="select_web_client_directory">选择Web客户端目录</string>
</resources>