mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-03 01:17:41 +08:00
优化:发送通道企业微信应用
支持http/socks5代理(应对IP白名单限制)
This commit is contained in:
parent
93fb0b657a
commit
6ea06ed5ef
@ -1,6 +1,8 @@
|
|||||||
package com.idormy.sms.forwarder.entity.setting
|
package com.idormy.sms.forwarder.entity.setting
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
import java.net.Proxy
|
||||||
|
|
||||||
data class WeworkAgentSetting(
|
data class WeworkAgentSetting(
|
||||||
var corpID: String = "",
|
var corpID: String = "",
|
||||||
@ -8,4 +10,19 @@ data class WeworkAgentSetting(
|
|||||||
val secret: String = "",
|
val secret: String = "",
|
||||||
val atAll: Boolean? = false,
|
val atAll: Boolean? = false,
|
||||||
val toUser: String? = "@all",
|
val toUser: String? = "@all",
|
||||||
) : Serializable
|
val proxyType: Proxy.Type = Proxy.Type.DIRECT,
|
||||||
|
val proxyHost: String? = "",
|
||||||
|
val proxyPort: String? = "",
|
||||||
|
val proxyAuthenticator: Boolean? = false,
|
||||||
|
val proxyUsername: String? = "",
|
||||||
|
val proxyPassword: String? = "",
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
fun getProxyTypeCheckId(): Int {
|
||||||
|
return when (proxyType) {
|
||||||
|
Proxy.Type.HTTP -> R.id.rb_proxyHttp
|
||||||
|
Proxy.Type.SOCKS -> R.id.rb_proxySocks
|
||||||
|
else -> R.id.rb_proxyNone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
|
import android.widget.RadioGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.idormy.sms.forwarder.R
|
import com.idormy.sms.forwarder.R
|
||||||
@ -33,6 +34,7 @@ import io.reactivex.SingleObserver
|
|||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import java.net.Proxy
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Page(name = "企业微信应用")
|
@Page(name = "企业微信应用")
|
||||||
@ -126,6 +128,12 @@ class WeworkAgentFragment : BaseFragment<FragmentSendersWeworkAgentBinding?>(),
|
|||||||
binding!!.etSecret.setText(settingVo.secret)
|
binding!!.etSecret.setText(settingVo.secret)
|
||||||
binding!!.sbAtAll.isChecked = settingVo.atAll == true
|
binding!!.sbAtAll.isChecked = settingVo.atAll == true
|
||||||
binding!!.etToUser.setText(settingVo.toUser)
|
binding!!.etToUser.setText(settingVo.toUser)
|
||||||
|
binding!!.rgProxyType.check(settingVo.getProxyTypeCheckId())
|
||||||
|
binding!!.etProxyHost.setText(settingVo.proxyHost)
|
||||||
|
binding!!.etProxyPort.setText(settingVo.proxyPort)
|
||||||
|
binding!!.sbProxyAuthenticator.isChecked = settingVo.proxyAuthenticator == true
|
||||||
|
binding!!.etProxyUsername.setText(settingVo.proxyUsername)
|
||||||
|
binding!!.etProxyPassword.setText(settingVo.proxyPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -136,17 +144,36 @@ class WeworkAgentFragment : BaseFragment<FragmentSendersWeworkAgentBinding?>(),
|
|||||||
binding!!.btnDel.setOnClickListener(this)
|
binding!!.btnDel.setOnClickListener(this)
|
||||||
binding!!.btnSave.setOnClickListener(this)
|
binding!!.btnSave.setOnClickListener(this)
|
||||||
binding!!.sbAtAll.setOnCheckedChangeListener(this)
|
binding!!.sbAtAll.setOnCheckedChangeListener(this)
|
||||||
|
binding!!.sbProxyAuthenticator.setOnCheckedChangeListener(this)
|
||||||
|
binding!!.rgProxyType.setOnCheckedChangeListener { _: RadioGroup?, checkedId: Int ->
|
||||||
|
if (checkedId == R.id.rb_proxyHttp || checkedId == R.id.rb_proxySocks) {
|
||||||
|
binding!!.layoutProxyHost.visibility = View.VISIBLE
|
||||||
|
binding!!.layoutProxyPort.visibility = View.VISIBLE
|
||||||
|
binding!!.layoutProxyAuthenticator.visibility = if (binding!!.sbProxyAuthenticator.isChecked) View.VISIBLE else View.GONE
|
||||||
|
} else {
|
||||||
|
binding!!.layoutProxyHost.visibility = View.GONE
|
||||||
|
binding!!.layoutProxyPort.visibility = View.GONE
|
||||||
|
binding!!.layoutProxyAuthenticator.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
|
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
||||||
//注意:因为只有一个监听,暂不需要判断id
|
when (buttonView.id) {
|
||||||
if (isChecked) {
|
R.id.sb_at_all -> {
|
||||||
binding!!.etToUser.setText("@all")
|
if (isChecked) {
|
||||||
binding!!.layoutToUser.visibility = View.GONE
|
binding!!.etToUser.setText("@all")
|
||||||
} else {
|
binding!!.layoutToUser.visibility = View.GONE
|
||||||
binding!!.etToUser.setText("")
|
} else {
|
||||||
binding!!.layoutToUser.visibility = View.VISIBLE
|
binding!!.etToUser.setText("")
|
||||||
|
binding!!.layoutToUser.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.sb_proxyAuthenticator -> {
|
||||||
|
binding!!.layoutProxyAuthenticator.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +252,26 @@ class WeworkAgentFragment : BaseFragment<FragmentSendersWeworkAgentBinding?>(),
|
|||||||
val atAll = binding!!.sbAtAll.isChecked
|
val atAll = binding!!.sbAtAll.isChecked
|
||||||
val toUser = binding!!.etToUser.text.toString().trim()
|
val toUser = binding!!.etToUser.text.toString().trim()
|
||||||
|
|
||||||
return WeworkAgentSetting(corpID, agentID, secret, atAll, toUser)
|
val proxyType: Proxy.Type = when (binding!!.rgProxyType.checkedRadioButtonId) {
|
||||||
|
R.id.rb_proxyHttp -> Proxy.Type.HTTP
|
||||||
|
R.id.rb_proxySocks -> Proxy.Type.SOCKS
|
||||||
|
else -> Proxy.Type.DIRECT
|
||||||
|
}
|
||||||
|
val proxyHost = binding!!.etProxyHost.text.toString().trim()
|
||||||
|
val proxyPort = binding!!.etProxyPort.text.toString().trim()
|
||||||
|
|
||||||
|
if (proxyType != Proxy.Type.DIRECT && (TextUtils.isEmpty(proxyHost) || TextUtils.isEmpty(proxyPort))) {
|
||||||
|
throw Exception(getString(R.string.invalid_host_or_port))
|
||||||
|
}
|
||||||
|
|
||||||
|
val proxyAuthenticator = binding!!.sbProxyAuthenticator.isChecked
|
||||||
|
val proxyUsername = binding!!.etProxyUsername.text.toString().trim()
|
||||||
|
val proxyPassword = binding!!.etProxyPassword.text.toString().trim()
|
||||||
|
if (proxyAuthenticator && TextUtils.isEmpty(proxyUsername) && TextUtils.isEmpty(proxyPassword)) {
|
||||||
|
throw Exception(getString(R.string.invalid_username_or_password))
|
||||||
|
}
|
||||||
|
|
||||||
|
return WeworkAgentSetting(corpID, agentID, secret, atAll, toUser, proxyType, proxyHost, proxyPort, proxyAuthenticator, proxyUsername, proxyPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -17,6 +17,14 @@ import com.xuexiang.xhttp2.cache.model.CacheMode
|
|||||||
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
import com.xuexiang.xhttp2.exception.ApiException
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
import com.xuexiang.xui.utils.ResUtils.getString
|
import com.xuexiang.xui.utils.ResUtils.getString
|
||||||
|
import com.xuexiang.xutil.net.NetworkUtils
|
||||||
|
import okhttp3.Credentials
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.Route
|
||||||
|
import java.net.Authenticator
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.PasswordAuthentication
|
||||||
|
import java.net.Proxy
|
||||||
|
|
||||||
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
|
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
|
||||||
class WeworkAgentUtils private constructor() {
|
class WeworkAgentUtils private constructor() {
|
||||||
@ -42,8 +50,49 @@ class WeworkAgentUtils private constructor() {
|
|||||||
getTokenUrl += "&corpsecret=" + setting.secret
|
getTokenUrl += "&corpsecret=" + setting.secret
|
||||||
Log.d(TAG, "getTokenUrl:$getTokenUrl")
|
Log.d(TAG, "getTokenUrl:$getTokenUrl")
|
||||||
|
|
||||||
XHttp.get(getTokenUrl)
|
val request = XHttp.get(getTokenUrl)
|
||||||
.keepJson(true)
|
|
||||||
|
//设置代理
|
||||||
|
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||||
|
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||||
|
) {
|
||||||
|
//代理服务器的IP和端口号
|
||||||
|
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||||
|
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||||
|
if (!NetworkUtils.isIP(proxyHost)) {
|
||||||
|
throw Exception("代理服务器主机名解析失败:proxyHost=$proxyHost")
|
||||||
|
}
|
||||||
|
val proxyPort: Int = setting.proxyPort?.toInt() ?: 7890
|
||||||
|
|
||||||
|
Log.d(TAG, "proxyHost = $proxyHost, proxyPort = $proxyPort")
|
||||||
|
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||||
|
|
||||||
|
//代理的鉴权账号密码
|
||||||
|
if (setting.proxyAuthenticator == true
|
||||||
|
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||||
|
) {
|
||||||
|
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||||
|
|
||||||
|
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||||
|
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||||
|
//设置代理服务器账号密码
|
||||||
|
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||||
|
response.request().newBuilder()
|
||||||
|
.header("Proxy-Authorization", credential)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Authenticator.setDefault(object : Authenticator() {
|
||||||
|
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||||
|
return PasswordAuthentication(setting.proxyUsername.toString(), setting.proxyPassword?.toCharArray())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.keepJson(true)
|
||||||
|
.ignoreHttpsCert()
|
||||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||||
.cacheMode(CacheMode.NO_CACHE)
|
.cacheMode(CacheMode.NO_CACHE)
|
||||||
.timeStamp(true)
|
.timeStamp(true)
|
||||||
@ -96,9 +145,50 @@ class WeworkAgentUtils private constructor() {
|
|||||||
val requestMsg: String = Gson().toJson(textMsgMap)
|
val requestMsg: String = Gson().toJson(textMsgMap)
|
||||||
Log.i(TAG, "requestMsg:$requestMsg")
|
Log.i(TAG, "requestMsg:$requestMsg")
|
||||||
|
|
||||||
XHttp.post(requestUrl)
|
val request = XHttp.post(requestUrl)
|
||||||
.upJson(requestMsg)
|
|
||||||
|
//设置代理
|
||||||
|
if ((setting.proxyType == Proxy.Type.HTTP || setting.proxyType == Proxy.Type.SOCKS)
|
||||||
|
&& !TextUtils.isEmpty(setting.proxyHost) && !TextUtils.isEmpty(setting.proxyPort)
|
||||||
|
) {
|
||||||
|
//代理服务器的IP和端口号
|
||||||
|
Log.d(TAG, "proxyHost = ${setting.proxyHost}, proxyPort = ${setting.proxyPort}")
|
||||||
|
val proxyHost = if (NetworkUtils.isIP(setting.proxyHost)) setting.proxyHost else NetworkUtils.getDomainAddress(setting.proxyHost)
|
||||||
|
if (!NetworkUtils.isIP(proxyHost)) {
|
||||||
|
throw Exception("代理服务器主机名解析失败:proxyHost=$proxyHost")
|
||||||
|
}
|
||||||
|
val proxyPort: Int = setting.proxyPort?.toInt() ?: 7890
|
||||||
|
|
||||||
|
Log.d(TAG, "proxyHost = $proxyHost, proxyPort = $proxyPort")
|
||||||
|
request.okproxy(Proxy(setting.proxyType, InetSocketAddress(proxyHost, proxyPort)))
|
||||||
|
|
||||||
|
//代理的鉴权账号密码
|
||||||
|
if (setting.proxyAuthenticator == true
|
||||||
|
&& (!TextUtils.isEmpty(setting.proxyUsername) || !TextUtils.isEmpty(setting.proxyPassword))
|
||||||
|
) {
|
||||||
|
Log.i(TAG, "proxyUsername = ${setting.proxyUsername}, proxyPassword = ${setting.proxyPassword}")
|
||||||
|
|
||||||
|
if (setting.proxyType == Proxy.Type.HTTP) {
|
||||||
|
request.okproxyAuthenticator { _: Route?, response: Response ->
|
||||||
|
//设置代理服务器账号密码
|
||||||
|
val credential = Credentials.basic(setting.proxyUsername.toString(), setting.proxyPassword.toString())
|
||||||
|
response.request().newBuilder()
|
||||||
|
.header("Proxy-Authorization", credential)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Authenticator.setDefault(object : Authenticator() {
|
||||||
|
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||||
|
return PasswordAuthentication(setting.proxyUsername.toString(), setting.proxyPassword?.toCharArray())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.upJson(requestMsg)
|
||||||
.keepJson(true)
|
.keepJson(true)
|
||||||
|
.ignoreHttpsCert()
|
||||||
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
.timeOut((SettingUtils.requestTimeout * 1000).toLong()) //超时时间10s
|
||||||
.cacheMode(CacheMode.NO_CACHE)
|
.cacheMode(CacheMode.NO_CACHE)
|
||||||
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
.retryCount(SettingUtils.requestRetryTimes) //超时重试的次数
|
||||||
|
@ -185,6 +185,153 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/proxy_settings"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/rg_proxyType"
|
||||||
|
style="@style/rg_style"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_proxyNone"
|
||||||
|
style="@style/rg_rb_style"
|
||||||
|
android:checked="true"
|
||||||
|
android:text="@string/proxy_none" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_proxyHttp"
|
||||||
|
style="@style/rg_rb_style"
|
||||||
|
android:text="@string/proxy_http" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_proxySocks"
|
||||||
|
style="@style/rg_rb_style"
|
||||||
|
android:text="@string/proxy_socks" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutProxyHost"
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/hostname" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_proxyHost"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutProxyPort"
|
||||||
|
style="@style/senderBarStyleWithSwitch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/port" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_proxyPort"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:digits="0123456789"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="5"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:text="@string/proxy_authenticator" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||||
|
android:id="@+id/sb_proxyAuthenticator"
|
||||||
|
style="@style/SwitchButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutProxyAuthenticator"
|
||||||
|
style="@style/senderBarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/username" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_proxyUsername"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_clearButton="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/password" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||||
|
android:id="@+id/et_proxyPassword"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:met_passWordButton="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user