新增:短信指令(根据短信指令开关对应功能) #I5YX3F

pull/286/head
pppscn 2 years ago
parent dd798e42cc
commit 6571775a0f

@ -1,77 +1,83 @@
package com.idormy.sms.forwarder.adapter
import android.annotation.SuppressLint
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.FrpcPagingAdapter.MyViewHolder
import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.databinding.AdapterFrpcsCardViewListItemBinding
import com.xuexiang.xutil.resource.ResUtils.getColors
import frpclib.Frpclib
class FrpcPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Frpc, MyViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = AdapterFrpcsCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
if (item != null) {
holder.binding.ivImage.setImageResource(R.drawable.ic_menu_frpc)
holder.binding.ivAutorun.setImageResource(item.autorunImageId)
holder.binding.tvName.text = item.name
if (item.connecting || Frpclib.isRunning(item.uid)) {
holder.binding.ivPlay.setImageResource(R.drawable.ic_stop)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStop)
}
} else {
holder.binding.ivPlay.setImageResource(R.drawable.ic_start)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStart)
}
}
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
holder.binding.ivPlay.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivEdit.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivDelete.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
}
}
class MyViewHolder(val binding: AdapterFrpcsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
interface OnItemClickListener {
fun onItemClicked(view: View?, item: Frpc)
fun onItemRemove(view: View?, id: Int)
}
companion object {
var diffCallback: DiffUtil.ItemCallback<Frpc> = object : DiffUtil.ItemCallback<Frpc>() {
override fun areItemsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
return oldItem.uid == newItem.uid
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
return oldItem === newItem
}
}
}
package com.idormy.sms.forwarder.adapter
import android.annotation.SuppressLint
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.FrpcPagingAdapter.MyViewHolder
import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.databinding.AdapterFrpcsCardViewListItemBinding
import com.xuexiang.xutil.resource.ResUtils.getColors
import frpclib.Frpclib
class FrpcPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Frpc, MyViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = AdapterFrpcsCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
if (item != null) {
holder.binding.ivImage.setImageResource(R.drawable.ic_menu_frpc)
holder.binding.ivAutorun.setImageResource(item.autorunImageId)
holder.binding.tvUid.text = "UID:${item.uid}"
holder.binding.tvName.text = item.name
if (item.connecting || Frpclib.isRunning(item.uid)) {
holder.binding.ivPlay.setImageResource(R.drawable.ic_stop)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStop)
}
} else {
holder.binding.ivPlay.setImageResource(R.drawable.ic_start)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStart)
}
}
holder.binding.ivCopy.setImageResource(R.drawable.ic_copy)
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
holder.binding.ivCopy.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivPlay.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivEdit.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivDelete.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
}
}
class MyViewHolder(val binding: AdapterFrpcsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
interface OnItemClickListener {
fun onItemClicked(view: View?, item: Frpc)
fun onItemRemove(view: View?, id: Int)
}
companion object {
var diffCallback: DiffUtil.ItemCallback<Frpc> = object : DiffUtil.ItemCallback<Frpc>() {
override fun areItemsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
return oldItem.uid == newItem.uid
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
return oldItem === newItem
}
}
}
}

@ -1,39 +1,42 @@
package com.idormy.sms.forwarder.database.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.idormy.sms.forwarder.database.entity.Frpc
import io.reactivex.Single
@Dao
interface FrpcDao {
@Insert
fun insert(frpc: Frpc)
@Delete
fun delete(frpc: Frpc)
@Query("DELETE FROM Frpc where uid=:uid")
fun delete(uid: String)
@Update
fun update(frpc: Frpc)
@Query("SELECT * FROM Frpc where uid=:uid")
fun get(uid: String): Single<Frpc>
//TODO:允许主线程访问,后面再优化
@Query("SELECT * FROM Frpc where autorun=1")
fun getAutorun(): List<Frpc>
@Query("SELECT * FROM Frpc ORDER BY time DESC")
fun pagingSource(): PagingSource<Int, Frpc>
//TODO:允许主线程访问,后面再优化
@Query("SELECT * FROM Frpc ORDER BY time ASC")
fun getAll(): List<Frpc>
@Query("DELETE FROM Frpc")
fun deleteAll()
package com.idormy.sms.forwarder.database.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.idormy.sms.forwarder.database.entity.Frpc
import io.reactivex.Single
@Dao
interface FrpcDao {
@Insert
fun insert(frpc: Frpc)
@Delete
fun delete(frpc: Frpc)
@Query("DELETE FROM Frpc where uid=:uid")
fun delete(uid: String)
@Update
fun update(frpc: Frpc)
@Query("SELECT * FROM Frpc where uid=:uid")
fun get(uid: String): Single<Frpc>
//TODO:允许主线程访问,后面再优化
@Query("SELECT * FROM Frpc where uid=:uid")
fun getOne(uid: String): Frpc
@Query("SELECT * FROM Frpc where autorun=1")
fun getAutorun(): List<Frpc>
@Query("SELECT * FROM Frpc ORDER BY time DESC")
fun pagingSource(): PagingSource<Int, Frpc>
//TODO:允许主线程访问,后面再优化
@Query("SELECT * FROM Frpc ORDER BY time ASC")
fun getAll(): List<Frpc>
@Query("DELETE FROM Frpc")
fun deleteAll()
}

@ -9,84 +9,130 @@ import java.io.Serializable
data class CloneInfo(
@SerializedName("version_code")
var versionCode: Int = 0,
@SerializedName("version_name")
var versionName: String? = null,
@SerializedName("enable_sms")
var enableSms: Boolean = false,
@SerializedName("enable_phone")
var enablePhone: Boolean = false,
@SerializedName("call_type1")
var callType1: Boolean = false,
@SerializedName("call_type2")
var callType2: Boolean = false,
@SerializedName("call_type3")
var callType3: Boolean = false,
@SerializedName("call_type4")
var callType4: Boolean = false,
@SerializedName("call_type5")
var callType5: Boolean = false,
@SerializedName("call_type6")
var callType6: Boolean = false,
@SerializedName("enable_app_notify")
var enableAppNotify: Boolean = false,
@SerializedName("cancel_app_notify")
var cancelAppNotify: Boolean = false,
@SerializedName("cancel_extra_app_notify")
var cancelExtraAppNotify: String? = null,
@SerializedName("enable_not_user_present")
var enableNotUserPresent: Boolean = false,
@SerializedName("enable_load_app_list")
var enableLoadAppList: Boolean = false,
@SerializedName("enable_load_user_app_list")
var enableLoadUserAppList: Boolean = false,
@SerializedName("enable_load_system_app_list")
var enableLoadSystemAppList: Boolean = false,
@SerializedName("duplicate_messages_limits")
var duplicateMessagesLimits: Int = 0,
@SerializedName("enable_network_state_receiver")
var enableNetworkStateReceiver: Boolean = false,
@SerializedName("enable_battery_receiver")
var enableBatteryReceiver: Boolean = false,
@SerializedName("battery_level_min")
var batteryLevelMin: Int = 0,
@SerializedName("battery_level_max")
var batteryLevelMax: Int = 0,
@SerializedName("battery_level_once")
var batteryLevelOnce: Boolean = false,
@SerializedName("enable_battery_cron")
var enableBatteryCron: Boolean = false,
@SerializedName("battery_cron_start_time")
var batteryCronStartTime: String? = null,
@SerializedName("battery_cron_interval")
var batteryCronInterval: Int = 0,
@SerializedName("enable_exclude_from_recents")
var enableExcludeFromRecents: Boolean = false,
@SerializedName("enable_cactus")
var enableCactus: Boolean = false,
@SerializedName("enable_play_silence_music")
var enablePlaySilenceMusic: Boolean = false,
@SerializedName("enable_one_pixel_activity")
var enableOnePixelActivity: Boolean = false,
@SerializedName("request_retry_times")
var requestRetryTimes: Int = 0,
@SerializedName("request_delay_time")
var requestDelayTime: Int = 0,
@SerializedName("request_timeout")
var requestTimeout: Int = 0,
@SerializedName("notify_content")
var notifyContent: String? = null,
@SerializedName("enable_sms_template")
var enableSmsTemplate: Boolean = false,
@SerializedName("sms_template")
var smsTemplate: String? = null,
@SerializedName("enable_help_tip")
var enableHelpTip: Boolean = false,
@SerializedName("enable_pure_client_mode")
var enablePureClientMode: Boolean = false,
@SerializedName("enable_sms_command")
var enableSmsCommand: Boolean = false,
@SerializedName("sms_command_safe_phone")
var smsCommandSafePhone: String? = null,
@SerializedName("sender_list")
var senderList: List<Sender>? = null,
@SerializedName("rule_list")
var ruleList: List<Rule>? = null,
@SerializedName("frpc_list")
var frpcList: List<Frpc>? = null,
) : Serializable

@ -1,202 +1,203 @@
package com.idormy.sms.forwarder.fragment
import android.content.Intent
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import com.alibaba.android.vlayout.VirtualLayoutManager
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.FrpcPagingAdapter
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
import com.idormy.sms.forwarder.databinding.FragmentFrpcsBinding
import com.idormy.sms.forwarder.service.ForegroundService
import com.idormy.sms.forwarder.utils.*
import com.jeremyliao.liveeventbus.LiveEventBus
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xpage.base.XPageActivity
import com.xuexiang.xpage.core.PageOption
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.LoadingDialog
import frpclib.Frpclib
import io.reactivex.CompletableObserver
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@Suppress("DEPRECATION")
@Page(name = "Frp内网穿透")
class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.OnItemClickListener {
var titleBar: TitleBar? = null
private var adapter = FrpcPagingAdapter(this)
private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) }
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentFrpcsBinding {
return FragmentFrpcsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_frpc)
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_add) {
@SingleClick
override fun performAction(view: View) {
FrpcUtils.getStringFromRaw(context!!, R.raw.frpc)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<String?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(content: String) {
LiveEventBus.get<Frpc>(INTENT_FRPC_EDIT_FILE).post(Frpc(content))
PageOption.to(FrpcEditFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
}
override fun onError(e: Throwable) {
e.message?.let { XToastUtils.error(it) }
}
override fun onComplete() {}
})
}
})
return titleBar
}
/**
* 初始化控件
*/
override fun initViews() {
val virtualLayoutManager = VirtualLayoutManager(requireContext())
binding!!.recyclerView.layoutManager = virtualLayoutManager
val viewPool = RecycledViewPool()
binding!!.recyclerView.setRecycledViewPool(viewPool)
viewPool.setMaxRecycledViews(0, 10)
binding!!.recyclerView.adapter = adapter
}
override fun initListeners() {
//下拉刷新
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
//adapter.refresh()
lifecycleScope.launch {
viewModel.allFrpc.collectLatest { adapter.submitData(it) }
}
refreshLayout.finishRefresh()
}
binding!!.refreshLayout.autoRefresh()
//更新时间
LiveEventBus.get(EVENT_FRPC_UPDATE_CONFIG, Frpc::class.java).observe(this) {
adapter.refresh()
}
//删除事件
LiveEventBus.get(EVENT_FRPC_DELETE_CONFIG, Frpc::class.java).observe(this) {
adapter.refresh()
}
//运行出错时间
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).observe(this) {
XToastUtils.error(getString(R.string.frpc_failed_to_run))
//FrpcUtils.checkAndStopService(requireContext())
adapter.refresh()
}
//运行成功
LiveEventBus.get(EVENT_FRPC_RUNNING_SUCCESS, String::class.java).observe(this) {
adapter.refresh()
}
}
override fun onItemClicked(view: View?, item: Frpc) {
val id = view?.id
if (id == R.id.iv_play) {
//if (!FrpcUtils.isServiceRunning(ForegroundService::class.java.name, requireContext())) {
if (!ForegroundService.isRunning) {
val intent = Intent(requireContext(), ForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requireContext().startForegroundService(intent)
} else {
requireContext().startService(intent)
}
}
if (Frpclib.isRunning(item.uid)) {
Frpclib.close(item.uid)
item.setConnecting(false)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
//FrpcUtils.checkAndStopService(requireContext())
return
}
FrpcUtils.waitService(ForegroundService::class.java.name, requireContext())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CompletableObserver {
var mLoadingDialog: LoadingDialog = WidgetUtils.getLoadingDialog(context!!).setIconScale(0.4f).setLoadingSpeed(8)
override fun onSubscribe(d: Disposable) {
mLoadingDialog.setLoadingIcon(R.drawable.ic_menu_frpc)
mLoadingDialog.updateMessage(R.string.tipWaitService)
mLoadingDialog.show()
}
override fun onComplete() {
mLoadingDialog.dismiss()
mLoadingDialog.recycle()
LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE).postAcrossProcess(item.uid)
item.setConnecting(true)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
}
override fun onError(e: Throwable) {
mLoadingDialog.dismiss()
mLoadingDialog.recycle()
e.message?.let { XToastUtils.error(it) }
item.setConnecting(false)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
}
})
} else {
//编辑或删除需要先停止客户端
if (Frpclib.isRunning(item.uid)) {
XToastUtils.warning(R.string.tipServiceRunning)
return
}
if (id == R.id.iv_edit) {
LiveEventBus.get<Frpc>(INTENT_FRPC_EDIT_FILE).post(item)
openNewPage(FrpcEditFragment::class.java)
} else if (id == R.id.iv_delete) {
try {
viewModel.delete(item)
LiveEventBus.get<Frpc>(EVENT_FRPC_DELETE_CONFIG).post(item)
XToastUtils.success(getString(R.string.successfully_deleted))
} catch (e: Exception) {
e.message?.let { XToastUtils.error(it) }
}
}
}
}
override fun onItemRemove(view: View?, id: Int) {}
package com.idormy.sms.forwarder.fragment
import android.content.Intent
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import com.alibaba.android.vlayout.VirtualLayoutManager
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.FrpcPagingAdapter
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
import com.idormy.sms.forwarder.databinding.FragmentFrpcsBinding
import com.idormy.sms.forwarder.service.ForegroundService
import com.idormy.sms.forwarder.utils.*
import com.jeremyliao.liveeventbus.LiveEventBus
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xpage.base.XPageActivity
import com.xuexiang.xpage.core.PageOption
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.LoadingDialog
import com.xuexiang.xutil.system.ClipboardUtils
import frpclib.Frpclib
import io.reactivex.CompletableObserver
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@Suppress("DEPRECATION")
@Page(name = "Frp内网穿透")
class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.OnItemClickListener {
var titleBar: TitleBar? = null
private var adapter = FrpcPagingAdapter(this)
private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) }
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentFrpcsBinding {
return FragmentFrpcsBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_frpc)
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_add) {
@SingleClick
override fun performAction(view: View) {
FrpcUtils.getStringFromRaw(context!!, R.raw.frpc).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<String?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(content: String) {
LiveEventBus.get<Frpc>(INTENT_FRPC_EDIT_FILE).post(Frpc(content))
PageOption.to(FrpcEditFragment::class.java).setNewActivity(true).open((context as XPageActivity?)!!)
}
override fun onError(e: Throwable) {
e.message?.let { XToastUtils.error(it) }
}
override fun onComplete() {}
})
}
})
return titleBar
}
/**
* 初始化控件
*/
override fun initViews() {
val virtualLayoutManager = VirtualLayoutManager(requireContext())
binding!!.recyclerView.layoutManager = virtualLayoutManager
val viewPool = RecycledViewPool()
binding!!.recyclerView.setRecycledViewPool(viewPool)
viewPool.setMaxRecycledViews(0, 10)
binding!!.recyclerView.adapter = adapter
}
override fun initListeners() {
//下拉刷新
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
//adapter.refresh()
lifecycleScope.launch {
viewModel.allFrpc.collectLatest { adapter.submitData(it) }
}
refreshLayout.finishRefresh()
}
binding!!.refreshLayout.autoRefresh()
//更新时间
LiveEventBus.get(EVENT_FRPC_UPDATE_CONFIG, Frpc::class.java).observe(this) {
adapter.refresh()
}
//删除事件
LiveEventBus.get(EVENT_FRPC_DELETE_CONFIG, Frpc::class.java).observe(this) {
adapter.refresh()
}
//运行出错时间
LiveEventBus.get(EVENT_FRPC_RUNNING_ERROR, String::class.java).observe(this) {
XToastUtils.error(getString(R.string.frpc_failed_to_run))
adapter.refresh()
}
//运行成功
LiveEventBus.get(EVENT_FRPC_RUNNING_SUCCESS, String::class.java).observe(this) {
adapter.refresh()
}
}
override fun onItemClicked(view: View?, item: Frpc) {
when (val id = view?.id) {
R.id.iv_copy -> {
ClipboardUtils.copyText(item.uid)
XToastUtils.info(String.format(getString(R.string.copied_to_clipboard), item.uid))
}
R.id.iv_play -> {
if (!ForegroundService.isRunning) {
val intent = Intent(requireContext(), ForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requireContext().startForegroundService(intent)
} else {
requireContext().startService(intent)
}
}
if (Frpclib.isRunning(item.uid)) {
Frpclib.close(item.uid)
item.setConnecting(false)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
return
}
FrpcUtils.waitService(ForegroundService::class.java.name, requireContext()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : CompletableObserver {
var mLoadingDialog: LoadingDialog = WidgetUtils.getLoadingDialog(context!!).setIconScale(0.4f).setLoadingSpeed(8)
override fun onSubscribe(d: Disposable) {
mLoadingDialog.setLoadingIcon(R.drawable.ic_menu_frpc)
mLoadingDialog.updateMessage(R.string.tipWaitService)
mLoadingDialog.show()
}
override fun onComplete() {
mLoadingDialog.dismiss()
mLoadingDialog.recycle()
LiveEventBus.get<String>(INTENT_FRPC_APPLY_FILE).postAcrossProcess(item.uid)
item.setConnecting(true)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
}
override fun onError(e: Throwable) {
mLoadingDialog.dismiss()
mLoadingDialog.recycle()
e.message?.let { XToastUtils.error(it) }
item.setConnecting(false)
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(item)
}
})
}
else -> {
//编辑或删除需要先停止客户端
if (Frpclib.isRunning(item.uid)) {
XToastUtils.warning(R.string.tipServiceRunning)
return
}
when (id) {
R.id.iv_edit -> {
LiveEventBus.get<Frpc>(INTENT_FRPC_EDIT_FILE).post(item)
openNewPage(FrpcEditFragment::class.java)
}
R.id.iv_delete -> {
try {
viewModel.delete(item)
LiveEventBus.get<Frpc>(EVENT_FRPC_DELETE_CONFIG).post(item)
XToastUtils.success(getString(R.string.successfully_deleted))
} catch (e: Exception) {
e.message?.let { XToastUtils.error(it) }
}
}
}
}
}
}
override fun onItemRemove(view: View?, id: Int) {}
}

@ -97,6 +97,10 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
switchEnableAppNotify(
binding!!.sbEnableAppNotify, binding!!.scbCancelAppNotify, binding!!.scbNotUserPresent
)
//短信指令
switchEnableSmsCommand(binding!!.sbEnableSmsCommand, binding!!.etSafePhone)
//设置自动消除额外APP通知
editExtraAppList(binding!!.etAppList)
//启动时异步获取已安装App信息
@ -440,12 +444,12 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
val layoutOptionalAction: LinearLayout = binding!!.layoutOptionalAction
layoutOptionalAction.visibility = if (isEnable) View.VISIBLE else View.GONE
val layoutAppList: LinearLayout = binding!!.layoutAppList
layoutAppList.visibility = if (isEnable) View.VISIBLE else View.GONE
//val layoutAppList: LinearLayout = binding!!.layoutAppList
//layoutAppList.visibility = if (isEnable) View.VISIBLE else View.GONE
sbEnableAppNotify.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
layoutOptionalAction.visibility = if (isChecked) View.VISIBLE else View.GONE
layoutAppList.visibility = if (isChecked) View.VISIBLE else View.GONE
//layoutAppList.visibility = if (isChecked) View.VISIBLE else View.GONE
SettingUtils.enableAppNotify = isChecked
if (isChecked) {
//检查权限是否获取
@ -473,6 +477,57 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
}
}
//接受短信指令
@SuppressLint("UseSwitchCompatOrMaterialCode")
fun switchEnableSmsCommand(sbEnableSmsCommand: SwitchButton, etSafePhone: EditText) {
sbEnableSmsCommand.isChecked = SettingUtils.enableSmsCommand
etSafePhone.visibility = if (SettingUtils.enableSmsCommand) View.VISIBLE else View.GONE
sbEnableSmsCommand.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
SettingUtils.enableSmsCommand = isChecked
etSafePhone.visibility = if (isChecked) View.VISIBLE else View.GONE
if (isChecked) {
//检查权限是否获取
XXPermissions.with(this)
// 接收短信
.permission(Permission.RECEIVE_SMS)
// 发送短信
//.permission(Permission.SEND_SMS)
// 读取短信
.permission(Permission.READ_SMS).request(object : OnPermissionCallback {
override fun onGranted(permissions: List<String>, all: Boolean) {
if (all) {
XToastUtils.info(R.string.toast_granted_all)
} else {
XToastUtils.info(R.string.toast_granted_part)
}
}
override fun onDenied(permissions: List<String>, never: Boolean) {
if (never) {
XToastUtils.info(R.string.toast_denied_never)
// 如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.startPermissionActivity(requireContext(), permissions)
} else {
XToastUtils.info(R.string.toast_denied)
}
SettingUtils.enableSmsCommand = false
sbEnableSmsCommand.isChecked = false
}
})
}
}
etSafePhone.setText(SettingUtils.smsCommandSafePhone)
etSafePhone.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) {
SettingUtils.smsCommandSafePhone = etSafePhone.text.toString().trim().removeSuffix("\n")
}
})
}
//设置自动消除额外APP通知
private fun editExtraAppList(textAppList: EditText) {
textAppList.setText(SettingUtils.cancelExtraAppNotify)

@ -10,15 +10,25 @@ import androidx.work.WorkManager
import androidx.work.workDataOf
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.database.AppDatabase
import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.service.HttpService
import com.idormy.sms.forwarder.utils.PhoneUtils
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.Worker
import com.idormy.sms.forwarder.workers.SendWorker
import com.xuexiang.xrouter.utils.TextUtils
import com.xuexiang.xutil.file.FileUtils
import frpclib.Frpclib
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import java.util.*
//短信广播
@Suppress("PrivatePropertyName", "DEPRECATION")
@OptIn(DelicateCoroutinesApi::class)
@Suppress("PrivatePropertyName", "DEPRECATION", "DeferredResultUnused", "SENSELESS_COMPARISON")
class SmsReceiver : BroadcastReceiver() {
private var TAG = "SmsReceiver"
@ -28,20 +38,25 @@ class SmsReceiver : BroadcastReceiver() {
//纯客户端模式
if (SettingUtils.enablePureClientMode) return
//总开关
if (!SettingUtils.enableSms) return
//过滤广播
if (intent.action != Telephony.Sms.Intents.SMS_RECEIVED_ACTION && intent.action != Telephony.Sms.Intents.SMS_DELIVER_ACTION) return
var from = ""
var content = ""
var message = ""
for (smsMessage in Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
from = smsMessage.displayOriginatingAddress
content += smsMessage.messageBody
message += smsMessage.messageBody
}
Log.d(TAG, "from = $from, message = $message")
//短信指令
if (SettingUtils.enableSmsCommand && message.startsWith("smsf#")) {
doSmsCommand(context, from, message)
return
}
Log.d(TAG, "from = $from")
Log.d(TAG, "content = $content")
//总开关
if (!SettingUtils.enableSms) return
//TODO准确获取卡槽信息目前测试结果只有 subscription 相对靠谱
val slot = intent.extras?.getInt("slot") ?: -1
@ -78,7 +93,7 @@ class SmsReceiver : BroadcastReceiver() {
else -> ""
}
val msgInfo = MsgInfo("sms", from, content, Date(), simInfo, simSlot, subscription)
val msgInfo = MsgInfo("sms", from, message, Date(), simInfo, simSlot, subscription)
Log.d(TAG, "msgInfo = $msgInfo")
val request = OneTimeWorkRequestBuilder<SendWorker>().setInputData(
@ -93,4 +108,97 @@ class SmsReceiver : BroadcastReceiver() {
}
}
//处理短信指令
private fun doSmsCommand(context: Context, from: String, message: String) {
var safePhone = SettingUtils.smsCommandSafePhone
Log.d(TAG, "safePhone = $safePhone")
if (!TextUtils.isEmpty(safePhone)) {
var isSafePhone = false
safePhone = safePhone.replace(";", ",").replace("", ",").replace("", ",").trim()
for (phone in safePhone.split(",")) {
if (!TextUtils.isEmpty(phone.trim()) && from.endsWith(phone.trim())) {
isSafePhone = true
break
}
}
if (!isSafePhone) {
Log.d(TAG, "from = $from is not safePhone = $safePhone")
return
}
}
val smsCommand = message.substring(5)
val cmdList = smsCommand.split("#")
Log.d(TAG, "smsCommand = $smsCommand, cmdList = $cmdList")
if (cmdList.count() < 2) return
val function = cmdList[0]
val action = cmdList[1]
val param = if (cmdList.count() > 2) cmdList[2] else ""
when (function) {
"frpc" -> {
if (!FileUtils.isFileExists(context.filesDir?.absolutePath + "/libs/libgojni.so")) {
Log.d(TAG, "还未下载Frpc库")
return
}
if (TextUtils.isEmpty(param)) {
GlobalScope.async(Dispatchers.IO) {
val frpcList = AppDatabase.getInstance(App.context).frpcDao().getAutorun()
if (frpcList.isEmpty()) {
Log.d(TAG, "没有自启动的Frpc")
return@async
}
for (frpc in frpcList) {
if (action == "start") {
if (!Frpclib.isRunning(frpc.uid)) {
val error = Frpclib.runContent(frpc.uid, frpc.config)
if (!TextUtils.isEmpty(error)) {
Log.e(TAG, error)
}
}
} else if (action == "stop") {
if (Frpclib.isRunning(frpc.uid)) {
Frpclib.close(frpc.uid)
}
}
}
}
} else {
GlobalScope.async(Dispatchers.IO) {
val frpc = AppDatabase.getInstance(App.context).frpcDao().getOne(param)
if (frpc == null) {
Log.d(TAG, "没有找到指定的Frpc")
return@async
}
if (action == "start") {
if (!Frpclib.isRunning(frpc.uid)) {
val error = Frpclib.runContent(frpc.uid, frpc.config)
if (!TextUtils.isEmpty(error)) {
Log.e(TAG, error)
}
}
} else if (action == "stop") {
if (Frpclib.isRunning(frpc.uid)) {
Frpclib.close(frpc.uid)
}
}
}
}
}
"httpserver" -> {
if (action == "start") {
context.startService(Intent(context, HttpService::class.java))
} else if (action == "stop") {
context.stopService(Intent(context, HttpService::class.java))
}
}
}
}
}

@ -48,9 +48,6 @@ class NotifyService : NotificationListenerService() {
//纯客户端模式
if (SettingUtils.enablePureClientMode) return
//总开关
if (!SettingUtils.enableAppNotify) return
//异常通知跳过
if (sbn!!.notification == null) return
if (sbn.notification.extras == null) return
@ -70,6 +67,9 @@ class NotifyService : NotificationListenerService() {
}
}
//总开关
if (!SettingUtils.enableAppNotify) return
//仅锁屏状态转发APP通知
if (SettingUtils.enableNotUserPresent && !ScreenUtils.isScreenLock()) return

@ -41,6 +41,9 @@ const val SP_ENABLE_CANCEL_APP_NOTIFY = "enable_cancel_app_notify"
const val SP_CANCEL_EXTRA_APP_NOTIFY = "cancel_extra_app_notify_list"
const val SP_ENABLE_NOT_USER_PRESENT = "enable_not_user_present"
const val SP_ENABLE_SMS_COMMAND = "enable_sms_command"
const val SP_SMS_COMMAND_SAFE_PHONE = "sms_command_safe_phone"
const val ENABLE_LOAD_APP_LIST = "enable_load_app_list"
const val ENABLE_LOAD_USER_APP_LIST = "enable_load_user_app_list"
const val ENABLE_LOAD_SYSTEM_APP_LIST = "enable_load_system_app_list"

@ -180,6 +180,8 @@ class HttpServerUtils private constructor() {
cloneInfo.smsTemplate = SettingUtils.smsTemplate
cloneInfo.enableHelpTip = SettingUtils.enableHelpTip
cloneInfo.enablePureClientMode = SettingUtils.enablePureClientMode
cloneInfo.enableSmsCommand = SettingUtils.enableSmsCommand
cloneInfo.smsCommandSafePhone = SettingUtils.smsCommandSafePhone
cloneInfo.senderList = Core.sender.all
cloneInfo.ruleList = Core.rule.all
cloneInfo.frpcList = Core.frpc.all
@ -227,6 +229,8 @@ class HttpServerUtils private constructor() {
SettingUtils.smsTemplate = cloneInfo.smsTemplate.toString()
SettingUtils.enableHelpTip = cloneInfo.enableHelpTip
SettingUtils.enablePureClientMode = cloneInfo.enablePureClientMode
SettingUtils.enableSmsCommand = cloneInfo.enableSmsCommand
SettingUtils.smsCommandSafePhone = cloneInfo.smsCommandSafePhone.toString()
//删除发送通道、转发规则、转发日志
Core.sender.deleteAll()
//发送通道

@ -39,6 +39,10 @@ class SettingUtils private constructor() {
//是否转发应用通知
var enableAppNotify: Boolean by SharedPreference(SP_ENABLE_APP_NOTIFY, false)
//是否接受短信指令
var enableSmsCommand: Boolean by SharedPreference(SP_ENABLE_SMS_COMMAND, false)
var smsCommandSafePhone: String by SharedPreference(SP_SMS_COMMAND_SAFE_PHONE, "")
//是否转发应用通知——自动消除通知
var enableCancelAppNotify: Boolean by SharedPreference(SP_ENABLE_CANCEL_APP_NOTIFY, false)

@ -1,80 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<com.xuexiang.xui.widget.layout.XUIFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view"
style="@style/XUILayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/config_margin_5dp"
android:layout_marginTop="@dimen/config_margin_5dp"
android:layout_marginEnd="@dimen/config_margin_5dp"
android:paddingStart="@dimen/config_padding_5dp"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingEnd="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_autorun"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:layout_marginTop="-16dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:lines="1"
android:paddingLeft="16dp"
android:paddingRight="16dp" />
<ImageView
android:id="@+id/iv_play"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/colorStart"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_delete"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<com.xuexiang.xui.widget.layout.XUIFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view"
style="@style/XUILayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/config_margin_5dp"
android:layout_marginTop="@dimen/config_margin_5dp"
android:layout_marginEnd="@dimen/config_margin_5dp"
android:paddingStart="@dimen/config_padding_5dp"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingEnd="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_autorun"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:layout_marginTop="-16dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:lines="1"
android:paddingEnd="15dp"
tools:ignore="RtlSymmetry" />
<ImageView
android:id="@+id/iv_play"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/colorStart"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_delete"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_copy"
android:layout_width="@dimen/config_padding_18dp"
android:layout_height="@dimen/config_padding_18dp"
app:tint="@color/colorStart"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tv_uid"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:lines="1"
android:textSize="11sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.xuexiang.xui.widget.layout.XUIFrameLayout>

@ -8,6 +8,7 @@
android:orientation="vertical"
tools:ignore="TooManyViews">
<androidx.core.widget.NestedScrollView style="@style/ScrollViewStyle">
<LinearLayout
@ -242,142 +243,148 @@
style="@style/settingBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="0dp"
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
android:text="@string/forward_app_notify"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/forward_app_notify_tips"
android:textSize="9sp"
tools:ignore="SmallSp" />
<LinearLayout
android:id="@+id/layout_optional_action"
android:layout_width="match_parent"
android:layout_height="25dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/forward_app_notify"
android:text="@string/optional_action"
android:textSize="10sp"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_cancel_app_notify"
android:layout_width="15dp"
android:layout_height="15dp"
app:scb_color_checked="@color/colorPrimary" />
<TextView
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/forward_app_notify_tips"
android:textSize="9sp"
android:text="@string/cancel_app_notify"
android:textSize="10sp"
tools:ignore="SmallSp" />
<LinearLayout
android:id="@+id/layout_optional_action"
android:layout_width="match_parent"
android:layout_height="25dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_not_user_present"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/optional_action"
android:textSize="10sp"
android:textStyle="bold"
tools:ignore="SmallSp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_user_present"
android:textSize="10sp"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_cancel_app_notify"
android:layout_width="15dp"
android:layout_height="15dp"
app:scb_color_checked="@color/colorPrimary" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel_app_notify"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/scb_not_user_present"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_app_notify"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_user_present"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/extra_function"
android:textStyle="bold" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_app_notify"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/extra_function_tips"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
style="@style/settingBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<LinearLayout
android:id="@+id/layout_app_list"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingEnd="10dp"
android:visibility="gone"
tools:ignore="RtlSymmetry">
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/extra_app"
android:textSize="12sp"
android:textStyle="bold" />
android:text="@string/sms_command"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
android:id="@+id/et_app_list"
android:layout_width="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/extra_app_hint"
android:inputType="textMultiLine"
app:met_clearButton="true" />
android:text="@string/sms_command_tips"
android:textSize="9sp"
tools:ignore="SmallSp" />
<LinearLayout
android:id="@+id/layout_sp_app"
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_safe_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/choose_app"
android:textSize="12sp"
android:textStyle="bold" />
<com.xuexiang.xui.widget.spinner.editspinner.EditSpinner
android:id="@+id/sp_app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
app:es_hint="@string/choose_app_hint"
app:es_maxLength="20"
app:es_maxLine="1" />
</LinearLayout>
android:hint="@string/safe_phone_tips"
android:visibility="gone" />
</LinearLayout>
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_sms_command"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
@ -459,6 +466,58 @@
</LinearLayout>
<LinearLayout
android:id="@+id/layout_app_list"
style="@style/settingBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/extra_app"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
android:id="@+id/et_app_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/extra_app_hint"
android:inputType="textMultiLine"
app:met_clearButton="true" />
<LinearLayout
android:id="@+id/layout_sp_app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/choose_app"
android:textSize="12sp"
android:textStyle="bold" />
<com.xuexiang.xui.widget.spinner.editspinner.EditSpinner
android:id="@+id/sp_app"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
app:es_hint="@string/choose_app_hint"
app:es_maxLength="20"
app:es_maxLine="1" />
</LinearLayout>
</LinearLayout>
<LinearLayout
style="@style/settingBarStyle"
android:layout_width="match_parent"

@ -353,6 +353,10 @@
<string name="filtering_duplicate_messages_tips">0=disabled, judge duplicate: type+source+content</string>
<string name="forward_sms">Forward Sms</string>
<string name="forward_sms_tips">Main switch, requires permissions to read and sned SMS messages, especially verification SMS texts.</string>
<string name="sms_command">Sms Command</string>
<string name="sms_command_tips">Open the HttpServer or FRPC by the SMS command</string>
<string name="safe_phone">Safe Phone</string>
<string name="safe_phone_tips">Only handle requests from specified phones</string>
<string name="forward_missed_calls">Forward Calls Log</string>
<string name="forward_missed_calls_tips">Main switch, requires permissions to read call log and contacts.</string>
<string name="forward_app_notify">Forward App Notify</string>
@ -524,6 +528,8 @@
<string name="todo">TODO</string>
<string name="forwarding_function">Forwarding Function</string>
<string name="forwarding_function_tips">Main switch: Enable the forwarding function as required</string>
<string name="extra_function">Extra Function</string>
<string name="extra_function_tips">Enable the extra function as required</string>
<string name="call_date">Call date: </string>
<string name="call_duration">Call duration: </string>
<string name="ring_duration">Ring duration: </string>

@ -354,6 +354,10 @@
<string name="filtering_duplicate_messages_tips">0=禁用,判断重复:类型+来源+内容</string>
<string name="forward_sms">转发短信广播</string>
<string name="forward_sms_tips">请授予读取短信、通知类短信、发送短信等权限,关闭验证码保护</string>
<string name="sms_command">短信指令</string>
<string name="sms_command_tips">根据短信指令开关对应功能指令格式smsf#功能名#动作名</string>
<string name="safe_phone">安全手机</string>
<string name="safe_phone_tips">仅处理指定手机请求,多个手机以逗号分隔</string>
<string name="forward_missed_calls">转发通话记录</string>
<string name="forward_missed_calls_tips">请授予读取通话记录、联系人等权限,并选择转发类型,再开启</string>
<string name="forward_app_notify">转发应用通知</string>
@ -525,6 +529,8 @@
<string name="todo">TODO</string>
<string name="forwarding_function">转发功能</string>
<string name="forwarding_function_tips">总开关,请根据实际需要,启用对应的转发功能</string>
<string name="extra_function">增强功能</string>
<string name="extra_function_tips">请根据实际需要,启用对应的增强设置</string>
<string name="call_date">通话时间:</string>
<string name="call_duration">通话时长:</string>
<string name="ring_duration">响铃时长:</string>
@ -544,11 +550,11 @@
<string name="httpserver">被动接收本地 HttpServer</string>
<string name="httpserver_tips">WiFi网络下可用启动后局域网内其他机器可直接调用本机接口</string>
<string name="network_state_monitor">网络状态监控</string>
<string name="network_state_monitor_tips">【注意】需要手动创建APP转发规则包名77777777</string>
<string name="network_state_monitor_tips">需要手动创建APP转发规则包名77777777</string>
<string name="network_state_change_remind">网络状态改变提醒</string>
<string name="network_state_change_remind_tips">网络状态改变(连接方式/IP变化)时发出通知</string>
<string name="battery_monitor">电池监控</string>
<string name="battery_monitor_tips">【注意】需要手动创建APP转发规则包名88888888</string>
<string name="battery_monitor_tips">需要手动创建APP转发规则包名88888888</string>
<string name="keep_alive">保活措施</string>
<string name="keep_alive_tips">建议开启前三项授权或设置不要禁用通知栏避免APP被杀</string>
<string name="custom_settings">个性设置</string>

Loading…
Cancel
Save