parent
b4870207d1
commit
b79d3d8493
@ -1,434 +1,434 @@
|
||||
package com.idormy.sms.forwarder.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.gyf.cactus.ext.cactusUpdateNotification
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseActivity
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.database.AppDatabase
|
||||
import com.idormy.sms.forwarder.databinding.ActivityMainBinding
|
||||
import com.idormy.sms.forwarder.fragment.*
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit
|
||||
import com.idormy.sms.forwarder.widget.GuideTipsDialog.Companion.showTips
|
||||
import com.idormy.sms.forwarder.widget.GuideTipsDialog.Companion.showTipsForce
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.callback.DownloadProgressCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xpage.model.PageInfo
|
||||
import com.xuexiang.xui.adapter.FragmentAdapter
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.DensityUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.WidgetUtils
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.GravityEnum
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import com.xuexiang.xutil.net.NetworkUtils
|
||||
import frpclib.Frpclib
|
||||
import io.reactivex.CompletableObserver
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.io.File
|
||||
|
||||
|
||||
@Suppress("DEPRECATION", "PrivatePropertyName")
|
||||
class MainActivity : BaseActivity<ActivityMainBinding?>(),
|
||||
View.OnClickListener,
|
||||
BottomNavigationView.OnNavigationItemSelectedListener,
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
|
||||
private val TAG: String = MainActivity::class.java.simpleName
|
||||
private lateinit var mTitles: Array<String>
|
||||
private var logsType: String = "sms"
|
||||
private var ruleType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(inflater: LayoutInflater?): ActivityMainBinding {
|
||||
return ActivityMainBinding.inflate(inflater!!)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initViews()
|
||||
initData()
|
||||
initListeners()
|
||||
|
||||
//不在最近任务列表中显示
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SettingUtils.enableExcludeFromRecents) {
|
||||
val am = App.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
am.let {
|
||||
val tasks = it.appTasks
|
||||
if (!tasks.isNullOrEmpty()) {
|
||||
tasks[0].setExcludeFromRecents(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val isSupportSlideBack: Boolean
|
||||
get() = false
|
||||
|
||||
private fun initViews() {
|
||||
WidgetUtils.clearActivityBackground(this)
|
||||
mTitles = ResUtils.getStringArray(R.array.home_titles)
|
||||
binding!!.includeMain.toolbar.title = mTitles[0]
|
||||
binding!!.includeMain.toolbar.inflateMenu(R.menu.menu_logs)
|
||||
binding!!.includeMain.toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
//主页内容填充
|
||||
val fragments = arrayOf(
|
||||
LogsFragment(),
|
||||
RulesFragment(),
|
||||
SendersFragment(),
|
||||
SettingsFragment()
|
||||
)
|
||||
val adapter = FragmentAdapter(supportFragmentManager, fragments)
|
||||
binding!!.includeMain.viewPager.offscreenPageLimit = mTitles.size - 1
|
||||
binding!!.includeMain.viewPager.adapter = adapter
|
||||
|
||||
if (!SettingUtils.enableHelpTip) {
|
||||
val headerView = binding!!.navView.getHeaderView(0)
|
||||
val tvSlogan = headerView.findViewById<TextView>(R.id.tv_slogan)
|
||||
tvSlogan.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
//仅当有WIFI网络时自动检查更新/获取提示
|
||||
if (NetworkUtils.isWifi() && NetworkUtils.isHaveInternet()) {
|
||||
showTips(this)
|
||||
XUpdateInit.checkUpdate(this, false)
|
||||
}
|
||||
}
|
||||
|
||||
fun initListeners() {
|
||||
val toggle = ActionBarDrawerToggle(
|
||||
this,
|
||||
binding!!.drawerLayout,
|
||||
binding!!.includeMain.toolbar,
|
||||
R.string.navigation_drawer_open,
|
||||
R.string.navigation_drawer_close
|
||||
)
|
||||
binding!!.drawerLayout.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
//侧边栏点击事件
|
||||
binding!!.navView.setNavigationItemSelectedListener { menuItem: MenuItem ->
|
||||
if (menuItem.isCheckable) {
|
||||
binding!!.drawerLayout.closeDrawers()
|
||||
return@setNavigationItemSelectedListener handleNavigationItemSelected(menuItem)
|
||||
} else {
|
||||
when (menuItem.itemId) {
|
||||
R.id.nav_server -> openNewPage(ServerFragment::class.java)
|
||||
R.id.nav_client -> openNewPage(ClientFragment::class.java)
|
||||
R.id.nav_frpc -> {
|
||||
if (!FileUtils.isFileExists(filesDir.absolutePath + "/libs/libgojni.so")) {
|
||||
MaterialDialog.Builder(this)
|
||||
.title(
|
||||
String.format(
|
||||
getString(R.string.frpclib_download_title),
|
||||
FRPC_LIB_VERSION
|
||||
)
|
||||
)
|
||||
.content(R.string.download_frpc_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
downloadFrpcLib()
|
||||
}
|
||||
.show()
|
||||
return@setNavigationItemSelectedListener false
|
||||
}
|
||||
|
||||
if (FRPC_LIB_VERSION == Frpclib.getVersion()) {
|
||||
openNewPage(FrpcFragment::class.java)
|
||||
} else {
|
||||
MaterialDialog.Builder(this)
|
||||
.title(R.string.frpclib_version_mismatch)
|
||||
.content(R.string.download_frpc_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
downloadFrpcLib()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
R.id.nav_app_list -> openNewPage(AppListFragment::class.java)
|
||||
R.id.nav_logcat -> openNewPage(LogcatFragment::class.java)
|
||||
R.id.nav_help -> AgentWebActivity.goWeb(this, getString(R.string.url_help))
|
||||
R.id.nav_about -> openNewPage(AboutFragment::class.java)
|
||||
else -> XToastUtils.toast("Click:" + menuItem.title)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
//主页事件监听
|
||||
binding!!.includeMain.viewPager.addOnPageChangeListener(object :
|
||||
ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int,
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
val item = binding!!.includeMain.bottomNavigation.menu.getItem(position)
|
||||
binding!!.includeMain.toolbar.title = item.title
|
||||
binding!!.includeMain.toolbar.menu.clear()
|
||||
when (item.title) {
|
||||
getString(R.string.menu_rules) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_rules
|
||||
)
|
||||
getString(R.string.menu_senders) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_senders
|
||||
)
|
||||
getString(R.string.menu_settings) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_settings
|
||||
)
|
||||
else -> binding!!.includeMain.toolbar.inflateMenu(R.menu.menu_logs)
|
||||
}
|
||||
item.isChecked = true
|
||||
updateSideNavStatus(item)
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {}
|
||||
})
|
||||
binding!!.includeMain.bottomNavigation.setOnNavigationItemSelectedListener(this)
|
||||
|
||||
//tabBar分类切换
|
||||
LiveEventBus.get(EVENT_UPDATE_LOGS_TYPE, String::class.java).observe(this) { type: String ->
|
||||
logsType = type
|
||||
}
|
||||
LiveEventBus.get(EVENT_UPDATE_RULE_TYPE, String::class.java).observe(this) { type: String ->
|
||||
ruleType = type
|
||||
}
|
||||
|
||||
//更新通知栏文案
|
||||
LiveEventBus.get(EVENT_UPDATE_NOTIFY, String::class.java).observe(this) { notify: String ->
|
||||
cactusUpdateNotification {
|
||||
setContent(notify)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理侧边栏点击事件
|
||||
*
|
||||
* @param menuItem
|
||||
* @return
|
||||
*/
|
||||
private fun handleNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||
for (index in mTitles.indices) {
|
||||
if (mTitles[index] == menuItem.title) {
|
||||
binding!!.includeMain.toolbar.title = menuItem.title
|
||||
binding!!.includeMain.viewPager.setCurrentItem(index, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_notifications -> {
|
||||
showTipsForce(this)
|
||||
}
|
||||
R.id.action_clear_logs -> {
|
||||
MaterialDialog.Builder(this)
|
||||
.content(R.string.delete_type_log_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
AppDatabase.getInstance(this)
|
||||
.logsDao()
|
||||
.deleteAll(logsType)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : CompletableObserver {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onComplete() {
|
||||
XToastUtils.success(R.string.delete_type_log_toast)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.message?.let { XToastUtils.error(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.action_add_sender -> {
|
||||
val dialog = BottomSheetDialog(this)
|
||||
val view: View =
|
||||
LayoutInflater.from(this).inflate(R.layout.dialog_sender_bottom_sheet, null)
|
||||
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
|
||||
|
||||
WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f))
|
||||
val widgetItemAdapter = WidgetItemAdapter(SENDER_FRAGMENT_LIST)
|
||||
widgetItemAdapter.setOnItemClickListener(this)
|
||||
recyclerView.adapter = widgetItemAdapter
|
||||
|
||||
dialog.setContentView(view)
|
||||
dialog.setCancelable(true)
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
dialog.show()
|
||||
WidgetUtils.transparentBottomSheetDialogBackground(dialog)
|
||||
}
|
||||
R.id.action_add_rule -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.putString(KEY_RULE_TYPE, ruleType)
|
||||
.setNewActivity(true)
|
||||
.open(this)
|
||||
}
|
||||
/*R.id.action_restore_settings -> {
|
||||
XToastUtils.success(logsType)
|
||||
}*/
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
}
|
||||
|
||||
//================Navigation================//
|
||||
/**
|
||||
* 底部导航栏点击事件
|
||||
*
|
||||
* @param menuItem
|
||||
* @return
|
||||
*/
|
||||
override fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||
for (index in mTitles.indices) {
|
||||
if (mTitles[index] == menuItem.title) {
|
||||
binding!!.includeMain.toolbar.title = menuItem.title
|
||||
binding!!.includeMain.viewPager.setCurrentItem(index, false)
|
||||
updateSideNavStatus(menuItem)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新侧边栏菜单选中状态
|
||||
*
|
||||
* @param menuItem
|
||||
*/
|
||||
private fun updateSideNavStatus(menuItem: MenuItem) {
|
||||
val side = binding!!.navView.menu.findItem(menuItem.itemId)
|
||||
if (side != null) {
|
||||
side.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
//按返回键不退出回到桌面
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onBackPressed() {
|
||||
val intent = Intent(Intent.ACTION_MAIN)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.addCategory(Intent.CATEGORY_HOME)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onItemClick(itemView: View, widgetInfo: PageInfo, pos: Int) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true)
|
||||
.putInt(KEY_SENDER_TYPE, pos) //注意:目前刚好是这个顺序而已
|
||||
.open(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//动态加载FrpcLib
|
||||
private fun downloadFrpcLib() {
|
||||
val cpuAbi = when (Build.CPU_ABI) {
|
||||
"x86" -> "x86"
|
||||
"x86_64" -> "x86_64"
|
||||
"arm64-v8a" -> "arm64-v8a"
|
||||
else -> "armeabi-v7a"
|
||||
}
|
||||
|
||||
val libPath = filesDir.absolutePath + "/libs"
|
||||
val soFile = File(libPath)
|
||||
if (!soFile.exists()) soFile.mkdirs()
|
||||
val downloadUrl = String.format(FRPC_LIB_DOWNLOAD_URL, FRPC_LIB_VERSION, cpuAbi)
|
||||
val mContext = this
|
||||
val dialog: MaterialDialog = MaterialDialog.Builder(mContext)
|
||||
.title(String.format(getString(R.string.frpclib_download_title), FRPC_LIB_VERSION))
|
||||
.content(getString(R.string.frpclib_download_content))
|
||||
.contentGravity(GravityEnum.CENTER)
|
||||
.progress(false, 0, true)
|
||||
.progressNumberFormat("%2dMB/%1dMB")
|
||||
.build()
|
||||
|
||||
XHttp.downLoad(downloadUrl)
|
||||
.savePath(cacheDir.absolutePath)
|
||||
.execute(object : DownloadProgressCallBack<String?>() {
|
||||
override fun onStart() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
dialog.dismiss()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
|
||||
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||
Log.d(TAG, "onProgress: bytesRead=$bytesRead, contentLength=$contentLength")
|
||||
dialog.maxProgress = (contentLength / 1048576L).toInt()
|
||||
dialog.setProgress((bytesRead / 1048576L).toInt())
|
||||
}
|
||||
|
||||
override fun onComplete(srcPath: String) {
|
||||
dialog.dismiss()
|
||||
Log.d(TAG, "srcPath = $srcPath")
|
||||
|
||||
val srcFile = File(srcPath)
|
||||
val destFile = File("$libPath/libgojni.so")
|
||||
FileUtils.moveFile(srcFile, destFile, null)
|
||||
|
||||
val intent: Intent? = packageManager.getLaunchIntentForPackage(packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
android.os.Process.killProcess(android.os.Process.myPid()) //杀掉以前进程
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.gyf.cactus.ext.cactusUpdateNotification
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseActivity
|
||||
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
|
||||
import com.idormy.sms.forwarder.database.AppDatabase
|
||||
import com.idormy.sms.forwarder.databinding.ActivityMainBinding
|
||||
import com.idormy.sms.forwarder.fragment.*
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit
|
||||
import com.idormy.sms.forwarder.widget.GuideTipsDialog.Companion.showTips
|
||||
import com.idormy.sms.forwarder.widget.GuideTipsDialog.Companion.showTipsForce
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xhttp2.XHttp
|
||||
import com.xuexiang.xhttp2.callback.DownloadProgressCallBack
|
||||
import com.xuexiang.xhttp2.exception.ApiException
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xpage.model.PageInfo
|
||||
import com.xuexiang.xui.adapter.FragmentAdapter
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||
import com.xuexiang.xui.utils.DensityUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.WidgetUtils
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.GravityEnum
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import com.xuexiang.xutil.net.NetworkUtils
|
||||
import frpclib.Frpclib
|
||||
import io.reactivex.CompletableObserver
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.io.File
|
||||
|
||||
|
||||
@Suppress("DEPRECATION", "PrivatePropertyName")
|
||||
class MainActivity : BaseActivity<ActivityMainBinding?>(),
|
||||
View.OnClickListener,
|
||||
BottomNavigationView.OnNavigationItemSelectedListener,
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
RecyclerViewHolder.OnItemClickListener<PageInfo> {
|
||||
|
||||
private val TAG: String = MainActivity::class.java.simpleName
|
||||
private lateinit var mTitles: Array<String>
|
||||
private var logsType: String = "sms"
|
||||
private var ruleType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(inflater: LayoutInflater?): ActivityMainBinding {
|
||||
return ActivityMainBinding.inflate(inflater!!)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initViews()
|
||||
initData()
|
||||
initListeners()
|
||||
|
||||
//不在最近任务列表中显示
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SettingUtils.enableExcludeFromRecents) {
|
||||
val am = App.context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
am.let {
|
||||
val tasks = it.appTasks
|
||||
if (!tasks.isNullOrEmpty()) {
|
||||
tasks[0].setExcludeFromRecents(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val isSupportSlideBack: Boolean
|
||||
get() = false
|
||||
|
||||
private fun initViews() {
|
||||
WidgetUtils.clearActivityBackground(this)
|
||||
mTitles = ResUtils.getStringArray(R.array.home_titles)
|
||||
binding!!.includeMain.toolbar.title = mTitles[0]
|
||||
binding!!.includeMain.toolbar.inflateMenu(R.menu.menu_logs)
|
||||
binding!!.includeMain.toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
//主页内容填充
|
||||
val fragments = arrayOf(
|
||||
LogsFragment(),
|
||||
RulesFragment(),
|
||||
SendersFragment(),
|
||||
SettingsFragment()
|
||||
)
|
||||
val adapter = FragmentAdapter(supportFragmentManager, fragments)
|
||||
binding!!.includeMain.viewPager.offscreenPageLimit = mTitles.size - 1
|
||||
binding!!.includeMain.viewPager.adapter = adapter
|
||||
|
||||
if (!SettingUtils.enableHelpTip) {
|
||||
val headerView = binding!!.navView.getHeaderView(0)
|
||||
val tvSlogan = headerView.findViewById<TextView>(R.id.tv_slogan)
|
||||
tvSlogan.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
//仅当有WIFI网络时自动检查更新/获取提示
|
||||
if (NetworkUtils.isWifi() && NetworkUtils.isHaveInternet()) {
|
||||
showTips(this)
|
||||
XUpdateInit.checkUpdate(this, false)
|
||||
}
|
||||
}
|
||||
|
||||
fun initListeners() {
|
||||
val toggle = ActionBarDrawerToggle(
|
||||
this,
|
||||
binding!!.drawerLayout,
|
||||
binding!!.includeMain.toolbar,
|
||||
R.string.navigation_drawer_open,
|
||||
R.string.navigation_drawer_close
|
||||
)
|
||||
binding!!.drawerLayout.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
//侧边栏点击事件
|
||||
binding!!.navView.setNavigationItemSelectedListener { menuItem: MenuItem ->
|
||||
if (menuItem.isCheckable) {
|
||||
binding!!.drawerLayout.closeDrawers()
|
||||
return@setNavigationItemSelectedListener handleNavigationItemSelected(menuItem)
|
||||
} else {
|
||||
when (menuItem.itemId) {
|
||||
R.id.nav_server -> openNewPage(ServerFragment::class.java)
|
||||
R.id.nav_client -> openNewPage(ClientFragment::class.java)
|
||||
R.id.nav_frpc -> {
|
||||
if (!FileUtils.isFileExists(filesDir.absolutePath + "/libs/libgojni.so")) {
|
||||
MaterialDialog.Builder(this)
|
||||
.title(
|
||||
String.format(
|
||||
getString(R.string.frpclib_download_title),
|
||||
FRPC_LIB_VERSION
|
||||
)
|
||||
)
|
||||
.content(R.string.download_frpc_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
downloadFrpcLib()
|
||||
}
|
||||
.show()
|
||||
return@setNavigationItemSelectedListener false
|
||||
}
|
||||
|
||||
if (FRPC_LIB_VERSION == Frpclib.getVersion()) {
|
||||
openNewPage(FrpcFragment::class.java)
|
||||
} else {
|
||||
MaterialDialog.Builder(this)
|
||||
.title(R.string.frpclib_version_mismatch)
|
||||
.content(R.string.download_frpc_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
downloadFrpcLib()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
R.id.nav_app_list -> openNewPage(AppListFragment::class.java)
|
||||
R.id.nav_logcat -> openNewPage(LogcatFragment::class.java)
|
||||
R.id.nav_help -> AgentWebActivity.goWeb(this, getString(R.string.url_help))
|
||||
R.id.nav_about -> openNewPage(AboutFragment::class.java)
|
||||
else -> XToastUtils.toast("Click:" + menuItem.title)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
//主页事件监听
|
||||
binding!!.includeMain.viewPager.addOnPageChangeListener(object :
|
||||
ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int,
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
val item = binding!!.includeMain.bottomNavigation.menu.getItem(position)
|
||||
binding!!.includeMain.toolbar.title = item.title
|
||||
binding!!.includeMain.toolbar.menu.clear()
|
||||
when (item.title) {
|
||||
getString(R.string.menu_rules) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_rules
|
||||
)
|
||||
getString(R.string.menu_senders) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_senders
|
||||
)
|
||||
getString(R.string.menu_settings) -> binding!!.includeMain.toolbar.inflateMenu(
|
||||
R.menu.menu_settings
|
||||
)
|
||||
else -> binding!!.includeMain.toolbar.inflateMenu(R.menu.menu_logs)
|
||||
}
|
||||
item.isChecked = true
|
||||
updateSideNavStatus(item)
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {}
|
||||
})
|
||||
binding!!.includeMain.bottomNavigation.setOnNavigationItemSelectedListener(this)
|
||||
|
||||
//tabBar分类切换
|
||||
LiveEventBus.get(EVENT_UPDATE_LOGS_TYPE, String::class.java).observe(this) { type: String ->
|
||||
logsType = type
|
||||
}
|
||||
LiveEventBus.get(EVENT_UPDATE_RULE_TYPE, String::class.java).observe(this) { type: String ->
|
||||
ruleType = type
|
||||
}
|
||||
|
||||
//更新通知栏文案
|
||||
LiveEventBus.get(EVENT_UPDATE_NOTIFY, String::class.java).observe(this) { notify: String ->
|
||||
cactusUpdateNotification {
|
||||
setContent(notify)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理侧边栏点击事件
|
||||
*
|
||||
* @param menuItem
|
||||
* @return
|
||||
*/
|
||||
private fun handleNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||
for (index in mTitles.indices) {
|
||||
if (mTitles[index] == menuItem.title) {
|
||||
binding!!.includeMain.toolbar.title = menuItem.title
|
||||
binding!!.includeMain.viewPager.setCurrentItem(index, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_notifications -> {
|
||||
showTipsForce(this)
|
||||
}
|
||||
R.id.action_clear_logs -> {
|
||||
MaterialDialog.Builder(this)
|
||||
.content(R.string.delete_type_log_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
AppDatabase.getInstance(this)
|
||||
.msgDao()
|
||||
.deleteAll(logsType)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : CompletableObserver {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onComplete() {
|
||||
XToastUtils.success(R.string.delete_type_log_toast)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.message?.let { XToastUtils.error(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
.show()
|
||||
}
|
||||
R.id.action_add_sender -> {
|
||||
val dialog = BottomSheetDialog(this)
|
||||
val view: View =
|
||||
LayoutInflater.from(this).inflate(R.layout.dialog_sender_bottom_sheet, null)
|
||||
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
|
||||
|
||||
WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f))
|
||||
val widgetItemAdapter = WidgetItemAdapter(SENDER_FRAGMENT_LIST)
|
||||
widgetItemAdapter.setOnItemClickListener(this)
|
||||
recyclerView.adapter = widgetItemAdapter
|
||||
|
||||
dialog.setContentView(view)
|
||||
dialog.setCancelable(true)
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
dialog.show()
|
||||
WidgetUtils.transparentBottomSheetDialogBackground(dialog)
|
||||
}
|
||||
R.id.action_add_rule -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.putString(KEY_RULE_TYPE, ruleType)
|
||||
.setNewActivity(true)
|
||||
.open(this)
|
||||
}
|
||||
/*R.id.action_restore_settings -> {
|
||||
XToastUtils.success(logsType)
|
||||
}*/
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
}
|
||||
|
||||
//================Navigation================//
|
||||
/**
|
||||
* 底部导航栏点击事件
|
||||
*
|
||||
* @param menuItem
|
||||
* @return
|
||||
*/
|
||||
override fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||
for (index in mTitles.indices) {
|
||||
if (mTitles[index] == menuItem.title) {
|
||||
binding!!.includeMain.toolbar.title = menuItem.title
|
||||
binding!!.includeMain.viewPager.setCurrentItem(index, false)
|
||||
updateSideNavStatus(menuItem)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新侧边栏菜单选中状态
|
||||
*
|
||||
* @param menuItem
|
||||
*/
|
||||
private fun updateSideNavStatus(menuItem: MenuItem) {
|
||||
val side = binding!!.navView.menu.findItem(menuItem.itemId)
|
||||
if (side != null) {
|
||||
side.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
//按返回键不退出回到桌面
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onBackPressed() {
|
||||
val intent = Intent(Intent.ACTION_MAIN)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.addCategory(Intent.CATEGORY_HOME)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onItemClick(itemView: View, widgetInfo: PageInfo, pos: Int) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment
|
||||
.setNewActivity(true)
|
||||
.putInt(KEY_SENDER_TYPE, pos) //注意:目前刚好是这个顺序而已
|
||||
.open(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
//动态加载FrpcLib
|
||||
private fun downloadFrpcLib() {
|
||||
val cpuAbi = when (Build.CPU_ABI) {
|
||||
"x86" -> "x86"
|
||||
"x86_64" -> "x86_64"
|
||||
"arm64-v8a" -> "arm64-v8a"
|
||||
else -> "armeabi-v7a"
|
||||
}
|
||||
|
||||
val libPath = filesDir.absolutePath + "/libs"
|
||||
val soFile = File(libPath)
|
||||
if (!soFile.exists()) soFile.mkdirs()
|
||||
val downloadUrl = String.format(FRPC_LIB_DOWNLOAD_URL, FRPC_LIB_VERSION, cpuAbi)
|
||||
val mContext = this
|
||||
val dialog: MaterialDialog = MaterialDialog.Builder(mContext)
|
||||
.title(String.format(getString(R.string.frpclib_download_title), FRPC_LIB_VERSION))
|
||||
.content(getString(R.string.frpclib_download_content))
|
||||
.contentGravity(GravityEnum.CENTER)
|
||||
.progress(false, 0, true)
|
||||
.progressNumberFormat("%2dMB/%1dMB")
|
||||
.build()
|
||||
|
||||
XHttp.downLoad(downloadUrl)
|
||||
.savePath(cacheDir.absolutePath)
|
||||
.execute(object : DownloadProgressCallBack<String?>() {
|
||||
override fun onStart() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
override fun onError(e: ApiException) {
|
||||
dialog.dismiss()
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
|
||||
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||
Log.d(TAG, "onProgress: bytesRead=$bytesRead, contentLength=$contentLength")
|
||||
dialog.maxProgress = (contentLength / 1048576L).toInt()
|
||||
dialog.setProgress((bytesRead / 1048576L).toInt())
|
||||
}
|
||||
|
||||
override fun onComplete(srcPath: String) {
|
||||
dialog.dismiss()
|
||||
Log.d(TAG, "srcPath = $srcPath")
|
||||
|
||||
val srcFile = File(srcPath)
|
||||
val destFile = File("$libPath/libgojni.so")
|
||||
FileUtils.moveFile(srcFile, destFile, null)
|
||||
|
||||
val intent: Intent? = packageManager.getLaunchIntentForPackage(packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
android.os.Process.killProcess(android.os.Process.myPid()) //杀掉以前进程
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +1,56 @@
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.adapter.LogsPagingAdapter.MyViewHolder
|
||||
import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
|
||||
import com.idormy.sms.forwarder.database.entity.Sender
|
||||
import com.idormy.sms.forwarder.databinding.AdapterLogsCardViewListItemBinding
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
|
||||
class LogsPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<LogsAndRuleAndSender, MyViewHolder>(diffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = AdapterLogsCardViewListItemBinding.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.tvFrom.text = item.logs.from
|
||||
holder.binding.tvTime.text = DateUtils.getFriendlyTimeSpanByNow(item.logs.time)
|
||||
holder.binding.tvContent.text = item.logs.content
|
||||
holder.binding.ivSenderImage.setImageResource(Sender.getImageId(item.relation.sender.type))
|
||||
holder.binding.ivStatusImage.setImageResource(item.logs.statusImageId)
|
||||
holder.binding.ivSimImage.setImageResource(item.logs.simImageId)
|
||||
|
||||
holder.binding.cardView.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: AdapterLogsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(view: View?, item: LogsAndRuleAndSender)
|
||||
fun onItemRemove(view: View?, id: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var diffCallback: DiffUtil.ItemCallback<LogsAndRuleAndSender> = object : DiffUtil.ItemCallback<LogsAndRuleAndSender>() {
|
||||
override fun areItemsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||
return oldItem.logs.id == newItem.logs.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||
return oldItem.logs === newItem.logs
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.adapter.LogsPagingAdapter.MyViewHolder
|
||||
import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
|
||||
import com.idormy.sms.forwarder.databinding.AdapterLogsCardViewListItemBinding
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
|
||||
class LogsPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<LogsAndRuleAndSender, MyViewHolder>(diffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = AdapterLogsCardViewListItemBinding.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.tvFrom.text = item.msg.from
|
||||
holder.binding.tvTime.text = DateUtils.getFriendlyTimeSpanByNow(item.logs.time)
|
||||
holder.binding.tvContent.text = item.msg.content
|
||||
//holder.binding.ivSenderImage.setImageResource(Sender.getImageId(item.sender.type))
|
||||
//holder.binding.ivStatusImage.setImageResource(item.logs.statusImageId)
|
||||
holder.binding.ivSimImage.setImageResource(item.msg.simImageId)
|
||||
|
||||
holder.binding.cardView.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: AdapterLogsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(view: View?, item: LogsAndRuleAndSender)
|
||||
fun onItemRemove(view: View?, id: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var diffCallback: DiffUtil.ItemCallback<LogsAndRuleAndSender> = object : DiffUtil.ItemCallback<LogsAndRuleAndSender>() {
|
||||
override fun areItemsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||
return oldItem.logs.id == newItem.logs.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||
return oldItem.logs === newItem.logs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.MsgPagingAdapter.MyViewHolder
|
||||
import com.idormy.sms.forwarder.database.entity.LogsDetail
|
||||
import com.idormy.sms.forwarder.database.entity.MsgAndLogs
|
||||
import com.idormy.sms.forwarder.databinding.AdapterLogsCardViewListItemBinding
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
|
||||
class MsgPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<MsgAndLogs, MyViewHolder>(diffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = AdapterLogsCardViewListItemBinding.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.tvFrom.text = item.msg.from
|
||||
holder.binding.tvTime.text = DateUtils.getFriendlyTimeSpanByNow(item.msg.time)
|
||||
holder.binding.tvContent.text = item.msg.content
|
||||
//holder.binding.ivSenderImage.setImageResource(Sender.getImageId(item.sender.type))
|
||||
//holder.binding.ivStatusImage.setImageResource(item.msg.statusImageId)
|
||||
holder.binding.ivSimImage.setImageResource(item.msg.simImageId)
|
||||
|
||||
holder.binding.layoutLogs.removeAllViews()
|
||||
for (logs in item.logsList) {
|
||||
val layoutSenderItem = View.inflate(App.context, R.layout.item_logs, null) as LinearLayout
|
||||
val ivSenderImage = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_image)
|
||||
val ivSenderStatus = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_status)
|
||||
val tvSenderName = layoutSenderItem.findViewById<TextView>(R.id.tv_sender_name)
|
||||
ivSenderImage.setImageResource(logs.senderImageId)
|
||||
ivSenderStatus.setImageResource(logs.statusImageId)
|
||||
tvSenderName.text = logs.senderName
|
||||
layoutSenderItem.setOnClickListener { view: View? ->
|
||||
itemClickListener.onLogsClicked(view, logs)
|
||||
}
|
||||
holder.binding.layoutLogs.addView(layoutSenderItem)
|
||||
}
|
||||
|
||||
holder.binding.cardView.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: AdapterLogsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(view: View?, item: MsgAndLogs)
|
||||
fun onLogsClicked(view: View?, item: LogsDetail)
|
||||
fun onItemRemove(view: View?, id: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var diffCallback: DiffUtil.ItemCallback<MsgAndLogs> = object : DiffUtil.ItemCallback<MsgAndLogs>() {
|
||||
override fun areItemsTheSame(oldItem: MsgAndLogs, newItem: MsgAndLogs): Boolean {
|
||||
return oldItem.msg.id == newItem.msg.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: MsgAndLogs, newItem: MsgAndLogs): Boolean {
|
||||
return oldItem.msg === newItem.msg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +1,78 @@
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.RulePagingAdapter.MyViewHolder
|
||||
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||
import com.idormy.sms.forwarder.databinding.AdapterRulesCardViewListItemBinding
|
||||
|
||||
class RulePagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<RuleAndSender, MyViewHolder>(diffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = AdapterRulesCardViewListItemBinding.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.ivRuleImage.setImageResource(item.rule.imageId)
|
||||
holder.binding.ivRuleStatus.setImageResource(item.rule.statusImageId)
|
||||
holder.binding.tvRuleMatch.text = item.rule.ruleMatch
|
||||
holder.binding.ivSenderImage.setImageResource(item.sender.imageId)
|
||||
holder.binding.ivSenderStatus.setImageResource(item.sender.statusImageId)
|
||||
holder.binding.tvSenderName.text = item.sender.name
|
||||
|
||||
/*holder.binding.cardView.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}*/
|
||||
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.ivEdit.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
holder.binding.ivDelete.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: AdapterRulesCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(view: View?, item: RuleAndSender)
|
||||
fun onItemRemove(view: View?, id: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var diffCallback: DiffUtil.ItemCallback<RuleAndSender> = object : DiffUtil.ItemCallback<RuleAndSender>() {
|
||||
override fun areItemsTheSame(oldItem: RuleAndSender, newItem: RuleAndSender): Boolean {
|
||||
return oldItem.rule.id == newItem.rule.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: RuleAndSender, newItem: RuleAndSender): Boolean {
|
||||
return oldItem.rule === newItem.rule
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.RulePagingAdapter.MyViewHolder
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.databinding.AdapterRulesCardViewListItemBinding
|
||||
|
||||
class RulePagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Rule, MyViewHolder>(diffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||
val binding = AdapterRulesCardViewListItemBinding.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.ivRuleImage.setImageResource(item.imageId)
|
||||
holder.binding.ivRuleStatus.setImageResource(item.statusImageId)
|
||||
holder.binding.tvRuleMatch.text = item.ruleMatch
|
||||
|
||||
holder.binding.layoutSenders.removeAllViews()
|
||||
for (sender in item.senderList) {
|
||||
val layoutSenderItem = View.inflate(App.context, R.layout.item_sender, null) as LinearLayout
|
||||
val ivSenderImage = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_image)
|
||||
val ivSenderStatus = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_status)
|
||||
val tvSenderName = layoutSenderItem.findViewById<TextView>(R.id.tv_sender_name)
|
||||
ivSenderImage.setImageResource(sender.imageId)
|
||||
ivSenderStatus.setImageResource(sender.statusImageId)
|
||||
tvSenderName.text = sender.name
|
||||
holder.binding.layoutSenders.addView(layoutSenderItem)
|
||||
}
|
||||
|
||||
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.ivEdit.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
holder.binding.ivDelete.setOnClickListener { view: View? ->
|
||||
itemClickListener.onItemClicked(view, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyViewHolder(val binding: AdapterRulesCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
interface OnItemClickListener {
|
||||
fun onItemClicked(view: View?, item: Rule)
|
||||
fun onItemRemove(view: View?, id: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var diffCallback: DiffUtil.ItemCallback<Rule> = object : DiffUtil.ItemCallback<Rule>() {
|
||||
override fun areItemsTheSame(oldItem: Rule, newItem: Rule): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: Rule, newItem: Rule): Boolean {
|
||||
return oldItem === newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +1,58 @@
|
||||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.work.Configuration
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.BuildConfig
|
||||
import com.idormy.sms.forwarder.database.repository.FrpcRepository
|
||||
import com.idormy.sms.forwarder.database.repository.LogsRepository
|
||||
import com.idormy.sms.forwarder.database.repository.RuleRepository
|
||||
import com.idormy.sms.forwarder.database.repository.SenderRepository
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object Core : Configuration.Provider {
|
||||
lateinit var app: Application
|
||||
val frpc: FrpcRepository by lazy { (app as App).frpcRepository }
|
||||
val logs: LogsRepository by lazy { (app as App).logsRepository }
|
||||
val rule: RuleRepository by lazy { (app as App).ruleRepository }
|
||||
val sender: SenderRepository by lazy { (app as App).senderRepository }
|
||||
/*
|
||||
val telephonyManager: TelephonyManager by lazy { app.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager }
|
||||
val smsManager: SmsManager by lazy { app.getSystemService(SmsManager::class.java) }
|
||||
val subscriptionManager: SubscriptionManager by lazy {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
SubscriptionManager.from(app)
|
||||
} else {
|
||||
app.getSystemService(SubscriptionManager::class.java)
|
||||
}
|
||||
}
|
||||
val user by lazy { app.getSystemService<UserManager>()!! }*/
|
||||
|
||||
|
||||
/*val directBootAware: Boolean get() = directBootSupported && dataStore.canToggleLocked
|
||||
val directBootSupported by lazy {
|
||||
Build.VERSION.SDK_INT >= 24 && try {
|
||||
app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus ==
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
|
||||
} catch (_: RuntimeException) {
|
||||
false
|
||||
}
|
||||
}*/
|
||||
|
||||
fun init(app: Application) {
|
||||
this.app = app
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration(): Configuration {
|
||||
return Configuration.Builder().apply {
|
||||
setDefaultProcessName(app.packageName + ":bg")
|
||||
setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.VERBOSE else Log.INFO)
|
||||
setExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||
setTaskExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||
}.build()
|
||||
}
|
||||
|
||||
fun startService() = ContextCompat.startForegroundService(app, Intent(app, ForegroundService::class.java))
|
||||
}
|
||||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.work.Configuration
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.BuildConfig
|
||||
import com.idormy.sms.forwarder.database.repository.*
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object Core : Configuration.Provider {
|
||||
lateinit var app: Application
|
||||
val frpc: FrpcRepository by lazy { (app as App).frpcRepository }
|
||||
val msg: MsgRepository by lazy { (app as App).msgRepository }
|
||||
val logs: LogsRepository by lazy { (app as App).logsRepository }
|
||||
val rule: RuleRepository by lazy { (app as App).ruleRepository }
|
||||
val sender: SenderRepository by lazy { (app as App).senderRepository }
|
||||
/*
|
||||
val telephonyManager: TelephonyManager by lazy { app.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager }
|
||||
val smsManager: SmsManager by lazy { app.getSystemService(SmsManager::class.java) }
|
||||
val subscriptionManager: SubscriptionManager by lazy {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
SubscriptionManager.from(app)
|
||||
} else {
|
||||
app.getSystemService(SubscriptionManager::class.java)
|
||||
}
|
||||
}
|
||||
val user by lazy { app.getSystemService<UserManager>()!! }*/
|
||||
|
||||
|
||||
/*val directBootAware: Boolean get() = directBootSupported && dataStore.canToggleLocked
|
||||
val directBootSupported by lazy {
|
||||
Build.VERSION.SDK_INT >= 24 && try {
|
||||
app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus ==
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
|
||||
} catch (_: RuntimeException) {
|
||||
false
|
||||
}
|
||||
}*/
|
||||
|
||||
fun init(app: Application) {
|
||||
this.app = app
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration(): Configuration {
|
||||
return Configuration.Builder().apply {
|
||||
setDefaultProcessName(app.packageName + ":bg")
|
||||
setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.VERBOSE else Log.INFO)
|
||||
setExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||
setTaskExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||
}.build()
|
||||
}
|
||||
|
||||
fun startService() = ContextCompat.startForegroundService(app, Intent(app, ForegroundService::class.java))
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.idormy.sms.forwarder.database.dao
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.*
|
||||
import com.idormy.sms.forwarder.database.entity.Msg
|
||||
import com.idormy.sms.forwarder.database.entity.MsgAndLogs
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
|
||||
@Dao
|
||||
interface MsgDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
suspend fun insert(msg: Msg): Long
|
||||
|
||||
@Delete
|
||||
fun delete(msg: Msg): Completable
|
||||
|
||||
@Query("DELETE FROM Msg where id=:id")
|
||||
fun delete(id: Long)
|
||||
|
||||
@Query("DELETE FROM Msg where type=:type")
|
||||
fun deleteAll(type: String): Completable
|
||||
|
||||
@Query("DELETE FROM Msg where time<:time")
|
||||
fun deleteTimeAgo(time: Long)
|
||||
|
||||
@Update
|
||||
fun update(msg: Msg): Completable
|
||||
|
||||
@Query("SELECT * FROM Msg where id=:id")
|
||||
fun get(id: Long): Single<Msg>
|
||||
|
||||
@Query("SELECT count(*) FROM Msg where type=:type")
|
||||
fun count(type: String): Single<Int>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Msg WHERE type = :type ORDER BY id DESC")
|
||||
fun pagingSource(type: String): PagingSource<Int, MsgAndLogs>
|
||||
|
||||
}
|
@ -1,18 +1,32 @@
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class LogsAndRuleAndSender(
|
||||
@Embedded val logs: Logs,
|
||||
|
||||
@Relation(
|
||||
entity = Rule::class,
|
||||
parentColumn = "rule_id",
|
||||
entityColumn = "id"
|
||||
)
|
||||
val relation: RuleAndSender,
|
||||
) : Parcelable
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class LogsAndRuleAndSender(
|
||||
@Embedded val logs: Logs,
|
||||
|
||||
@Relation(
|
||||
entity = Msg::class,
|
||||
parentColumn = "msg_id",
|
||||
entityColumn = "id"
|
||||
)
|
||||
val msg: Msg,
|
||||
|
||||
@Relation(
|
||||
entity = Rule::class,
|
||||
parentColumn = "rule_id",
|
||||
entityColumn = "id"
|
||||
)
|
||||
val rule: Rule,
|
||||
|
||||
@Relation(
|
||||
entity = Sender::class,
|
||||
parentColumn = "sender_id",
|
||||
entityColumn = "id"
|
||||
)
|
||||
val sender: Sender,
|
||||
) : Parcelable
|
||||
|
@ -0,0 +1,59 @@
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.DatabaseView
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Parcelize
|
||||
@DatabaseView("SELECT LOGS.id,LOGS.type,LOGS.msg_id,LOGS.rule_id,LOGS.sender_id,LOGS.forward_status,LOGS.forward_response,LOGS.TIME,Rule.filed AS rule_filed,Rule.`check` AS rule_check,Rule.value AS rule_value,Rule.sim_slot AS rule_sim_slot,Sender.type AS sender_type,Sender.NAME AS sender_name FROM LOGS LEFT JOIN Rule ON LOGS.rule_id = Rule.id LEFT JOIN Sender ON LOGS.sender_id = Sender.id")
|
||||
data class LogsDetail(
|
||||
@ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "type", defaultValue = "sms") var type: String,
|
||||
@ColumnInfo(name = "msg_id", defaultValue = "0") var msgId: Long = 0,
|
||||
@ColumnInfo(name = "rule_id", defaultValue = "0") var ruleId: Long = 0,
|
||||
@ColumnInfo(name = "sender_id", defaultValue = "0") var senderId: Long = 0,
|
||||
@ColumnInfo(name = "forward_status", defaultValue = "1") var forwardStatus: Int = 1,
|
||||
@ColumnInfo(name = "forward_response", defaultValue = "") var forwardResponse: String = "",
|
||||
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||
@ColumnInfo(name = "rule_filed", defaultValue = "") var ruleFiled: String,
|
||||
@ColumnInfo(name = "rule_check", defaultValue = "") var ruleCheck: String,
|
||||
@ColumnInfo(name = "rule_value", defaultValue = "") var ruleValue: String,
|
||||
@ColumnInfo(name = "rule_sim_slot", defaultValue = "") var ruleSimSlot: String,
|
||||
@ColumnInfo(name = "sender_type", defaultValue = "1") var senderType: Int = 1,
|
||||
@ColumnInfo(name = "sender_name", defaultValue = "") var senderName: String,
|
||||
) : Parcelable {
|
||||
|
||||
val statusImageId: Int
|
||||
get() {
|
||||
if (forwardStatus == 1) {
|
||||
return R.drawable.ic_round_warning
|
||||
} else if (forwardStatus == 2) {
|
||||
return R.drawable.ic_round_check
|
||||
}
|
||||
return R.drawable.ic_round_cancel
|
||||
}
|
||||
|
||||
val senderImageId: Int
|
||||
get() = when (senderType) {
|
||||
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
|
||||
TYPE_EMAIL -> R.drawable.icon_email
|
||||
TYPE_BARK -> R.drawable.icon_bark
|
||||
TYPE_WEBHOOK -> R.drawable.icon_webhook
|
||||
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
|
||||
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
|
||||
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
|
||||
TYPE_TELEGRAM -> R.drawable.icon_telegram
|
||||
TYPE_FEISHU -> R.drawable.icon_feishu
|
||||
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
|
||||
TYPE_GOTIFY -> R.drawable.icon_gotify
|
||||
TYPE_SMS -> R.drawable.icon_sms
|
||||
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
|
||||
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
|
||||
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
|
||||
else -> R.drawable.icon_sms
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class MsgAndLogs(
|
||||
@Embedded val msg: Msg,
|
||||
|
||||
@Relation(
|
||||
parentColumn = "id",
|
||||
entityColumn = "msg_id"
|
||||
)
|
||||
val logsList: List<LogsDetail>
|
||||
) : Parcelable
|
@ -1,180 +1,193 @@
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.room.*
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import java.util.regex.PatternSyntaxException
|
||||
|
||||
@Parcelize
|
||||
@Entity(
|
||||
tableName = "Rule",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Sender::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["sender_id"],
|
||||
onDelete = ForeignKey.CASCADE, //级联操作
|
||||
onUpdate = ForeignKey.CASCADE //级联操作
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index(value = ["id"], unique = true),
|
||||
Index(value = ["sender_id"])
|
||||
]
|
||||
)
|
||||
data class Rule(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "type", defaultValue = "sms") var type: String,
|
||||
@ColumnInfo(name = "filed", defaultValue = "transpond_all") var filed: String,
|
||||
@ColumnInfo(name = "check", defaultValue = "is") var check: String,
|
||||
@ColumnInfo(name = "value", defaultValue = "") var value: String,
|
||||
@ColumnInfo(name = "sender_id", defaultValue = "0") var senderId: Long = 0,
|
||||
@ColumnInfo(name = "sms_template", defaultValue = "") var smsTemplate: String = "",
|
||||
@ColumnInfo(name = "regex_replace", defaultValue = "") var regexReplace: String = "",
|
||||
@ColumnInfo(name = "sim_slot", defaultValue = "ALL") var simSlot: String = "",
|
||||
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
|
||||
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
val TAG: String = Rule::class.java.simpleName
|
||||
|
||||
fun getRuleMatch(filed: String?, check: String?, value: String?, simSlot: String?): Any {
|
||||
val sb = StringBuilder()
|
||||
sb.append(SIM_SLOT_MAP[simSlot]).append(getString(R.string.rule_card))
|
||||
if (filed == null || filed == FILED_TRANSPOND_ALL) {
|
||||
sb.append(getString(R.string.rule_all_fw_to))
|
||||
} else {
|
||||
sb.append(getString(R.string.rule_when)).append(FILED_MAP[filed]).append(CHECK_MAP[check]).append(value).append(getString(R.string.rule_fw_to))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val ruleMatch: String
|
||||
get() {
|
||||
val simStr = if ("app" == type) "" else SIM_SLOT_MAP[simSlot].toString() + getString(R.string.rule_card)
|
||||
return if (filed == FILED_TRANSPOND_ALL) {
|
||||
simStr + getString(R.string.rule_all_fw_to)
|
||||
} else {
|
||||
simStr + getString(R.string.rule_when) + FILED_MAP[filed] + CHECK_MAP[check] + value + getString(R.string.rule_fw_to)
|
||||
}
|
||||
}
|
||||
|
||||
val statusChecked: Boolean
|
||||
get() = status != STATUS_OFF
|
||||
|
||||
val imageId: Int
|
||||
get() = when (simSlot) {
|
||||
CHECK_SIM_SLOT_1 -> R.drawable.ic_sim1
|
||||
CHECK_SIM_SLOT_2 -> R.drawable.ic_sim2
|
||||
CHECK_SIM_SLOT_ALL -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||
else -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||
}
|
||||
|
||||
val statusImageId: Int
|
||||
get() = when (status) {
|
||||
STATUS_OFF -> R.drawable.icon_off
|
||||
else -> R.drawable.icon_on
|
||||
}
|
||||
|
||||
fun getSimSlotCheckId(): Int {
|
||||
return when (simSlot) {
|
||||
CHECK_SIM_SLOT_1 -> R.id.rb_sim_slot_1
|
||||
CHECK_SIM_SLOT_2 -> R.id.rb_sim_slot_2
|
||||
else -> R.id.rb_sim_slot_all
|
||||
}
|
||||
}
|
||||
|
||||
fun getFiledCheckId(): Int {
|
||||
return when (filed) {
|
||||
FILED_MSG_CONTENT -> R.id.rb_content
|
||||
FILED_PHONE_NUM -> R.id.rb_phone
|
||||
FILED_PACKAGE_NAME -> R.id.rb_package_name
|
||||
FILED_INFORM_CONTENT -> R.id.rb_inform_content
|
||||
FILED_MULTI_MATCH -> R.id.rb_multi_match
|
||||
else -> R.id.rb_transpond_all
|
||||
}
|
||||
}
|
||||
|
||||
fun getCheckCheckId(): Int {
|
||||
return when (check) {
|
||||
CHECK_CONTAIN -> R.id.rb_contain
|
||||
CHECK_NOT_CONTAIN -> R.id.rb_not_contain
|
||||
CHECK_START_WITH -> R.id.rb_start_with
|
||||
CHECK_END_WITH -> R.id.rb_end_with
|
||||
CHECK_REGEX -> R.id.rb_regex
|
||||
else -> R.id.rb_is
|
||||
}
|
||||
}
|
||||
|
||||
//字段分支
|
||||
@Throws(Exception::class)
|
||||
fun checkMsg(msg: MsgInfo?): Boolean {
|
||||
|
||||
//检查这一行和上一行合并的结果是否命中
|
||||
var mixChecked = false
|
||||
if (msg != null) {
|
||||
//先检查规则是否命中
|
||||
when (this.filed) {
|
||||
FILED_TRANSPOND_ALL -> mixChecked = true
|
||||
FILED_PHONE_NUM, FILED_PACKAGE_NAME -> mixChecked = checkValue(msg.from)
|
||||
FILED_MSG_CONTENT, FILED_INFORM_CONTENT -> mixChecked = checkValue(msg.content)
|
||||
FILED_MULTI_MATCH -> mixChecked = RuleLineUtils.checkRuleLines(msg, this.value)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "rule:$this checkMsg:$msg checked:$mixChecked")
|
||||
return mixChecked
|
||||
}
|
||||
|
||||
//内容分支
|
||||
private fun checkValue(msgValue: String?): Boolean {
|
||||
var checked = false
|
||||
when (this.check) {
|
||||
CHECK_IS -> checked = this.value == msgValue
|
||||
CHECK_NOT_IS -> checked = this.value != msgValue
|
||||
CHECK_CONTAIN -> if (msgValue != null) {
|
||||
checked = msgValue.contains(this.value)
|
||||
}
|
||||
CHECK_NOT_CONTAIN -> if (msgValue != null) {
|
||||
checked = !msgValue.contains(this.value)
|
||||
}
|
||||
CHECK_START_WITH -> if (msgValue != null) {
|
||||
checked = msgValue.startsWith(this.value)
|
||||
}
|
||||
CHECK_END_WITH -> if (msgValue != null) {
|
||||
checked = msgValue.endsWith(this.value)
|
||||
}
|
||||
CHECK_REGEX -> if (msgValue != null) {
|
||||
try {
|
||||
//checked = Pattern.matches(this.value, msgValue);
|
||||
val pattern = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE)
|
||||
val matcher = pattern.matcher(msgValue)
|
||||
while (matcher.find()) {
|
||||
checked = true
|
||||
break
|
||||
}
|
||||
} catch (e: PatternSyntaxException) {
|
||||
Log.d(TAG, "PatternSyntaxException: ")
|
||||
Log.d(TAG, "Description: " + e.description)
|
||||
Log.d(TAG, "Index: " + e.index)
|
||||
Log.d(TAG, "Message: " + e.message)
|
||||
Log.d(TAG, "Pattern: " + e.pattern)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
Log.i(TAG, "checkValue " + msgValue + " " + this.check + " " + this.value + " checked:" + checked)
|
||||
return checked
|
||||
}
|
||||
package com.idormy.sms.forwarder.database.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.room.*
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.database.ext.ConvertersSenderList
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.xuexiang.xui.utils.ResUtils.getString
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import java.util.regex.PatternSyntaxException
|
||||
|
||||
@Parcelize
|
||||
@Entity(
|
||||
tableName = "Rule",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Sender::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["sender_id"],
|
||||
onDelete = ForeignKey.CASCADE, //级联操作
|
||||
onUpdate = ForeignKey.CASCADE //级联操作
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index(value = ["id"], unique = true),
|
||||
Index(value = ["sender_id"]),
|
||||
Index(value = ["sender_list"])
|
||||
]
|
||||
)
|
||||
@TypeConverters(ConvertersSenderList::class)
|
||||
data class Rule(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "type", defaultValue = "sms") var type: String,
|
||||
@ColumnInfo(name = "filed", defaultValue = "transpond_all") var filed: String,
|
||||
@ColumnInfo(name = "check", defaultValue = "is") var check: String,
|
||||
@ColumnInfo(name = "value", defaultValue = "") var value: String,
|
||||
@ColumnInfo(name = "sender_id", defaultValue = "0") var senderId: Long = 0,
|
||||
@ColumnInfo(name = "sms_template", defaultValue = "") var smsTemplate: String = "",
|
||||
@ColumnInfo(name = "regex_replace", defaultValue = "") var regexReplace: String = "",
|
||||
@ColumnInfo(name = "sim_slot", defaultValue = "ALL") var simSlot: String = "",
|
||||
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
|
||||
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||
@ColumnInfo(name = "sender_list", defaultValue = "") var senderList: List<Sender>,
|
||||
@ColumnInfo(name = "sender_logic", defaultValue = "ALL") var senderLogic: String = "ALL",
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
val TAG: String = Rule::class.java.simpleName
|
||||
|
||||
fun getRuleMatch(filed: String?, check: String?, value: String?, simSlot: String?): Any {
|
||||
val sb = StringBuilder()
|
||||
sb.append(SIM_SLOT_MAP[simSlot]).append(getString(R.string.rule_card))
|
||||
if (filed == null || filed == FILED_TRANSPOND_ALL) {
|
||||
sb.append(getString(R.string.rule_all_fw_to))
|
||||
} else {
|
||||
sb.append(getString(R.string.rule_when)).append(FILED_MAP[filed]).append(CHECK_MAP[check]).append(value).append(getString(R.string.rule_fw_to))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val ruleMatch: String
|
||||
get() {
|
||||
val simStr = if ("app" == type) "" else SIM_SLOT_MAP[simSlot].toString() + getString(R.string.rule_card)
|
||||
return if (filed == FILED_TRANSPOND_ALL) {
|
||||
simStr + getString(R.string.rule_all_fw_to)
|
||||
} else {
|
||||
simStr + getString(R.string.rule_when) + FILED_MAP[filed] + CHECK_MAP[check] + value + getString(R.string.rule_fw_to)
|
||||
}
|
||||
}
|
||||
|
||||
val statusChecked: Boolean
|
||||
get() = status != STATUS_OFF
|
||||
|
||||
val imageId: Int
|
||||
get() = when (simSlot) {
|
||||
CHECK_SIM_SLOT_1 -> R.drawable.ic_sim1
|
||||
CHECK_SIM_SLOT_2 -> R.drawable.ic_sim2
|
||||
CHECK_SIM_SLOT_ALL -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||
else -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||
}
|
||||
|
||||
val statusImageId: Int
|
||||
get() = when (status) {
|
||||
STATUS_OFF -> R.drawable.icon_off
|
||||
else -> R.drawable.icon_on
|
||||
}
|
||||
|
||||
fun getSenderLogicCheckId(): Int {
|
||||
return when (senderLogic) {
|
||||
SENDER_LOGIC_UNTIL_FAIL -> R.id.rb_sender_logic_until_fail
|
||||
SENDER_LOGIC_UNTIL_SUCCESS -> R.id.rb_sender_logic_until_success
|
||||
else -> R.id.rb_sender_logic_all
|
||||
}
|
||||
}
|
||||
|
||||
fun getSimSlotCheckId(): Int {
|
||||
return when (simSlot) {
|
||||
CHECK_SIM_SLOT_1 -> R.id.rb_sim_slot_1
|
||||
CHECK_SIM_SLOT_2 -> R.id.rb_sim_slot_2
|
||||
else -> R.id.rb_sim_slot_all
|
||||
}
|
||||
}
|
||||
|
||||
fun getFiledCheckId(): Int {
|
||||
return when (filed) {
|
||||
FILED_MSG_CONTENT -> R.id.rb_content
|
||||
FILED_PHONE_NUM -> R.id.rb_phone
|
||||
FILED_PACKAGE_NAME -> R.id.rb_package_name
|
||||
FILED_INFORM_CONTENT -> R.id.rb_inform_content
|
||||
FILED_MULTI_MATCH -> R.id.rb_multi_match
|
||||
else -> R.id.rb_transpond_all
|
||||
}
|
||||
}
|
||||
|
||||
fun getCheckCheckId(): Int {
|
||||
return when (check) {
|
||||
CHECK_CONTAIN -> R.id.rb_contain
|
||||
CHECK_NOT_CONTAIN -> R.id.rb_not_contain
|
||||
CHECK_START_WITH -> R.id.rb_start_with
|
||||
CHECK_END_WITH -> R.id.rb_end_with
|
||||
CHECK_REGEX -> R.id.rb_regex
|
||||
else -> R.id.rb_is
|
||||
}
|
||||
}
|
||||
|
||||
//字段分支
|
||||
@Throws(Exception::class)
|
||||
fun checkMsg(msg: MsgInfo?): Boolean {
|
||||
|
||||
//检查这一行和上一行合并的结果是否命中
|
||||
var mixChecked = false
|
||||
if (msg != null) {
|
||||
//先检查规则是否命中
|
||||
when (this.filed) {
|
||||
FILED_TRANSPOND_ALL -> mixChecked = true
|
||||
FILED_PHONE_NUM, FILED_PACKAGE_NAME -> mixChecked = checkValue(msg.from)
|
||||
FILED_MSG_CONTENT, FILED_INFORM_CONTENT -> mixChecked = checkValue(msg.content)
|
||||
FILED_MULTI_MATCH -> mixChecked = RuleLineUtils.checkRuleLines(msg, this.value)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "rule:$this checkMsg:$msg checked:$mixChecked")
|
||||
return mixChecked
|
||||
}
|
||||
|
||||
//内容分支
|
||||
private fun checkValue(msgValue: String?): Boolean {
|
||||
var checked = false
|
||||
when (this.check) {
|
||||
CHECK_IS -> checked = this.value == msgValue
|
||||
CHECK_NOT_IS -> checked = this.value != msgValue
|
||||
CHECK_CONTAIN -> if (msgValue != null) {
|
||||
checked = msgValue.contains(this.value)
|
||||
}
|
||||
CHECK_NOT_CONTAIN -> if (msgValue != null) {
|
||||
checked = !msgValue.contains(this.value)
|
||||
}
|
||||
CHECK_START_WITH -> if (msgValue != null) {
|
||||
checked = msgValue.startsWith(this.value)
|
||||
}
|
||||
CHECK_END_WITH -> if (msgValue != null) {
|
||||
checked = msgValue.endsWith(this.value)
|
||||
}
|
||||
CHECK_REGEX -> if (msgValue != null) {
|
||||
try {
|
||||
//checked = Pattern.matches(this.value, msgValue);
|
||||
val pattern = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE)
|
||||
val matcher = pattern.matcher(msgValue)
|
||||
while (matcher.find()) {
|
||||
checked = true
|
||||
break
|
||||
}
|
||||
} catch (e: PatternSyntaxException) {
|
||||
Log.d(TAG, "PatternSyntaxException: ")
|
||||
Log.d(TAG, "Description: " + e.description)
|
||||
Log.d(TAG, "Index: " + e.index)
|
||||
Log.d(TAG, "Message: " + e.message)
|
||||
Log.d(TAG, "Pattern: " + e.pattern)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
Log.i(TAG, "checkValue " + msgValue + " " + this.check + " " + this.value + " checked:" + checked)
|
||||
return checked
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
package com.idormy.sms.forwarder.database.ext
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Date? {
|
||||
return value?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
package com.idormy.sms.forwarder.database.ext
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
class ConvertersDate {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Date? {
|
||||
return value?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.idormy.sms.forwarder.database.ext
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.database.entity.Sender
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
class ConvertersSenderList {
|
||||
|
||||
@TypeConverter
|
||||
fun stringToObject(value: String): List<Sender> {
|
||||
var senderList: MutableList<Sender> = mutableListOf()
|
||||
value.split(",").map { it.trim() }.forEach {
|
||||
val sender = Core.sender.getOne(it.toLong())
|
||||
senderList.add(sender)
|
||||
}
|
||||
return senderList
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun objectToString(list: List<Sender>): String {
|
||||
var senderList = ArrayList<Long>()
|
||||
list.forEach {
|
||||
senderList += it.id
|
||||
}
|
||||
return senderList.joinToString(",")
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.idormy.sms.forwarder.database.repository
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.idormy.sms.forwarder.database.dao.MsgDao
|
||||
import com.idormy.sms.forwarder.database.entity.Msg
|
||||
|
||||
class MsgRepository(private val msgDao: MsgDao) {
|
||||
|
||||
@WorkerThread
|
||||
fun delete(id: Long) {
|
||||
msgDao.delete(id)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun deleteTimeAgo(time: Long) {
|
||||
msgDao.deleteTimeAgo(time)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
suspend fun insert(msg: Msg): Long = msgDao.insert(msg)
|
||||
|
||||
}
|
@ -1,36 +1,39 @@
|
||||
package com.idormy.sms.forwarder.database.repository
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.idormy.sms.forwarder.database.dao.RuleDao
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
|
||||
class RuleRepository(
|
||||
private val ruleDao: RuleDao,
|
||||
) {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@WorkerThread
|
||||
fun insert(rule: Rule) {
|
||||
ruleDao.insert(rule)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun delete(id: Long) {
|
||||
listener?.onDelete(id)
|
||||
ruleDao.delete(id)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun get(id: Long) = ruleDao.get(id)
|
||||
|
||||
suspend fun getRuleAndSender(type: String, status: Int, simSlot: String) = ruleDao.getRuleAndSender(type, status, simSlot)
|
||||
|
||||
fun getRuleList(type: String, status: Int, simSlot: String) = ruleDao.getRuleList(type, status, simSlot)
|
||||
|
||||
@WorkerThread
|
||||
fun update(rule: Rule) = ruleDao.update(rule)
|
||||
|
||||
//TODO:允许主线程访问,后面再优化
|
||||
val all: List<Rule> = ruleDao.getAll()
|
||||
package com.idormy.sms.forwarder.database.repository
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.idormy.sms.forwarder.database.dao.RuleDao
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
|
||||
class RuleRepository(
|
||||
private val ruleDao: RuleDao,
|
||||
) {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@WorkerThread
|
||||
fun insert(rule: Rule) {
|
||||
ruleDao.insert(rule)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun delete(id: Long) {
|
||||
listener?.onDelete(id)
|
||||
ruleDao.delete(id)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun get(id: Long) = ruleDao.get(id)
|
||||
|
||||
@WorkerThread
|
||||
fun getOne(id: Long) = ruleDao.getOne(id)
|
||||
|
||||
suspend fun getRuleAndSender(type: String, status: Int, simSlot: String) = ruleDao.getRuleAndSender(type, status, simSlot)
|
||||
|
||||
fun getRuleList(type: String, status: Int, simSlot: String) = ruleDao.getRuleList(type, status, simSlot)
|
||||
|
||||
@WorkerThread
|
||||
fun update(rule: Rule) = ruleDao.update(rule)
|
||||
|
||||
//TODO:允许主线程访问,后面再优化
|
||||
val all: List<Rule> = ruleDao.getAll()
|
||||
}
|
@ -1,34 +1,36 @@
|
||||
package com.idormy.sms.forwarder.database.repository
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.idormy.sms.forwarder.database.dao.SenderDao
|
||||
import com.idormy.sms.forwarder.database.entity.Sender
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class SenderRepository(private val senderDao: SenderDao) {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@WorkerThread
|
||||
fun insert(sender: Sender) = senderDao.insert(sender)
|
||||
|
||||
@WorkerThread
|
||||
fun delete(id: Long) {
|
||||
listener?.onDelete(id)
|
||||
senderDao.delete(id)
|
||||
}
|
||||
|
||||
fun get(id: Long) = senderDao.get(id)
|
||||
|
||||
fun update(sender: Sender) = senderDao.update(sender)
|
||||
|
||||
val count: Flow<Long> = senderDao.getOnCount()
|
||||
|
||||
//TODO:允许主线程访问,后面再优化
|
||||
val all: List<Sender> = senderDao.getAll2()
|
||||
|
||||
fun deleteAll() {
|
||||
senderDao.deleteAll()
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.database.repository
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.idormy.sms.forwarder.database.dao.SenderDao
|
||||
import com.idormy.sms.forwarder.database.entity.Sender
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class SenderRepository(private val senderDao: SenderDao) {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@WorkerThread
|
||||
fun insert(sender: Sender) = senderDao.insert(sender)
|
||||
|
||||
@WorkerThread
|
||||
fun delete(id: Long) {
|
||||
listener?.onDelete(id)
|
||||
senderDao.delete(id)
|
||||
}
|
||||
|
||||
fun get(id: Long) = senderDao.get(id)
|
||||
|
||||
fun getOne(id: Long) = senderDao.getOne(id)
|
||||
|
||||
fun update(sender: Sender) = senderDao.update(sender)
|
||||
|
||||
val count: Flow<Long> = senderDao.getOnCount()
|
||||
|
||||
//TODO:允许主线程访问,后面再优化
|
||||
val all: List<Sender> = senderDao.getAll2()
|
||||
|
||||
fun deleteAll() {
|
||||
senderDao.deleteAll()
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +1,45 @@
|
||||
package com.idormy.sms.forwarder.database.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.idormy.sms.forwarder.database.AppDatabase
|
||||
|
||||
class BaseViewModelFactory(private val context: Context?) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
|
||||
if (context == null) throw IllegalArgumentException("Context CAN NOT BE null")
|
||||
|
||||
when {
|
||||
modelClass.isAssignableFrom(FrpcViewModel::class.java) -> {
|
||||
val frpcDao = AppDatabase.getInstance(context).frpcDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return FrpcViewModel(frpcDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(LogsViewModel::class.java) -> {
|
||||
val logDao = AppDatabase.getInstance(context).logsDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return LogsViewModel(logDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(RuleViewModel::class.java) -> {
|
||||
val ruleDao = AppDatabase.getInstance(context).ruleDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return RuleViewModel(ruleDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(SenderViewModel::class.java) -> {
|
||||
val senderDao = AppDatabase.getInstance(context).senderDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return SenderViewModel(senderDao) as T
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.database.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.idormy.sms.forwarder.database.AppDatabase
|
||||
|
||||
class BaseViewModelFactory(private val context: Context?) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
|
||||
if (context == null) throw IllegalArgumentException("Context CAN NOT BE null")
|
||||
|
||||
when {
|
||||
modelClass.isAssignableFrom(FrpcViewModel::class.java) -> {
|
||||
val frpcDao = AppDatabase.getInstance(context).frpcDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return FrpcViewModel(frpcDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(MsgViewModel::class.java) -> {
|
||||
val msgDao = AppDatabase.getInstance(context).msgDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return MsgViewModel(msgDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(LogsViewModel::class.java) -> {
|
||||
val logDao = AppDatabase.getInstance(context).logsDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return LogsViewModel(logDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(RuleViewModel::class.java) -> {
|
||||
val ruleDao = AppDatabase.getInstance(context).ruleDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return RuleViewModel(ruleDao) as T
|
||||
}
|
||||
modelClass.isAssignableFrom(SenderViewModel::class.java) -> {
|
||||
val senderDao = AppDatabase.getInstance(context).senderDao()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return SenderViewModel(senderDao) as T
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.idormy.sms.forwarder.database.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.idormy.sms.forwarder.database.dao.MsgDao
|
||||
import com.idormy.sms.forwarder.database.entity.MsgAndLogs
|
||||
import com.idormy.sms.forwarder.database.ext.ioThread
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class MsgViewModel(private val dao: MsgDao) : ViewModel() {
|
||||
private var type: String = "sms"
|
||||
|
||||
fun setType(type: String): MsgViewModel {
|
||||
this.type = type
|
||||
return this
|
||||
}
|
||||
|
||||
val allMsg: Flow<PagingData<MsgAndLogs>> = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 10,
|
||||
enablePlaceholders = false,
|
||||
initialLoadSize = 10
|
||||
)
|
||||
) {
|
||||
dao.pagingSource(type)
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
|
||||
fun delete(id: Long) = ioThread {
|
||||
dao.delete(id)
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +1,37 @@
|
||||
package com.idormy.sms.forwarder.database.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.idormy.sms.forwarder.database.dao.RuleDao
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||
import com.idormy.sms.forwarder.database.ext.ioThread
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RuleViewModel(private val dao: RuleDao) : ViewModel() {
|
||||
private var type: String = "sms"
|
||||
|
||||
fun setType(type: String): RuleViewModel {
|
||||
this.type = type
|
||||
return this
|
||||
}
|
||||
|
||||
val allRules: Flow<PagingData<RuleAndSender>> = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 10,
|
||||
enablePlaceholders = false,
|
||||
initialLoadSize = 10
|
||||
)
|
||||
) { dao.pagingSource(type) }.flow.cachedIn(viewModelScope)
|
||||
|
||||
fun insertOrUpdate(rule: Rule) = ioThread {
|
||||
if (rule.id > 0) dao.update(rule) else dao.insert(rule)
|
||||
}
|
||||
|
||||
fun delete(id: Long) = ioThread {
|
||||
dao.delete(id)
|
||||
}
|
||||
package com.idormy.sms.forwarder.database.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.idormy.sms.forwarder.database.dao.RuleDao
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.database.ext.ioThread
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RuleViewModel(private val dao: RuleDao) : ViewModel() {
|
||||
private var type: String = "sms"
|
||||
|
||||
fun setType(type: String): RuleViewModel {
|
||||
this.type = type
|
||||
return this
|
||||
}
|
||||
|
||||
val allRules: Flow<PagingData<Rule>> = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 10,
|
||||
enablePlaceholders = false,
|
||||
initialLoadSize = 10
|
||||
)
|
||||
) { dao.pagingSource(type) }.flow.cachedIn(viewModelScope)
|
||||
|
||||
fun insertOrUpdate(rule: Rule) = ioThread {
|
||||
if (rule.id > 0) dao.update(rule) else dao.insert(rule)
|
||||
}
|
||||
|
||||
fun delete(id: Long) = ioThread {
|
||||
dao.delete(id)
|
||||
}
|
||||
}
|
@ -1,139 +1,160 @@
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.LogsPagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.LogsViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentLogsBinding
|
||||
import com.idormy.sms.forwarder.utils.EVENT_UPDATE_LOGS_TYPE
|
||||
import com.idormy.sms.forwarder.utils.FORWARD_STATUS_MAP
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xrouter.utils.TextUtils
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "转发日志")
|
||||
class LogsFragment : BaseFragment<FragmentLogsBinding?>(), LogsPagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = LogsFragment::class.java.simpleName
|
||||
private var adapter = LogsPagingAdapter(this)
|
||||
private val viewModel by viewModels<LogsViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentLogsBinding {
|
||||
return FragmentLogsBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
binding!!.recyclerView.isFocusableInTouchMode = false
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.type_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
currentType = when (position) {
|
||||
1 -> "call"
|
||||
2 -> "app"
|
||||
else -> "sms"
|
||||
}
|
||||
viewModel.setType(currentType)
|
||||
LiveEventBus.get(EVENT_UPDATE_LOGS_TYPE, String::class.java).post(currentType)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
//adapter.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setType(currentType).allLogs.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: LogsAndRuleAndSender) {
|
||||
val ruleStr = StringBuilder()
|
||||
ruleStr.append(Rule.getRuleMatch(item.relation.rule.filed, item.relation.rule.check, item.relation.rule.value, item.relation.rule.simSlot)).append(item.relation.sender.name)
|
||||
val detailStr = StringBuilder()
|
||||
detailStr.append(ResUtils.getString(R.string.from)).append(item.logs.from).append("\n\n")
|
||||
detailStr.append(ResUtils.getString(R.string.msg)).append(item.logs.content).append("\n\n")
|
||||
if (!TextUtils.isEmpty(item.logs.simInfo)) detailStr.append(ResUtils.getString(R.string.slot)).append(item.logs.simInfo).append("\n\n")
|
||||
detailStr.append(ResUtils.getString(R.string.rule)).append(ruleStr.toString()).append("\n\n")
|
||||
@SuppressLint("SimpleDateFormat") val utcFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
detailStr.append(ResUtils.getString(R.string.time)).append(DateUtils.date2String(item.logs.time, utcFormatter)).append("\n\n")
|
||||
detailStr.append(ResUtils.getString(R.string.result)).append(FORWARD_STATUS_MAP[item.logs.forwardStatus]).append("\n--------------------\n").append(item.logs.forwardResponse)
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.iconRes(item.logs.simImageId)
|
||||
.title(R.string.details)
|
||||
.content(detailStr.toString())
|
||||
.cancelable(true)
|
||||
.positiveText(R.string.del)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
viewModel.delete(item.logs.id)
|
||||
XToastUtils.success(R.string.delete_log_toast)
|
||||
}
|
||||
.negativeText(R.string.resend)
|
||||
.onNegative { _: MaterialDialog?, _: DialogAction? ->
|
||||
XToastUtils.toast(R.string.resend_toast)
|
||||
SendUtils.resendMsg(item, false)
|
||||
}
|
||||
.neutralText(R.string.rematch)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
XToastUtils.toast(R.string.rematch_toast)
|
||||
SendUtils.resendMsg(item, true)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
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.MsgPagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.LogsDetail
|
||||
import com.idormy.sms.forwarder.database.entity.MsgAndLogs
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.MsgViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentLogsBinding
|
||||
import com.idormy.sms.forwarder.utils.EVENT_UPDATE_LOGS_TYPE
|
||||
import com.idormy.sms.forwarder.utils.FORWARD_STATUS_MAP
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import com.xuexiang.xutil.data.DateUtils
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "转发日志")
|
||||
class LogsFragment : BaseFragment<FragmentLogsBinding?>(), MsgPagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = LogsFragment::class.java.simpleName
|
||||
private var adapter = MsgPagingAdapter(this)
|
||||
private val viewModel by viewModels<MsgViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentLogsBinding {
|
||||
return FragmentLogsBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
binding!!.recyclerView.isFocusableInTouchMode = false
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.type_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
currentType = when (position) {
|
||||
1 -> "call"
|
||||
2 -> "app"
|
||||
else -> "sms"
|
||||
}
|
||||
viewModel.setType(currentType)
|
||||
LiveEventBus.get(EVENT_UPDATE_LOGS_TYPE, String::class.java).post(currentType)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
//adapter.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setType(currentType).allMsg.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: MsgAndLogs) {
|
||||
Log.d(TAG, "item: $item")
|
||||
|
||||
val detailStr = StringBuilder()
|
||||
detailStr.append(ResUtils.getString(R.string.from)).append(item.msg.from).append("\n\n")
|
||||
detailStr.append(ResUtils.getString(R.string.msg)).append(item.msg.content).append("\n\n")
|
||||
if (!TextUtils.isEmpty(item.msg.simInfo)) detailStr.append(ResUtils.getString(R.string.slot)).append(item.msg.simInfo).append("\n\n")
|
||||
@SuppressLint("SimpleDateFormat") val utcFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
detailStr.append(ResUtils.getString(R.string.time)).append(DateUtils.date2String(item.msg.time, utcFormatter))
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.iconRes(item.msg.simImageId)
|
||||
.title(R.string.details)
|
||||
.content(detailStr.toString())
|
||||
.cancelable(true)
|
||||
.positiveText(R.string.del)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
viewModel.delete(item.msg.id)
|
||||
XToastUtils.success(R.string.delete_log_toast)
|
||||
}
|
||||
.neutralText(R.string.rematch)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { _: MaterialDialog?, _: DialogAction? ->
|
||||
XToastUtils.toast(R.string.rematch_toast)
|
||||
//SendUtils.resendMsg(item, true)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onLogsClicked(view: View?, item: LogsDetail) {
|
||||
Log.d(TAG, "item: $item")
|
||||
val ruleStr = StringBuilder()
|
||||
ruleStr.append(Rule.getRuleMatch(item.ruleFiled, item.ruleCheck, item.ruleValue, item.ruleSimSlot)).append(item.senderName)
|
||||
val detailStr = StringBuilder()
|
||||
detailStr.append(ResUtils.getString(R.string.rule)).append(ruleStr.toString()).append("\n\n")
|
||||
@SuppressLint("SimpleDateFormat") val utcFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
detailStr.append(ResUtils.getString(R.string.time)).append(DateUtils.date2String(item.time, utcFormatter)).append("\n\n")
|
||||
detailStr.append(ResUtils.getString(R.string.result)).append(FORWARD_STATUS_MAP[item.forwardStatus]).append("\n--------------------\n").append(item.forwardResponse)
|
||||
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.details)
|
||||
.content(detailStr.toString())
|
||||
.cancelable(true)
|
||||
.positiveText(R.string.del)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
viewModel.delete(item.id)
|
||||
XToastUtils.success(R.string.delete_log_toast)
|
||||
}
|
||||
.negativeText(R.string.resend)
|
||||
.onNegative { _: MaterialDialog?, _: DialogAction? ->
|
||||
XToastUtils.toast(R.string.resend_toast)
|
||||
//SendUtils.resendMsg(item, false)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,131 +1,129 @@
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.util.Log
|
||||
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.RulePagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.RuleViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentRulesBinding
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "转发规则")
|
||||
class RulesFragment : BaseFragment<FragmentRulesBinding?>(), RulePagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = RulesFragment::class.java.simpleName
|
||||
private var adapter = RulePagingAdapter(this)
|
||||
private val viewModel by viewModels<RuleViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentRulesBinding {
|
||||
return FragmentRulesBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.type_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
currentType = when (position) {
|
||||
1 -> "call"
|
||||
2 -> "app"
|
||||
else -> "sms"
|
||||
}
|
||||
viewModel.setType(currentType)
|
||||
LiveEventBus.get(EVENT_UPDATE_RULE_TYPE, String::class.java).post(currentType)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
//adapter!!.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setType(currentType).allRules.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: RuleAndSender) {
|
||||
Log.e(TAG, item.toString())
|
||||
when (view?.id) {
|
||||
R.id.iv_copy -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.setNewActivity(true)
|
||||
.putLong(KEY_RULE_ID, item.rule.id)
|
||||
.putString(KEY_RULE_TYPE, item.rule.type)
|
||||
.putBoolean(KEY_RULE_CLONE, true)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_edit -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.setNewActivity(true)
|
||||
.putLong(KEY_RULE_ID, item.rule.id)
|
||||
.putString(KEY_RULE_TYPE, item.rule.type)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_delete -> {
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.delete_rule_title)
|
||||
.content(R.string.delete_rule_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
viewModel.delete(item.rule.id)
|
||||
XToastUtils.success(R.string.delete_rule_toast)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
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.RulePagingAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.RuleViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentRulesBinding
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.core.PageOption
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Page(name = "转发规则")
|
||||
class RulesFragment : BaseFragment<FragmentRulesBinding?>(), RulePagingAdapter.OnItemClickListener {
|
||||
|
||||
val TAG: String = RulesFragment::class.java.simpleName
|
||||
private var adapter = RulePagingAdapter(this)
|
||||
private val viewModel by viewModels<RuleViewModel> { BaseViewModelFactory(context) }
|
||||
private var currentType: String = "sms"
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentRulesBinding {
|
||||
return FragmentRulesBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
override fun initTitle(): TitleBar? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
val virtualLayoutManager = VirtualLayoutManager(requireContext())
|
||||
binding!!.recyclerView.layoutManager = virtualLayoutManager
|
||||
val viewPool = RecycledViewPool()
|
||||
binding!!.recyclerView.setRecycledViewPool(viewPool)
|
||||
viewPool.setMaxRecycledViews(0, 10)
|
||||
|
||||
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.type_param_option))
|
||||
binding!!.tabBar.setOnTabClickListener { _, position ->
|
||||
//XToastUtils.toast("点击了$title--$position")
|
||||
currentType = when (position) {
|
||||
1 -> "call"
|
||||
2 -> "app"
|
||||
else -> "sms"
|
||||
}
|
||||
viewModel.setType(currentType)
|
||||
LiveEventBus.get(EVENT_UPDATE_RULE_TYPE, String::class.java).post(currentType)
|
||||
adapter.refresh()
|
||||
binding!!.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
binding!!.recyclerView.adapter = adapter
|
||||
|
||||
//下拉刷新
|
||||
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
|
||||
refreshLayout.layout.postDelayed({
|
||||
//adapter!!.refresh()
|
||||
lifecycleScope.launch {
|
||||
viewModel.setType(currentType).allRules.collectLatest { adapter.submitData(it) }
|
||||
}
|
||||
refreshLayout.finishRefresh()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
binding!!.refreshLayout.autoRefresh()
|
||||
}
|
||||
|
||||
override fun onItemClicked(view: View?, item: Rule) {
|
||||
when (view?.id) {
|
||||
R.id.iv_copy -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.setNewActivity(true)
|
||||
.putLong(KEY_RULE_ID, item.id)
|
||||
.putString(KEY_RULE_TYPE, item.type)
|
||||
.putBoolean(KEY_RULE_CLONE, true)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_edit -> {
|
||||
PageOption.to(RulesEditFragment::class.java)
|
||||
.setNewActivity(true)
|
||||
.putLong(KEY_RULE_ID, item.id)
|
||||
.putString(KEY_RULE_TYPE, item.type)
|
||||
.open(this)
|
||||
}
|
||||
R.id.iv_delete -> {
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.title(R.string.delete_rule_title)
|
||||
.content(R.string.delete_rule_tips)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
viewModel.delete(item.id)
|
||||
XToastUtils.success(R.string.delete_rule_toast)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRemove(view: View?, id: Int) {}
|
||||
|
||||
}
|
@ -1,141 +1,141 @@
|
||||
package com.idormy.sms.forwarder.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.BatteryUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.Worker
|
||||
import com.idormy.sms.forwarder.workers.SendWorker
|
||||
import java.util.*
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class BatteryService : Service() {
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.i(TAG, "onCreate--------------")
|
||||
|
||||
//纯客户端模式
|
||||
//if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
val batteryFilter = IntentFilter()
|
||||
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
|
||||
registerReceiver(batteryReceiver, batteryFilter)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
Log.i(TAG, "onStartCommand--------------")
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(TAG, "onDestroy--------------")
|
||||
super.onDestroy()
|
||||
|
||||
//纯客户端模式
|
||||
//if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
unregisterReceiver(batteryReceiver)
|
||||
}
|
||||
|
||||
// 接收电池信息更新的广播
|
||||
private val batteryReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
//自动删除N天前的转发记录
|
||||
if (SettingUtils.autoCleanLogsDays > 0) {
|
||||
Log.d(TAG, "自动删除N天前的转发记录")
|
||||
val cal = Calendar.getInstance()
|
||||
cal.add(Calendar.DAY_OF_MONTH, 0 - SettingUtils.autoCleanLogsDays)
|
||||
Core.logs.deleteTimeAgo(cal.timeInMillis)
|
||||
}
|
||||
|
||||
//电量发生变化
|
||||
val levelCur: Int = intent.getIntExtra("level", 0)
|
||||
val levelPre: Int = SettingUtils.batteryLevelCurrent
|
||||
if (levelCur != levelPre) {
|
||||
var msg: String = BatteryUtils.getBatteryInfo(intent).toString()
|
||||
SettingUtils.batteryLevelCurrent = levelCur
|
||||
val levelMin: Int = SettingUtils.batteryLevelMin
|
||||
val levelMax: Int = SettingUtils.batteryLevelMax
|
||||
if (SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur <= levelMin) { //电量下降到下限
|
||||
msg = String.format(getString(R.string.below_level_min), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur >= levelMax) { //电量上升到上限
|
||||
msg = String.format(getString(R.string.over_level_max), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (!SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur == levelMin) { //电量下降到下限
|
||||
msg = String.format(getString(R.string.reach_level_min), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (!SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur == levelMax) { //电量上升到上限
|
||||
msg = String.format(getString(R.string.reach_level_max), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//充电状态改变
|
||||
val status: Int = intent.getIntExtra("status", 0)
|
||||
if (SettingUtils.enableBatteryReceiver) {
|
||||
val oldStatus: Int = SettingUtils.batteryStatus
|
||||
if (status != oldStatus) {
|
||||
var msg: String = BatteryUtils.getBatteryInfo(intent).toString()
|
||||
SettingUtils.batteryStatus = status
|
||||
msg = getString(R.string.battery_status_changed) + BatteryUtils.getStatus(
|
||||
oldStatus
|
||||
) + " → " + BatteryUtils.getStatus(status) + msg
|
||||
sendMessage(context, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//发送信息
|
||||
private fun sendMessage(context: Context, msg: String) {
|
||||
Log.i(TAG, msg)
|
||||
try {
|
||||
val msgInfo = MsgInfo(
|
||||
"app",
|
||||
"88888888",
|
||||
msg,
|
||||
Date(),
|
||||
getString(R.string.battery_status_monitor),
|
||||
-1
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo),
|
||||
)
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getLog e:" + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BatteryReceiver"
|
||||
}
|
||||
package com.idormy.sms.forwarder.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.BatteryUtils
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.Worker
|
||||
import com.idormy.sms.forwarder.workers.SendWorker
|
||||
import java.util.*
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class BatteryService : Service() {
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.i(TAG, "onCreate--------------")
|
||||
|
||||
//纯客户端模式
|
||||
//if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
val batteryFilter = IntentFilter()
|
||||
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
|
||||
registerReceiver(batteryReceiver, batteryFilter)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
Log.i(TAG, "onStartCommand--------------")
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(TAG, "onDestroy--------------")
|
||||
super.onDestroy()
|
||||
|
||||
//纯客户端模式
|
||||
//if (SettingUtils.enablePureClientMode) return
|
||||
|
||||
unregisterReceiver(batteryReceiver)
|
||||
}
|
||||
|
||||
// 接收电池信息更新的广播
|
||||
private val batteryReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
//自动删除N天前的转发记录
|
||||
if (SettingUtils.autoCleanLogsDays > 0) {
|
||||
Log.d(TAG, "自动删除N天前的转发记录")
|
||||
val cal = Calendar.getInstance()
|
||||
cal.add(Calendar.DAY_OF_MONTH, 0 - SettingUtils.autoCleanLogsDays)
|
||||
Core.msg.deleteTimeAgo(cal.timeInMillis)
|
||||
}
|
||||
|
||||
//电量发生变化
|
||||
val levelCur: Int = intent.getIntExtra("level", 0)
|
||||
val levelPre: Int = SettingUtils.batteryLevelCurrent
|
||||
if (levelCur != levelPre) {
|
||||
var msg: String = BatteryUtils.getBatteryInfo(intent).toString()
|
||||
SettingUtils.batteryLevelCurrent = levelCur
|
||||
val levelMin: Int = SettingUtils.batteryLevelMin
|
||||
val levelMax: Int = SettingUtils.batteryLevelMax
|
||||
if (SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur <= levelMin) { //电量下降到下限
|
||||
msg = String.format(getString(R.string.below_level_min), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur >= levelMax) { //电量上升到上限
|
||||
msg = String.format(getString(R.string.over_level_max), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (!SettingUtils.batteryLevelOnce && levelMin > 0 && levelPre > levelCur && levelCur == levelMin) { //电量下降到下限
|
||||
msg = String.format(getString(R.string.reach_level_min), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
} else if (!SettingUtils.batteryLevelOnce && levelMax > 0 && levelPre < levelCur && levelCur == levelMax) { //电量上升到上限
|
||||
msg = String.format(getString(R.string.reach_level_max), msg)
|
||||
sendMessage(context, msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//充电状态改变
|
||||
val status: Int = intent.getIntExtra("status", 0)
|
||||
if (SettingUtils.enableBatteryReceiver) {
|
||||
val oldStatus: Int = SettingUtils.batteryStatus
|
||||
if (status != oldStatus) {
|
||||
var msg: String = BatteryUtils.getBatteryInfo(intent).toString()
|
||||
SettingUtils.batteryStatus = status
|
||||
msg = getString(R.string.battery_status_changed) + BatteryUtils.getStatus(
|
||||
oldStatus
|
||||
) + " → " + BatteryUtils.getStatus(status) + msg
|
||||
sendMessage(context, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//发送信息
|
||||
private fun sendMessage(context: Context, msg: String) {
|
||||
Log.i(TAG, msg)
|
||||
try {
|
||||
val msgInfo = MsgInfo(
|
||||
"app",
|
||||
"88888888",
|
||||
msg,
|
||||
Date(),
|
||||
getString(R.string.battery_status_monitor),
|
||||
-1
|
||||
)
|
||||
val request = OneTimeWorkRequestBuilder<SendWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
Worker.sendMsgInfo to Gson().toJson(msgInfo),
|
||||
)
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "getLog e:" + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BatteryReceiver"
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
|
||||
/**
|
||||
* Created by aykutasil on 8.12.2016.
|
||||
*/
|
||||
|
||||
@Suppress("unused")
|
||||
class PrefsHelper private constructor() {
|
||||
|
||||
lateinit var preference: SharedPreferences
|
||||
|
||||
val prefEditor: SharedPreferences.Editor
|
||||
get() = preference.edit()
|
||||
|
||||
constructor(context: Context, prefName: String) : this() {
|
||||
preference = context.getSharedPreferences(prefName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
constructor(context: Context) : this() {
|
||||
preference = getDefaultPreference(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val DEFAULT_STRING_VALUE: String? = null
|
||||
private const val DEFAULT_INT_VALUE = 0
|
||||
private const val DEFAULT_BOOLEAN_VALUE = false
|
||||
|
||||
fun getDefaultPreference(context: Context): SharedPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
||||
fun writePrefString(context: Context, key: String, value: String?) {
|
||||
PrefsHelper(context).prefEditor.putString(key, value).commit()
|
||||
}
|
||||
|
||||
fun readPrefString(context: Context, key: String): String? {
|
||||
return PrefsHelper(context).preference.getString(key, DEFAULT_STRING_VALUE)
|
||||
}
|
||||
|
||||
fun writePrefInt(context: Context, key: String, value: Int) {
|
||||
PrefsHelper(context).prefEditor.putInt(key, value).commit()
|
||||
}
|
||||
|
||||
fun readPrefInt(context: Context, key: String): Int {
|
||||
return PrefsHelper(context).preference.getInt(key, DEFAULT_INT_VALUE)
|
||||
}
|
||||
|
||||
fun writePrefBool(context: Context, key: String, value: Boolean) {
|
||||
PrefsHelper(context).prefEditor.putBoolean(key, value).commit()
|
||||
}
|
||||
|
||||
fun readPrefBool(context: Context, key: String): Boolean {
|
||||
return PrefsHelper(context).preference.getBoolean(key, DEFAULT_BOOLEAN_VALUE)
|
||||
}
|
||||
|
||||
fun clearPreference(context: Context) {
|
||||
PrefsHelper(context).preference.edit().clear().apply()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.idormy.sms.forwarder.workers
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.database.entity.Logs
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.utils.SendUtils
|
||||
import com.idormy.sms.forwarder.utils.Worker
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SendLogicWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters,
|
||||
) : CoroutineWorker(context, workerParams) {
|
||||
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
|
||||
|
||||
val msgInfoJson = inputData.getString(Worker.sendMsgInfo)
|
||||
val msgInfo = Gson().fromJson(msgInfoJson, MsgInfo::class.java)
|
||||
val ruleId = inputData.getLong(Worker.ruleId, 0L)
|
||||
val senderIndex = inputData.getInt(Worker.senderIndex, 0)
|
||||
val msgId = inputData.getLong(Worker.msgId, 0L)
|
||||
|
||||
val rule = Core.rule.getOne(ruleId)
|
||||
val sender = rule.senderList[senderIndex]
|
||||
val log = Logs(0, rule.type, msgId, rule.id, sender.id)
|
||||
val logId = Core.logs.insert(log)
|
||||
SendUtils.sendMsgSender(msgInfo, rule, senderIndex, logId, msgId)
|
||||
|
||||
return@withContext Result.success()
|
||||
}
|
||||
|
||||
}
|
@ -1,92 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.xuexiang.xui.widget.layout.XUIFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_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_status_image"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="-16dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
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_from"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sim_image"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="5dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="3dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxEms="8"
|
||||
android:maxLines="3"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.xuexiang.xui.widget.layout.XUIFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
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_from"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sim_image"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="5dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="3dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxEms="8"
|
||||
android:maxLines="3"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:background="?attr/xui_config_color_separator_light" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_Logs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"></LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.xuexiang.xui.widget.layout.XUIFrameLayout>
|
@ -1,124 +1,99 @@
|
||||
<?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:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_rule_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_rule_status"
|
||||
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_rule_match"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxEms="8"
|
||||
android:maxLines="3"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="@dimen/card_view_image_size"
|
||||
android:layout_height="@dimen/card_view_image_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_status"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sender_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxEms="6"
|
||||
android:maxLines="1"
|
||||
android:textSize="9sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_copy"
|
||||
android:layout_width="@dimen/card_view_image_size"
|
||||
android:layout_height="@dimen/card_view_image_size"
|
||||
android:layout_marginStart="10dp"
|
||||
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:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_rule_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_rule_status"
|
||||
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_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_rule_match"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="5dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxEms="8"
|
||||
android:maxLines="3"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_Senders"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:orientation="horizontal"></LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_copy"
|
||||
android:layout_width="@dimen/card_view_image_size"
|
||||
android:layout_height="@dimen/card_view_image_size"
|
||||
android:layout_marginStart="10dp"
|
||||
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>
|
||||
|
||||
</com.xuexiang.xui.widget.layout.XUIFrameLayout>
|
File diff suppressed because it is too large
Load Diff
@ -1,262 +1,277 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/xui_config_color_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyleWithSwitch"
|
||||
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/sender_name_status"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
android:id="@+id/sb_enable"
|
||||
style="@style/SwitchButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true" />
|
||||
|
||||
</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/Method"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_method"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_post"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/post" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_get"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/get" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_put"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/put" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_patch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/patch" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
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/webhook_server"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/webhook_server_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_webServer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@string/webhook_params'
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/webhook_params_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_webParams"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/optional"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/webhook_secret"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_Secret"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/optional"
|
||||
android:singleLine="true"
|
||||
app:met_passWordButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_Headers"
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/headers"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.ShadowButton
|
||||
android:id="@+id/btn_add_header"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="@drawable/icon_add"
|
||||
app:sb_shape_type="round" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_del"
|
||||
style="@style/SuperButton.Gray.Icon"
|
||||
android:drawableStart="@drawable/icon_delete"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/del"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_save"
|
||||
style="@style/SuperButton.Blue.Icon"
|
||||
android:layout_marginStart="10dp"
|
||||
android:drawableStart="@drawable/icon_save"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/save"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_test"
|
||||
style="@style/SuperButton.Green.Icon"
|
||||
android:layout_marginStart="10dp"
|
||||
android:drawableStart="@drawable/icon_test"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/test"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/xui_config_color_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyleWithSwitch"
|
||||
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/sender_name_status"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
android:id="@+id/sb_enable"
|
||||
style="@style/SwitchButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true" />
|
||||
|
||||
</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/Method"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_method"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_post"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:scaleX="0.7"
|
||||
android:scaleY="0.7"
|
||||
android:text="@string/post"
|
||||
android:textSize="14sp"
|
||||
android:translationX="18dp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_get"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleX="0.7"
|
||||
android:scaleY="0.7"
|
||||
android:text="@string/get"
|
||||
android:textSize="14sp"
|
||||
android:translationX="18dp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_put"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleX="0.7"
|
||||
android:scaleY="0.7"
|
||||
android:text="@string/put"
|
||||
android:textSize="14sp"
|
||||
android:translationX="18dp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_method_patch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleX="0.7"
|
||||
android:scaleY="0.7"
|
||||
android:text="@string/patch"
|
||||
android:textSize="14sp"
|
||||
android:translationX="18dp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
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/webhook_server"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/webhook_server_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_webServer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@string/webhook_params'
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/webhook_params_tips"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_webParams"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/optional"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/webhook_secret"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_Secret"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/optional"
|
||||
android:singleLine="true"
|
||||
app:met_passWordButton="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_Headers"
|
||||
style="@style/senderBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/headers"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.ShadowButton
|
||||
android:id="@+id/btn_add_header"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="@drawable/icon_add"
|
||||
app:sb_shape_type="round" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_del"
|
||||
style="@style/SuperButton.Gray.Icon"
|
||||
android:drawableStart="@drawable/icon_delete"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/del"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_save"
|
||||
style="@style/SuperButton.Blue.Icon"
|
||||
android:layout_marginStart="10dp"
|
||||
android:drawableStart="@drawable/icon_save"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/save"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_test"
|
||||
style="@style/SuperButton.Green.Icon"
|
||||
android:layout_marginStart="10dp"
|
||||
android:drawableStart="@drawable/icon_test"
|
||||
android:paddingStart="15dp"
|
||||
android:text="@string/test"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/xui_config_color_white"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_status"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sender_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:minHeight="?attr/ms_item_height_size"
|
||||
android:paddingStart="?attr/ms_padding_left_size"
|
||||
android:paddingTop="?attr/ms_padding_top_size"
|
||||
android:paddingEnd="?attr/ms_padding_left_size"
|
||||
android:paddingBottom="?attr/ms_padding_top_size"
|
||||
android:singleLine="true"
|
||||
tools:ignore="PrivateResource" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_remove_sender"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:contentDescription="@string/sender_del"
|
||||
android:src="@drawable/icon_delete"
|
||||
app:tint="@color/design_default_color_error" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/xui_config_color_separator_light" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_status"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
tools:ignore="ContentDescription,VisualLintBounds" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sender_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxEms="10"
|
||||
android:maxLines="1"
|
||||
android:textSize="9sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sender_status"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="-10dp"
|
||||
tools:ignore="ContentDescription,VisualLintBounds" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sender_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxEms="10"
|
||||
android:maxLines="1"
|
||||
android:textSize="9sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue