整理:code review & 精简无用资源

pull/335/head
pppscn 1 year ago
parent 8953981d4e
commit f5de522967

@ -249,7 +249,7 @@ dependencies {
//MarkdownViewhttps://github.com/tiagohm/MarkdownView
implementation 'com.github.tiagohm.MarkdownView:library:0.19.0'
implementation 'com.github.tiagohm.MarkdownView:emoji:0.19.0'
//implementation 'com.github.tiagohm.MarkdownView:emoji:0.19.0'
def retrofit2_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version"

@ -1,39 +0,0 @@
/*
* Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.idormy.sms.forwarder
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* @see [Testing documentation](http://d.android.com/tools/testing)
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Assert.assertEquals("com.idormy.sms.forwarder", appContext.packageName)
}
}

@ -1,17 +1,17 @@
{
"Code": 0,
"Data": [
{
"title": "新用户必读",
"content": "开始设置之前,请您认真地看一遍 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages\"><font color=\"#800080\">Wiki</font></a> <br />\n遇到问题请按照 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4877445&doc_id=1821427\"><font color=\"#0000FF\">常见问题</font></a> 章节进行排查!<br />\n没找到答案的再加入QQ互助交流群里提问请清楚地描述问题并给出对应的配置截图与相关日志方便大家直观的判断问题 "
},
{
"title": "QQ互助交流群",
"content": "<a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=Mj5m39bqy6eodOImrFLI19Tdeqvv-9zf\">QQ互助交流①群</a><br /><a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=jPXy4YaUzA7Uo0yPPbZXdkb66NS1smU_\">QQ互助交流②群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=itGVH4lB-HLGyJGTfP_5rjyCQj6kgIBt\">QQ互助交流③群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=83fYtikg2ARpUECsgJv9CcWTKQB74REK\">QQ互助交流④群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=CcamLcA-QVN-KqCDjeMZqdTx8IGlJrVx\">QQ互助交流⑤群</a>"
},
{
"title": "打赏名单",
"content": "感谢热心网友们对开源项目的喜爱和支持!<a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427\"><font color=\"#800080\">查看赞助名单!</font></a>"
}
]
}
{
"Code": 0,
"Data": [
{
"title": "新用户必读",
"content": "开始设置之前,请您认真地看一遍 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages\"><font color=\"#800080\">Wiki</font></a> <br />\n遇到问题请按照 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4877445&doc_id=1821427\"><font color=\"#0000FF\">常见问题</font></a> 章节进行排查!<br />\n没找到答案的再加入QQ互助交流群里提问请清楚地描述问题并给出对应的配置截图与相关日志方便大家直观的判断问题 "
},
{
"title": "互助交流群",
"content": "<a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=Mj5m39bqy6eodOImrFLI19Tdeqvv-9zf\">QQ互助交流①群</a><br /><a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=jPXy4YaUzA7Uo0yPPbZXdkb66NS1smU_\">QQ互助交流②群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=itGVH4lB-HLGyJGTfP_5rjyCQj6kgIBt\">QQ互助交流③群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=83fYtikg2ARpUECsgJv9CcWTKQB74REK\">QQ互助交流④群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=CcamLcA-QVN-KqCDjeMZqdTx8IGlJrVx\">QQ互助交流⑤群</a>"
},
{
"title": "打赏名单",
"content": "感谢热心网友们对开源项目的喜爱和支持!<a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427\"><font color=\"#800080\">查看赞助名单!</font></a>"
}
]
}

@ -40,7 +40,6 @@ import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
@Suppress("PrivatePropertyName")
class App : Application(), CactusCallback, Configuration.Provider by Core {
val applicationScope = CoroutineScope(SupervisorJob())

@ -59,7 +59,6 @@ import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File
@Suppress("DEPRECATION", "PrivatePropertyName")
class MainActivity : BaseActivity<ActivityMainBinding?>(),
View.OnClickListener,

@ -12,6 +12,7 @@ import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
import com.idormy.sms.forwarder.databinding.AdapterLogsCardViewListItemBinding
import com.xuexiang.xutil.data.DateUtils
@Suppress("unused")
class LogsPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<LogsAndRuleAndSender, MyViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {

@ -36,7 +36,7 @@ abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapt
}
constructor(data: Array<T>?) {
if (data != null && data.isNotEmpty()) {
if (!data.isNullOrEmpty()) {
mData.addAll(listOf(*data))
}
}
@ -180,7 +180,7 @@ abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapt
*/
@SuppressLint("NotifyDataSetChanged")
fun refresh(array: Array<T>?): XDelegateAdapter<*, *> {
if (array != null && array.isNotEmpty()) {
if (!array.isNullOrEmpty()) {
mData.clear()
mData.addAll(listOf(*array))
selectPosition = -1
@ -212,7 +212,7 @@ abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapt
*/
@SuppressLint("NotifyDataSetChanged")
fun loadMore(array: Array<T>?): XDelegateAdapter<*, *> {
if (array != null && array.isNotEmpty()) {
if (!array.isNullOrEmpty()) {
mData.addAll(listOf(*array))
notifyDataSetChanged()
}

@ -1,45 +1,40 @@
package com.idormy.sms.forwarder.adapter.spinner
import android.graphics.drawable.Drawable
import com.xuexiang.xui.utils.ResUtils
@Suppress("unused")
class AppListAdapterItem {
var name: String = ""
var icon: Drawable? = null
var packageName: String? = null
//var packagePath: String? = null
//var versionName: String? = null
//var versionCode: Int = 0
//var isSystem: Boolean = false
constructor(name: String, icon: Drawable?, packageName: String?) {
this.name = name
this.icon = icon
this.packageName = packageName
}
constructor(name: String) : this(name, null, null)
constructor(name: String, drawableId: Int, packageName: String) : this(name, ResUtils.getDrawable(drawableId), packageName)
//注意自定义实体需要重写对象的toString方法
override fun toString(): String {
return name
}
companion object {
fun of(name: String): AppListAdapterItem {
return AppListAdapterItem(name)
}
fun arrayof(title: Array<String>): Array<AppListAdapterItem?> {
val array = arrayOfNulls<AppListAdapterItem>(title.size)
for (i in array.indices) {
array[i] = AppListAdapterItem(title[i])
}
return array
}
}
}
package com.idormy.sms.forwarder.adapter.spinner
import android.graphics.drawable.Drawable
import com.xuexiang.xui.utils.ResUtils
@Suppress("unused")
class AppListAdapterItem {
var name: String = ""
var icon: Drawable? = null
var packageName: String? = null
constructor(name: String, icon: Drawable?, packageName: String?) {
this.name = name
this.icon = icon
this.packageName = packageName
}
constructor(name: String) : this(name, null, null)
constructor(name: String, drawableId: Int, packageName: String) : this(name, ResUtils.getDrawable(drawableId), packageName)
//注意自定义实体需要重写对象的toString方法
override fun toString(): String {
return name
}
companion object {
fun of(name: String): AppListAdapterItem {
return AppListAdapterItem(name)
}
fun arrayof(title: Array<String>): Array<AppListAdapterItem?> {
val array = arrayOfNulls<AppListAdapterItem>(title.size)
for (i in array.indices) {
array[i] = AppListAdapterItem(title[i])
}
return array
}
}
}

@ -1,157 +1,156 @@
package com.idormy.sms.forwarder.adapter.spinner
import android.annotation.SuppressLint
import android.os.Build
import android.text.Html
import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.idormy.sms.forwarder.R
import com.xuexiang.xui.utils.CollectionUtils
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
@Suppress("unused", "NAME_SHADOWING", "SENSELESS_COMPARISON", "DEPRECATION")
class AppListSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
/**
* 选项的文字颜色
*/
private var mTextColor = 0
/**
* 选项的文字大小
*/
private var mTextSize = 0f
/**
* 背景颜色
*/
private var mBackgroundSelector = 0
/**
* 过滤关键词的选中颜色
*/
private var mFilterColor = "#F15C58"
private var mIsFilterKey = false
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: List<T>?) : super(data)
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: Array<T>?) : super(data)
override fun getEditSpinnerFilter(): EditSpinnerFilter {
return this
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
var convertView = convertView
val holder: ViewHolder
if (convertView == null) {
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
convertView.tag = holder
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as AppListAdapterItem
holder.iconView.setImageDrawable(item.icon)
//holder.titleView.text = Html.fromHtml(item.toString())
holder.titleView.text = Html.fromHtml(getItem(position))
return convertView
}
override fun onFilter(keyword: String): Boolean {
mDisplayData.clear()
Log.d("AppListSpinnerAdapter", "keyword = $keyword")
Log.d("AppListSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
if (TextUtils.isEmpty(keyword)) {
initDisplayData(mDataSource)
for (i in mIndexs.indices) {
mIndexs[i] = i
}
} else {
try {
for (i in mDataSource.indices) {
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
mIndexs[mDisplayData.size] = i
if (mIsFilterKey) {
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
} else {
mDisplayData.add(getDataSourceString(i))
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.d("AppListSpinnerAdapter", "mDisplayData = $mDisplayData")
notifyDataSetChanged()
return mDisplayData.size > 0
}
fun setTextColor(@ColorInt textColor: Int): AppListSpinnerAdapter<*> {
mTextColor = textColor
return this
}
fun setTextSize(textSize: Float): AppListSpinnerAdapter<*> {
mTextSize = textSize
return this
}
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): AppListSpinnerAdapter<*> {
mBackgroundSelector = backgroundSelector
return this
}
fun setFilterColor(filterColor: String): AppListSpinnerAdapter<*> {
mFilterColor = filterColor
return this
}
fun setIsFilterKey(isFilterKey: Boolean): AppListSpinnerAdapter<*> {
mIsFilterKey = isFilterKey
return this
}
@Suppress("DEPRECATION")
@SuppressLint("ObsoleteSdkInt")
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
val titleView: TextView = convertView.findViewById(R.id.tv_title)
init {
if (textColor > 0) titleView.setTextColor(textColor)
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val config = convertView.resources.configuration
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
titleView.textDirection = View.TEXT_DIRECTION_RTL
}
}
}
}
fun getItemSource(position: Int): T {
return mDataSource[mIndexs[position]]
}
}
package com.idormy.sms.forwarder.adapter.spinner
import android.annotation.SuppressLint
import android.os.Build
import android.text.Html
import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.idormy.sms.forwarder.R
import com.xuexiang.xui.utils.CollectionUtils
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
@Suppress("unused", "NAME_SHADOWING", "DEPRECATION")
class AppListSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
/**
* 选项的文字颜色
*/
private var mTextColor = 0
/**
* 选项的文字大小
*/
private var mTextSize = 0f
/**
* 背景颜色
*/
private var mBackgroundSelector = 0
/**
* 过滤关键词的选中颜色
*/
private var mFilterColor = "#F15C58"
private var mIsFilterKey = false
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: List<T>?) : super(data)
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: Array<T>?) : super(data)
override fun getEditSpinnerFilter(): EditSpinnerFilter {
return this
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
var convertView = convertView
val holder: ViewHolder
if (convertView == null) {
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
convertView.tag = holder
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as AppListAdapterItem
holder.iconView.setImageDrawable(item.icon)
//holder.titleView.text = Html.fromHtml(item.toString())
holder.titleView.text = Html.fromHtml(getItem(position))
return convertView
}
override fun onFilter(keyword: String): Boolean {
mDisplayData.clear()
Log.d("AppListSpinnerAdapter", "keyword = $keyword")
Log.d("AppListSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
if (TextUtils.isEmpty(keyword)) {
initDisplayData(mDataSource)
for (i in mIndexs.indices) {
mIndexs[i] = i
}
} else {
try {
for (i in mDataSource.indices) {
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
mIndexs[mDisplayData.size] = i
if (mIsFilterKey) {
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
} else {
mDisplayData.add(getDataSourceString(i))
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.d("AppListSpinnerAdapter", "mDisplayData = $mDisplayData")
notifyDataSetChanged()
return mDisplayData.size > 0
}
fun setTextColor(@ColorInt textColor: Int): AppListSpinnerAdapter<*> {
mTextColor = textColor
return this
}
fun setTextSize(textSize: Float): AppListSpinnerAdapter<*> {
mTextSize = textSize
return this
}
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): AppListSpinnerAdapter<*> {
mBackgroundSelector = backgroundSelector
return this
}
fun setFilterColor(filterColor: String): AppListSpinnerAdapter<*> {
mFilterColor = filterColor
return this
}
fun setIsFilterKey(isFilterKey: Boolean): AppListSpinnerAdapter<*> {
mIsFilterKey = isFilterKey
return this
}
@SuppressLint("ObsoleteSdkInt")
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
val titleView: TextView = convertView.findViewById(R.id.tv_title)
init {
if (textColor > 0) titleView.setTextColor(textColor)
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val config = convertView.resources.configuration
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
titleView.textDirection = View.TEXT_DIRECTION_RTL
}
}
}
}
fun getItemSource(position: Int): T {
return mDataSource[mIndexs[position]]
}
}

@ -1,167 +1,166 @@
package com.idormy.sms.forwarder.adapter.spinner
import android.annotation.SuppressLint
import android.os.Build
import android.text.Html
import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.utils.STATUS_OFF
import com.xuexiang.xui.utils.CollectionUtils
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
@Suppress("unused", "NAME_SHADOWING", "SENSELESS_COMPARISON", "DEPRECATION")
class SenderSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
/**
* 选项的文字颜色
*/
private var mTextColor = 0
/**
* 选项的文字大小
*/
private var mTextSize = 0f
/**
* 背景颜色
*/
private var mBackgroundSelector = 0
/**
* 过滤关键词的选中颜色
*/
private var mFilterColor = "#F15C58"
private var mIsFilterKey = false
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: List<T>?) : super(data)
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: Array<T>?) : super(data)
override fun getEditSpinnerFilter(): EditSpinnerFilter {
return this
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
var convertView = convertView
val holder: ViewHolder
if (convertView == null) {
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
convertView.tag = holder
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderAdapterItem
holder.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable(
ResUtils.getDrawable(
when (item.status) {
STATUS_OFF -> R.drawable.icon_off
else -> R.drawable.icon_on
}
)
)
//holder.titleView.text = Html.fromHtml(item.toString())
holder.titleView.text = Html.fromHtml(getItem(position))
return convertView
}
override fun onFilter(keyword: String): Boolean {
mDisplayData.clear()
Log.d("SenderSpinnerAdapter", "keyword = $keyword")
Log.d("SenderSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
if (TextUtils.isEmpty(keyword)) {
initDisplayData(mDataSource)
for (i in mIndexs.indices) {
mIndexs[i] = i
}
} else {
try {
for (i in mDataSource.indices) {
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
mIndexs[mDisplayData.size] = i
if (mIsFilterKey) {
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
} else {
mDisplayData.add(getDataSourceString(i))
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.d("SenderSpinnerAdapter", "mDisplayData = $mDisplayData")
notifyDataSetChanged()
return mDisplayData.size > 0
}
fun setTextColor(@ColorInt textColor: Int): SenderSpinnerAdapter<*> {
mTextColor = textColor
return this
}
fun setTextSize(textSize: Float): SenderSpinnerAdapter<*> {
mTextSize = textSize
return this
}
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): SenderSpinnerAdapter<*> {
mBackgroundSelector = backgroundSelector
return this
}
fun setFilterColor(filterColor: String): SenderSpinnerAdapter<*> {
mFilterColor = filterColor
return this
}
fun setIsFilterKey(isFilterKey: Boolean): SenderSpinnerAdapter<*> {
mIsFilterKey = isFilterKey
return this
}
@Suppress("DEPRECATION")
@SuppressLint("ObsoleteSdkInt")
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
val titleView: TextView = convertView.findViewById(R.id.tv_title)
init {
if (textColor > 0) titleView.setTextColor(textColor)
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val config = convertView.resources.configuration
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
titleView.textDirection = View.TEXT_DIRECTION_RTL
}
}
}
}
fun getItemSource(position: Int): T {
return mDataSource[mIndexs[position]]
}
}
package com.idormy.sms.forwarder.adapter.spinner
import android.annotation.SuppressLint
import android.os.Build
import android.text.Html
import android.text.TextUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.utils.STATUS_OFF
import com.xuexiang.xui.utils.CollectionUtils
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
@Suppress("unused", "NAME_SHADOWING", "DEPRECATION")
class SenderSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
/**
* 选项的文字颜色
*/
private var mTextColor = 0
/**
* 选项的文字大小
*/
private var mTextSize = 0f
/**
* 背景颜色
*/
private var mBackgroundSelector = 0
/**
* 过滤关键词的选中颜色
*/
private var mFilterColor = "#F15C58"
private var mIsFilterKey = false
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: List<T>?) : super(data)
/**
* 构造方法
*
* @param data 选项数据
*/
constructor(data: Array<T>?) : super(data)
override fun getEditSpinnerFilter(): EditSpinnerFilter {
return this
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
var convertView = convertView
val holder: ViewHolder
if (convertView == null) {
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
convertView.tag = holder
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderAdapterItem
holder.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable(
ResUtils.getDrawable(
when (item.status) {
STATUS_OFF -> R.drawable.icon_off
else -> R.drawable.icon_on
}
)
)
//holder.titleView.text = Html.fromHtml(item.toString())
holder.titleView.text = Html.fromHtml(getItem(position))
return convertView
}
override fun onFilter(keyword: String): Boolean {
mDisplayData.clear()
Log.d("SenderSpinnerAdapter", "keyword = $keyword")
Log.d("SenderSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
if (TextUtils.isEmpty(keyword)) {
initDisplayData(mDataSource)
for (i in mIndexs.indices) {
mIndexs[i] = i
}
} else {
try {
for (i in mDataSource.indices) {
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
mIndexs[mDisplayData.size] = i
if (mIsFilterKey) {
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
} else {
mDisplayData.add(getDataSourceString(i))
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.d("SenderSpinnerAdapter", "mDisplayData = $mDisplayData")
notifyDataSetChanged()
return mDisplayData.size > 0
}
fun setTextColor(@ColorInt textColor: Int): SenderSpinnerAdapter<*> {
mTextColor = textColor
return this
}
fun setTextSize(textSize: Float): SenderSpinnerAdapter<*> {
mTextSize = textSize
return this
}
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): SenderSpinnerAdapter<*> {
mBackgroundSelector = backgroundSelector
return this
}
fun setFilterColor(filterColor: String): SenderSpinnerAdapter<*> {
mFilterColor = filterColor
return this
}
fun setIsFilterKey(isFilterKey: Boolean): SenderSpinnerAdapter<*> {
mIsFilterKey = isFilterKey
return this
}
@SuppressLint("ObsoleteSdkInt")
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
val titleView: TextView = convertView.findViewById(R.id.tv_title)
init {
if (textColor > 0) titleView.setTextColor(textColor)
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val config = convertView.resources.configuration
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
titleView.textDirection = View.TEXT_DIRECTION_RTL
}
}
}
}
fun getItemSource(position: Int): T {
return mDataSource[mIndexs[position]]
}
}

@ -11,6 +11,7 @@ import com.idormy.sms.forwarder.database.repository.*
import com.idormy.sms.forwarder.service.ForegroundService
import kotlinx.coroutines.launch
@Suppress("unused")
object Core : Configuration.Provider {
lateinit var app: Application
val frpc: FrpcRepository by lazy { (app as App).frpcRepository }

@ -3,7 +3,7 @@ package com.idormy.sms.forwarder.database.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.idormy.sms.forwarder.database.entity.Rule
import com.idormy.sms.forwarder.database.entity.RuleAndSender
//import com.idormy.sms.forwarder.database.entity.RuleAndSender
import io.reactivex.Completable
import io.reactivex.Single
@ -48,9 +48,9 @@ interface RuleDao {
@Query("SELECT * FROM Rule where type=:type ORDER BY id DESC")
fun pagingSource(type: String): PagingSource<Int, Rule>
@Transaction
@Query("SELECT * FROM Rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)")
suspend fun getRuleAndSender(type: String, status: Int, simSlot: String): List<RuleAndSender>
//@Transaction
//@Query("SELECT * FROM Rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)")
//suspend fun getRuleAndSender(type: String, status: Int, simSlot: String): List<RuleAndSender>
@Transaction
@Query("SELECT * FROM Rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)")

@ -49,16 +49,4 @@ data class Logs(
@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(),
) : 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
}
}
) : Parcelable

@ -20,29 +20,6 @@ data class Sender(
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
@ColumnInfo(name = "time") var time: Date = Date(),
) : Parcelable {
companion object {
fun getImageId(type: Int): Int = when (type) {
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
TYPE_SOCKET -> R.drawable.icon_socket
else -> R.drawable.icon_sms
}
}
val imageId: Int
get() = when (type) {

@ -1,36 +1,36 @@
package com.idormy.sms.forwarder.database.repository
import androidx.annotation.WorkerThread
import com.idormy.sms.forwarder.database.dao.FrpcDao
import com.idormy.sms.forwarder.database.entity.Frpc
class FrpcRepository(
private val frpcDao: FrpcDao,
) {
var listener: Listener? = null
@WorkerThread
fun insert(frpc: Frpc) {
frpcDao.insert(frpc)
}
@WorkerThread
fun delete(uid: String) {
frpcDao.delete(uid)
}
@WorkerThread
fun get(uid: String) = frpcDao.get(uid)
@WorkerThread
fun update(frpc: Frpc) = frpcDao.update(frpc)
//TODO:允许主线程访问,后面再优化
val all: List<Frpc> = frpcDao.getAll()
fun deleteAll() {
frpcDao.deleteAll()
}
package com.idormy.sms.forwarder.database.repository
import androidx.annotation.WorkerThread
import com.idormy.sms.forwarder.database.dao.FrpcDao
import com.idormy.sms.forwarder.database.entity.Frpc
class FrpcRepository(
private val frpcDao: FrpcDao,
) {
//var listener: Listener? = null
@WorkerThread
fun insert(frpc: Frpc) {
frpcDao.insert(frpc)
}
@WorkerThread
fun delete(uid: String) {
frpcDao.delete(uid)
}
@WorkerThread
fun get(uid: String) = frpcDao.get(uid)
@WorkerThread
fun update(frpc: Frpc) = frpcDao.update(frpc)
//TODO:允许主线程访问,后面再优化
val all: List<Frpc> = frpcDao.getAll()
fun deleteAll() {
frpcDao.deleteAll()
}
}

@ -11,10 +11,10 @@ class LogsRepository(private val logsDao: LogsDao) {
logsDao.delete(id)
}
@WorkerThread
fun deleteTimeAgo(time: Long) {
logsDao.deleteTimeAgo(time)
}
//@WorkerThread
//fun deleteTimeAgo(time: Long) {
// logsDao.deleteTimeAgo(time)
//}
@WorkerThread
suspend fun insert(logs: Logs): Long = logsDao.insert(logs)

@ -8,7 +8,7 @@ class RuleRepository(
private val ruleDao: RuleDao,
) {
var listener: Listener? = null
private var listener: Listener? = null
@WorkerThread
fun insert(rule: Rule) {
@ -27,7 +27,7 @@ class RuleRepository(
@WorkerThread
fun getOne(id: Long) = ruleDao.getOne(id)
suspend fun getRuleAndSender(type: String, status: Int, simSlot: String) = ruleDao.getRuleAndSender(type, status, simSlot)
//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)

@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow
class SenderRepository(private val senderDao: SenderDao) {
var listener: Listener? = null
private var listener: Listener? = null
@WorkerThread
fun insert(sender: Sender) = senderDao.insert(sender)

@ -38,7 +38,7 @@ import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.data.ConvertTools
@Suppress("PrivatePropertyName", "PropertyName")
@Suppress("PropertyName")
@Page(name = "主动控制·客户端")
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {

@ -27,7 +27,7 @@ import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
@Suppress("PrivatePropertyName", "DEPRECATION")
@Suppress("DEPRECATION")
@Page(name = "Frp内网穿透·编辑配置")
class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() {

@ -38,7 +38,6 @@ import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@Suppress("DEPRECATION")
@Page(name = "Frp内网穿透")
class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.OnItemClickListener {

@ -1,109 +1,108 @@
package com.idormy.sms.forwarder.fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentLogcatBinding
import com.idormy.sms.forwarder.utils.XToastUtils
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xutil.system.ClipboardUtils
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.BufferedReader
import java.io.InputStreamReader
@Suppress("PrivatePropertyName")
@Page(name = "Logcat")
class LogcatFragment : BaseFragment<FragmentLogcatBinding?>() {
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentLogcatBinding {
return FragmentLogcatBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar {
val titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_logcat)
titleBar.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_copy) {
@SingleClick
override fun performAction(view: View) {
ClipboardUtils.copyText(binding!!.tvLogcat.text.toString())
XToastUtils.success(R.string.copySuccess)
}
})
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_delete) {
@SingleClick
override fun performAction(view: View) {
readLog(true)
binding!!.tvLogcat.text = ""
}
})
return titleBar
}
override fun initViews() {
}
override fun initListeners() {
readLog(false)
}
private fun readLog(flush: Boolean) {
val lst: HashSet<String> = LinkedHashSet()
lst.add("logcat")
lst.add("-d")
lst.add("-v")
lst.add("time")
lst.add("-s")
lst.add("GoLog,com.idormy.sms.forwarder.ForegroundService,com.idormy.sms.forwarder.server.ServerService")
Observable.create { emitter: ObservableEmitter<String?> ->
if (flush) {
val lst2: HashSet<String> = LinkedHashSet()
lst2.add("logcat")
lst2.add("-c")
val process = Runtime.getRuntime().exec(lst2.toTypedArray())
process.waitFor()
}
val process = Runtime.getRuntime().exec(lst.toTypedArray())
val `in` = InputStreamReader(process.inputStream)
val bufferedReader = BufferedReader(`in`)
var line: String?
while (bufferedReader.readLine().also { line = it } != null) {
emitter.onNext(line!!)
}
`in`.close()
bufferedReader.close()
emitter.onComplete()
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<String?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(s: String) {
binding!!.tvLogcat.append(s)
binding!!.tvLogcat.append("\r\n")
binding!!.svLogcat.fullScroll(View.FOCUS_DOWN)
}
override fun onError(e: Throwable) {
e.printStackTrace()
}
override fun onComplete() {}
})
}
package com.idormy.sms.forwarder.fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentLogcatBinding
import com.idormy.sms.forwarder.utils.XToastUtils
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xutil.system.ClipboardUtils
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.BufferedReader
import java.io.InputStreamReader
@Page(name = "Logcat")
class LogcatFragment : BaseFragment<FragmentLogcatBinding?>() {
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentLogcatBinding {
return FragmentLogcatBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar {
val titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_logcat)
titleBar.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_copy) {
@SingleClick
override fun performAction(view: View) {
ClipboardUtils.copyText(binding!!.tvLogcat.text.toString())
XToastUtils.success(R.string.copySuccess)
}
})
titleBar.addAction(object : TitleBar.ImageAction(R.drawable.ic_delete) {
@SingleClick
override fun performAction(view: View) {
readLog(true)
binding!!.tvLogcat.text = ""
}
})
return titleBar
}
override fun initViews() {
}
override fun initListeners() {
readLog(false)
}
private fun readLog(flush: Boolean) {
val lst: HashSet<String> = LinkedHashSet()
lst.add("logcat")
lst.add("-d")
lst.add("-v")
lst.add("time")
lst.add("-s")
lst.add("GoLog,com.idormy.sms.forwarder.ForegroundService,com.idormy.sms.forwarder.server.ServerService")
Observable.create { emitter: ObservableEmitter<String?> ->
if (flush) {
val lst2: HashSet<String> = LinkedHashSet()
lst2.add("logcat")
lst2.add("-c")
val process = Runtime.getRuntime().exec(lst2.toTypedArray())
process.waitFor()
}
val process = Runtime.getRuntime().exec(lst.toTypedArray())
val `in` = InputStreamReader(process.inputStream)
val bufferedReader = BufferedReader(`in`)
var line: String?
while (bufferedReader.readLine().also { line = it } != null) {
emitter.onNext(line!!)
}
`in`.close()
bufferedReader.close()
emitter.onComplete()
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<String?> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(s: String) {
binding!!.tvLogcat.append(s)
binding!!.tvLogcat.append("\r\n")
binding!!.svLogcat.fullScroll(View.FOCUS_DOWN)
}
override fun onError(e: Throwable) {
e.printStackTrace()
}
override fun onComplete() {}
})
}
}

@ -35,8 +35,6 @@ import java.io.File
import java.net.InetAddress
import java.security.KeyPairGenerator
@Suppress("PrivatePropertyName")
@Page(name = "主动控制·服务端")
class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListener {

@ -42,7 +42,6 @@ import com.xuexiang.xutil.file.FileUtils
import java.io.File
import java.util.*
@Suppress("PropertyName")
@Page(name = "一键换新机")
class CloneFragment : BaseFragment<FragmentClientCloneBinding?>(), View.OnClickListener {

@ -40,7 +40,6 @@ import com.xuexiang.xutil.data.ConvertTools
import com.xuexiang.xutil.system.ClipboardUtils
import me.samlss.broccoli.Broccoli
@Suppress("PropertyName")
@Page(name = "远程查话簿")
class ContactQueryFragment : BaseFragment<FragmentClientContactQueryBinding?>() {

@ -41,7 +41,6 @@ import com.xuexiang.xutil.data.ConvertTools
import com.xuexiang.xutil.data.DateUtils
import me.samlss.broccoli.Broccoli
@Suppress("PropertyName")
@Page(name = "远程查短信")
class SmsQueryFragment : BaseFragment<FragmentClientSmsQueryBinding?>() {

@ -29,7 +29,7 @@ import java.util.*
//短信广播
@OptIn(DelicateCoroutinesApi::class)
@Suppress("PrivatePropertyName", "DEPRECATION", "DeferredResultUnused", "SENSELESS_COMPARISON")
@Suppress("PrivatePropertyName", "DeferredResultUnused", "SENSELESS_COMPARISON")
class SmsReceiver : BroadcastReceiver() {
private var TAG = "SmsReceiver"

@ -22,7 +22,6 @@ import java.io.InputStream
import java.lang.reflect.Type
import java.nio.charset.Charset
@Suppress("PrivatePropertyName")
@Converter
class AppMessageConverter : MessageConverter {

@ -10,7 +10,7 @@ import com.yanzhenjie.andserver.annotation.*
import java.util.*
@SuppressLint("SimpleDateFormat")
@Suppress("PrivatePropertyName", "DEPRECATION")
@Suppress("PrivatePropertyName")
@RestController
@RequestMapping(path = ["/location"])
class LocationController {

@ -31,7 +31,7 @@ import kotlinx.coroutines.async
import java.util.*
@OptIn(DelicateCoroutinesApi::class)
@Suppress("DEPRECATION", "DeferredResultUnused")
@Suppress("DeferredResultUnused")
class BatteryService : Service() {
override fun onBind(intent: Intent): IBinder? {

@ -22,7 +22,7 @@ import com.xuexiang.xutil.app.ServiceUtils
import com.xuexiang.xutil.net.NetworkUtils
import java.util.*
@Suppress("DEPRECATION", "DeferredResultUnused")
@Suppress("DEPRECATION")
class NetworkStateService : Service() {
override fun onBind(intent: Intent): IBinder? {

@ -1,53 +0,0 @@
package com.idormy.sms.forwarder.utils
import android.annotation.SuppressLint
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.*
import javax.net.ssl.*
@Suppress("unused")
@SuppressLint("ALL")
object CertUtils {
//获取这个SSLSocketFactory
val sSLSocketFactory: SSLSocketFactory
get() = try {
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustManager, SecureRandom())
sslContext.socketFactory
} catch (e: Exception) {
throw RuntimeException(e)
}
//获取TrustManager
private val trustManager: Array<TrustManager>
get() = arrayOf(
object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
)
//获取HostnameVerifier
val hostnameVerifier: HostnameVerifier
get() = HostnameVerifier { _: String?, _: SSLSession? -> true }
val x509TrustManager: X509TrustManager?
get() {
var trustManager: X509TrustManager? = null
try {
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) { "Unexpected default trust managers:" + Arrays.toString(trustManagers) }
trustManager = trustManagers[0] as X509TrustManager
} catch (e: Exception) {
e.printStackTrace()
}
return trustManager
}
}

@ -1,74 +1,73 @@
package com.idormy.sms.forwarder.utils
import com.idormy.sms.forwarder.entity.CallInfo
import com.idormy.sms.forwarder.entity.ContactInfo
import com.idormy.sms.forwarder.entity.SmsInfo
import com.xuexiang.xaop.annotation.MemoryCache
object DataProvider {
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptySmsInfo: List<SmsInfo>
get() {
val list: MutableList<SmsInfo> = ArrayList()
for (i in 0..5) {
list.add(SmsInfo())
}
return list
}
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptyCallInfo: List<CallInfo>
get() {
val list: MutableList<CallInfo> = ArrayList()
for (i in 0..5) {
list.add(CallInfo())
}
return list
}
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptyContactInfo: List<ContactInfo>
get() {
val list: MutableList<ContactInfo> = ArrayList()
for (i in 0..5) {
list.add(ContactInfo())
}
return list
}
//获取时间段
@JvmStatic
@get:MemoryCache
val timePeriodOption: List<String>
get() {
return getTimePeriod(24, 10) //修改时请注意会不会造成旧版下标越界
}
/**
* 获取时间段
*
* @param interval 时间间隔分钟
* @return
*/
@Suppress("UNCHECKED_CAST")
fun getTimePeriod(totalHour: Int, interval: Int): List<String> {
val list: MutableList<String> = ArrayList()
var point: Int
var hour: Int
var min: Int
for (i in 0..totalHour * 60 / interval) {
point = i * interval
hour = point / 60
min = point - hour * 60
list.add((if (hour <= 9) "0$hour" else "" + hour) + ":" + if (min <= 9) "0$min" else "" + min)
}
return list
}
package com.idormy.sms.forwarder.utils
import com.idormy.sms.forwarder.entity.CallInfo
import com.idormy.sms.forwarder.entity.ContactInfo
import com.idormy.sms.forwarder.entity.SmsInfo
import com.xuexiang.xaop.annotation.MemoryCache
object DataProvider {
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptySmsInfo: List<SmsInfo>
get() {
val list: MutableList<SmsInfo> = ArrayList()
for (i in 0..5) {
list.add(SmsInfo())
}
return list
}
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptyCallInfo: List<CallInfo>
get() {
val list: MutableList<CallInfo> = ArrayList()
for (i in 0..5) {
list.add(CallInfo())
}
return list
}
//用于占位的空信息
@JvmStatic
@get:MemoryCache
val emptyContactInfo: List<ContactInfo>
get() {
val list: MutableList<ContactInfo> = ArrayList()
for (i in 0..5) {
list.add(ContactInfo())
}
return list
}
//获取时间段
@JvmStatic
@get:MemoryCache
val timePeriodOption: List<String>
get() {
return getTimePeriod(24, 10) //修改时请注意会不会造成旧版下标越界
}
/**
* 获取时间段
*
* @param interval 时间间隔分钟
* @return
*/
private fun getTimePeriod(totalHour: Int, interval: Int): List<String> {
val list: MutableList<String> = ArrayList()
var point: Int
var hour: Int
var min: Int
for (i in 0..totalHour * 60 / interval) {
point = i * interval
hour = point / 60
min = point - hour * 60
list.add((if (hour <= 9) "0$hour" else "" + hour) + ":" + if (min <= 9) "0$min" else "" + min)
}
return list
}
}

@ -13,7 +13,7 @@ import kotlin.reflect.KProperty
* @author pppscn
* @since 2022年5月9日
*/
@Suppress("PropertyName", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
class HistoryUtils<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
companion object {

@ -1,56 +0,0 @@
/*
* Copyright 2018 Zhenjie Yan.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.idormy.sms.forwarder.utils
import android.util.Log
/**
* Created by Zhenjie Yan on 2018/9/12.
*/
@Suppress("unused")
object Logger {
private const val TAG = "AndServer"
private const val DEBUG = true
fun i(obj: Any?) {
if (DEBUG) {
Log.i(TAG, obj?.toString() ?: "null")
}
}
fun d(obj: Any?) {
if (DEBUG) {
Log.d(TAG, obj?.toString() ?: "null")
}
}
fun v(obj: Any?) {
if (DEBUG) {
Log.v(TAG, obj?.toString() ?: "null")
}
}
fun w(obj: Any?) {
if (DEBUG) {
Log.w(TAG, obj?.toString() ?: "null")
}
}
fun e(obj: Any?) {
if (DEBUG) {
Log.e(TAG, obj?.toString() ?: "null")
}
}
}

@ -1,74 +0,0 @@
/*
* Copyright © 2018 Zhenjie Yan.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.idormy.sms.forwarder.utils
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.util.*
import java.util.regex.Pattern
/**
* Created by Zhenjie Yan on 2018/6/9.
*/
@Suppress("unused")
object NetUtils {
/**
* Ipv4 address check.
*/
private val IPV4_PATTERN = Pattern.compile(
"^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" +
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
)
/**
* Check if valid IPV4 address.
*
* @param input the address string to check for validity.
* @return True if the input parameter is a valid IPv4 address.
*/
private fun isIPv4Address(input: String?): Boolean {
return IPV4_PATTERN.matcher(input.toString()).matches()
}
/**
* Get local Ip address.
*/
val localIPAddress: InetAddress?
get() {
var enumeration: Enumeration<NetworkInterface>? = null
try {
enumeration = NetworkInterface.getNetworkInterfaces()
} catch (e: SocketException) {
e.printStackTrace()
}
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
val nif = enumeration.nextElement()
val inetAddresses = nif.inetAddresses
if (inetAddresses != null) {
while (inetAddresses.hasMoreElements()) {
val inetAddress = inetAddresses.nextElement()
if (!inetAddress.isLoopbackAddress && isIPv4Address(inetAddress.hostAddress)) {
return inetAddress
}
}
}
}
}
return null
}
}

@ -1,67 +0,0 @@
@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()
}
}
}

@ -7,7 +7,7 @@ class SettingUtils private constructor() {
companion object {
//是否是第一次启动
var isFirstOpen: Boolean by SharedPreference(IS_FIRST_OPEN_KEY, true)
//var isFirstOpen: Boolean by SharedPreference(IS_FIRST_OPEN_KEY, true)
//是否同意隐私政策
var isAgreePrivacy: Boolean by SharedPreference(IS_AGREE_PRIVACY_KEY, false)

@ -74,9 +74,6 @@ class UMengInit private constructor() {
/**
* 获取渠道信息
*
* @param context
* @return
*/
private fun getChannel(): String { //context: Context?
//return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID)

@ -15,7 +15,6 @@ import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
import java.util.regex.Pattern
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class BarkUtils {
companion object {

@ -20,7 +20,6 @@ import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
//钉钉群自定义机器人
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class DingtalkGroupRobotUtils private constructor() {
companion object {

@ -26,7 +26,6 @@ import java.net.PasswordAuthentication
import java.net.Proxy
//钉钉企业内机器人
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class DingtalkInnerRobotUtils private constructor() {
companion object {

@ -11,11 +11,10 @@ import com.idormy.sms.forwarder.utils.mail.Mail
import com.idormy.sms.forwarder.utils.mail.MailSender
import com.xuexiang.xui.utils.ResUtils
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class EmailUtils {
companion object {
private val TAG: String = EmailUtils::class.java.simpleName
//private val TAG: String = EmailUtils::class.java.simpleName
fun sendMsg(
setting: EmailSetting,

@ -18,7 +18,6 @@ import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xui.utils.ResUtils.getString
//飞书企业应用
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class FeishuAppUtils private constructor() {
companion object {

@ -19,7 +19,6 @@ import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class FeishuUtils private constructor() {
companion object {

@ -13,7 +13,6 @@ import com.xuexiang.xhttp2.cache.model.CacheMode
import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class GotifyUtils {
companion object {

@ -16,8 +16,6 @@ import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xui.utils.ResUtils
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class PushplusUtils private constructor() {
companion object {

@ -14,7 +14,6 @@ import com.xuexiang.xhttp2.cache.model.CacheMode
import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class ServerchanUtils {
companion object {

@ -16,7 +16,6 @@ import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.net.NetworkUtils
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class SmsUtils {
companion object {

@ -23,7 +23,6 @@ import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class SocketUtils {
companion object {

@ -19,8 +19,6 @@ import okhttp3.Response
import okhttp3.Route
import java.net.*
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class TelegramUtils private constructor() {
companion object {

@ -15,8 +15,6 @@ import java.net.URLEncoder
import java.text.SimpleDateFormat
import java.util.*
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class UrlSchemeUtils private constructor() {
companion object {

@ -22,7 +22,6 @@ import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER", "unused")
class WebhookUtils {
companion object {
@ -114,7 +113,7 @@ class WebhookUtils {
}
Log.d(TAG, "method = GET, Url = $requestUrl")
XHttp.get(requestUrl).keepJson(true)
} else if (webParams != null && webParams.isNotEmpty() && webParams.startsWith("{")) {
} else if (!webParams.isNullOrEmpty() && webParams.startsWith("{")) {
val bodyMsg = webParams.replace("[from]", from)
.replace("[content]", escapeJson(content))
.replace("[msg]", escapeJson(content))
@ -133,7 +132,7 @@ class WebhookUtils {
else -> XHttp.post(requestUrl).keepJson(true).upJson(bodyMsg)
}
} else {
if (webParams == null || webParams.isEmpty()) {
if (webParams.isNullOrEmpty()) {
webParams = "from=[from]&content=[content]&timestamp=[timestamp]"
if (!TextUtils.isEmpty(sign)) webParams += "&sign=[sign]"
}

@ -26,7 +26,6 @@ import java.net.InetSocketAddress
import java.net.PasswordAuthentication
import java.net.Proxy
@Suppress("PrivatePropertyName", "UNUSED_PARAMETER")
class WeworkAgentUtils private constructor() {
companion object {

@ -1,42 +0,0 @@
package com.idormy.sms.forwarder.utils.service
import android.content.Context
import com.xuexiang.xrouter.annotation.Router
import com.xuexiang.xrouter.facade.service.SerializationService
import com.xuexiang.xutil.net.JsonUtil
import java.lang.reflect.Type
/**
* @author XUE
* @since 2019/3/27 16:39
*/
@Router(path = "/service/json")
class JsonSerializationService : SerializationService {
/**
* 对象序列化为json
*
* @param instance obj
* @return json string
*/
override fun object2Json(instance: Any): String {
return JsonUtil.toJson(instance)
}
/**
* json反序列化为对象
*
* @param input json string
* @param clazz object type
* @return instance of object
*/
override fun <T> parseObject(input: String, clazz: Type): T {
return JsonUtil.fromJson(input, clazz)
}
/**
* 进程初始化的方法
*
* @param context 上下文
*/
override fun init(context: Context) {}
}

@ -1,240 +1,240 @@
package com.idormy.sms.forwarder.utils.tinker
import android.annotation.SuppressLint
import android.content.Context
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "UNCHECKED_CAST", "SENSELESS_COMPARISON", "unused")
object ShareReflectUtil {
/**
* Locates a given field anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the field into.
* @param name field name
* @return a field object
* @throws NoSuchFieldException if the field cannot be located
*/
@Throws(NoSuchFieldException::class)
fun findField(instance: Any, name: String): Field {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val field = clazz.getDeclaredField(name)
if (!field.isAccessible) {
field.isAccessible = true
}
return field
} catch (e: NoSuchFieldException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchFieldException("Field " + name + " not found in " + instance.javaClass)
}
@Throws(NoSuchFieldException::class)
fun findField(originClazz: Class<*>, name: String): Field {
var clazz: Class<*>? = originClazz
while (clazz != null) {
try {
val field = clazz.getDeclaredField(name)
if (!field.isAccessible) {
field.isAccessible = true
}
return field
} catch (e: NoSuchFieldException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchFieldException("Field $name not found in $originClazz")
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findMethod(instance: Any, name: String, vararg parameterTypes: Class<*>?): Method {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val method = clazz.getDeclaredMethod(name, *parameterTypes)
if (!method.isAccessible) {
method.isAccessible = true
}
return method
} catch (e: NoSuchMethodException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchMethodException(
"Method "
+ name
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + instance.javaClass
)
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param clazz a class to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findMethod(clazz: Class<*>?, name: String, vararg parameterTypes: Class<*>?): Method {
var tClazz = clazz
while (tClazz != null) {
try {
val method = tClazz.getDeclaredMethod(name, *parameterTypes)
if (!method.isAccessible) {
method.isAccessible = true
}
return method
} catch (e: NoSuchMethodException) {
// ignore and search next
}
tClazz = tClazz.superclass
}
throw NoSuchMethodException(
"Method "
+ name
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + tClazz
)
}
/**
* Locates a given constructor anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the constructor into.
* @param parameterTypes constructor parameter types
* @return a constructor object
* @throws NoSuchMethodException if the constructor cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findConstructor(instance: Any, vararg parameterTypes: Class<*>?): Constructor<*> {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val constructor = clazz.getDeclaredConstructor(*parameterTypes)
if (!constructor.isAccessible) {
constructor.isAccessible = true
}
return constructor
} catch (e: NoSuchMethodException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchMethodException(
"Constructor"
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + instance.javaClass
)
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
* @param extraElements elements to append at the end of the array.
*/
@Throws(NoSuchFieldException::class, IllegalArgumentException::class, IllegalAccessException::class)
fun expandFieldArray(instance: Any, fieldName: String, extraElements: Array<Any?>) {
val jlrField = findField(instance, fieldName)
val original = jlrField[instance] as Array<Any>
val combined = java.lang.reflect.Array.newInstance(original.javaClass.componentType, original.size + extraElements.size) as Array<Any>
// NOTE: changed to copy extraElements first, for patch load first
System.arraycopy(extraElements, 0, combined, 0, extraElements.size)
System.arraycopy(original, 0, combined, extraElements.size, original.size)
jlrField[instance] = combined
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
*/
@Throws(NoSuchFieldException::class, IllegalArgumentException::class, IllegalAccessException::class)
fun reduceFieldArray(instance: Any, fieldName: String, reduceSize: Int) {
if (reduceSize <= 0) {
return
}
val jlrField = findField(instance, fieldName)
val original = jlrField[instance] as Array<Any>
val finalLength = original.size - reduceSize
if (finalLength <= 0) {
return
}
val combined = java.lang.reflect.Array.newInstance(original.javaClass.componentType, finalLength) as Array<Any>
System.arraycopy(original, reduceSize, combined, 0, finalLength)
jlrField[instance] = combined
}
@SuppressLint("PrivateApi")
fun getActivityThread(
context: Context?,
activityThread: Class<*>?,
): Any? {
var tActivityThread = activityThread
return try {
if (tActivityThread == null) {
tActivityThread = Class.forName("android.app.ActivityThread")
}
val m = tActivityThread!!.getMethod("currentActivityThread")
m.isAccessible = true
var currentActivityThread = m.invoke(null)
if (currentActivityThread == null && context != null) {
// In older versions of Android (prior to frameworks/base 66a017b63461a22842)
// the currentActivityThread was built on thread locals, so we'll need to try
// even harder
val mLoadedApk = context.javaClass.getField("mLoadedApk")
mLoadedApk.isAccessible = true
val apk = mLoadedApk[context]
val mActivityThreadField = apk.javaClass.getDeclaredField("mActivityThread")
mActivityThreadField.isAccessible = true
currentActivityThread = mActivityThreadField[apk]
}
currentActivityThread
} catch (ignore: Throwable) {
null
}
}
/**
* Handy method for fetching hidden integer constant value in system classes.
*
* @param clazz
* @param fieldName
* @return
*/
fun getValueOfStaticIntField(clazz: Class<*>, fieldName: String, defVal: Int): Int {
return try {
val field = findField(clazz, fieldName)
field.getInt(null)
} catch (thr: Throwable) {
defVal
}
}
package com.idormy.sms.forwarder.utils.tinker
import android.annotation.SuppressLint
import android.content.Context
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "UNCHECKED_CAST", "unused")
object ShareReflectUtil {
/**
* Locates a given field anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the field into.
* @param name field name
* @return a field object
* @throws NoSuchFieldException if the field cannot be located
*/
@Throws(NoSuchFieldException::class)
fun findField(instance: Any, name: String): Field {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val field = clazz.getDeclaredField(name)
if (!field.isAccessible) {
field.isAccessible = true
}
return field
} catch (e: NoSuchFieldException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchFieldException("Field " + name + " not found in " + instance.javaClass)
}
@Throws(NoSuchFieldException::class)
fun findField(originClazz: Class<*>, name: String): Field {
var clazz: Class<*>? = originClazz
while (clazz != null) {
try {
val field = clazz.getDeclaredField(name)
if (!field.isAccessible) {
field.isAccessible = true
}
return field
} catch (e: NoSuchFieldException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchFieldException("Field $name not found in $originClazz")
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findMethod(instance: Any, name: String, vararg parameterTypes: Class<*>?): Method {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val method = clazz.getDeclaredMethod(name, *parameterTypes)
if (!method.isAccessible) {
method.isAccessible = true
}
return method
} catch (e: NoSuchMethodException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchMethodException(
"Method "
+ name
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + instance.javaClass
)
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param clazz a class to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findMethod(clazz: Class<*>?, name: String, vararg parameterTypes: Class<*>?): Method {
var tClazz = clazz
while (tClazz != null) {
try {
val method = tClazz.getDeclaredMethod(name, *parameterTypes)
if (!method.isAccessible) {
method.isAccessible = true
}
return method
} catch (e: NoSuchMethodException) {
// ignore and search next
}
tClazz = tClazz.superclass
}
throw NoSuchMethodException(
"Method "
+ name
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + tClazz
)
}
/**
* Locates a given constructor anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the constructor into.
* @param parameterTypes constructor parameter types
* @return a constructor object
* @throws NoSuchMethodException if the constructor cannot be located
*/
@Throws(NoSuchMethodException::class)
fun findConstructor(instance: Any, vararg parameterTypes: Class<*>?): Constructor<*> {
var clazz: Class<*>? = instance.javaClass
while (clazz != null) {
try {
val constructor = clazz.getDeclaredConstructor(*parameterTypes)
if (!constructor.isAccessible) {
constructor.isAccessible = true
}
return constructor
} catch (e: NoSuchMethodException) {
// ignore and search next
}
clazz = clazz.superclass
}
throw NoSuchMethodException(
"Constructor"
+ " with parameters "
+ listOf(*parameterTypes)
+ " not found in " + instance.javaClass
)
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
* @param extraElements elements to append at the end of the array.
*/
@Throws(NoSuchFieldException::class, IllegalArgumentException::class, IllegalAccessException::class)
fun expandFieldArray(instance: Any, fieldName: String, extraElements: Array<Any?>) {
val jlrField = findField(instance, fieldName)
val original = jlrField[instance] as Array<Any>
val combined = java.lang.reflect.Array.newInstance(original.javaClass.componentType, original.size + extraElements.size) as Array<Any>
// NOTE: changed to copy extraElements first, for patch load first
System.arraycopy(extraElements, 0, combined, 0, extraElements.size)
System.arraycopy(original, 0, combined, extraElements.size, original.size)
jlrField[instance] = combined
}
/**
* Replace the value of a field containing a non null array, by a new array containing the
* elements of the original array plus the elements of extraElements.
*
* @param instance the instance whose field is to be modified.
* @param fieldName the field to modify.
*/
@Throws(NoSuchFieldException::class, IllegalArgumentException::class, IllegalAccessException::class)
fun reduceFieldArray(instance: Any, fieldName: String, reduceSize: Int) {
if (reduceSize <= 0) {
return
}
val jlrField = findField(instance, fieldName)
val original = jlrField[instance] as Array<Any>
val finalLength = original.size - reduceSize
if (finalLength <= 0) {
return
}
val combined = java.lang.reflect.Array.newInstance(original.javaClass.componentType, finalLength) as Array<Any>
System.arraycopy(original, reduceSize, combined, 0, finalLength)
jlrField[instance] = combined
}
@SuppressLint("PrivateApi")
fun getActivityThread(
context: Context?,
activityThread: Class<*>?,
): Any? {
var tActivityThread = activityThread
return try {
if (tActivityThread == null) {
tActivityThread = Class.forName("android.app.ActivityThread")
}
val m = tActivityThread!!.getMethod("currentActivityThread")
m.isAccessible = true
var currentActivityThread = m.invoke(null)
if (currentActivityThread == null && context != null) {
// In older versions of Android (prior to frameworks/base 66a017b63461a22842)
// the currentActivityThread was built on thread locals, so we'll need to try
// even harder
val mLoadedApk = context.javaClass.getField("mLoadedApk")
mLoadedApk.isAccessible = true
val apk = mLoadedApk[context]
val mActivityThreadField = apk.javaClass.getDeclaredField("mActivityThread")
mActivityThreadField.isAccessible = true
currentActivityThread = mActivityThreadField[apk]
}
currentActivityThread
} catch (ignore: Throwable) {
null
}
}
/**
* Handy method for fetching hidden integer constant value in system classes.
*
* @param clazz
* @param fieldName
* @return
*/
fun getValueOfStaticIntField(clazz: Class<*>, fieldName: String, defVal: Int): Int {
return try {
val field = findField(clazz, fieldName)
field.getInt(null)
} catch (thr: Throwable) {
defVal
}
}
}

@ -167,7 +167,7 @@ class GuideTipsDialog(context: Context?, tips: List<TipInfo>) :
).tips, object : NoTipCallBack<List<TipInfo>>() {
@Throws(Throwable::class)
override fun onSuccess(response: List<TipInfo>?) {
if (response != null && response.isNotEmpty()) {
if (!response.isNullOrEmpty()) {
GuideTipsDialog(context, response).show()
}
}

@ -39,7 +39,7 @@ class LoadAppListWorker(
LiveEventBus.get(EVENT_LOAD_APP_LIST, String::class.java).post("finish")
App.LoadingAppList = false
Log.d("LoadAppListWorker", "LoadAppListWorker finish, App.LoadingAppList=${App.LoadingAppList}")
Log.d("LoadAppListWorker", "LoadAppListWorker finish")
return@withContext Result.success()
}

@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

@ -1,13 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#299ee3"
android:pathData="M597.8,511.49 L813.56,295.72c23.83,-23.83 23.83,-62.47 0,-86.31 -23.83,-23.83 -62.47,-23.83 -86.31,0L511.49,425.18 295.72,209.41c-23.83,-23.83 -62.48,-23.83 -86.31,0 -23.83,23.83 -23.83,62.47 0,86.31l215.77,215.77L209.41,727.26c-23.83,23.83 -23.83,62.47 0,86.31 23.83,23.83 62.47,23.83 86.31,0l215.77,-215.77 215.77,215.77c23.83,23.83 62.48,23.83 86.31,0 23.83,-23.83 23.83,-62.47 0,-86.31L597.8,511.49z" />
</vector>

@ -1,18 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#000000"
android:pathData="M869.03,393.85a54.14,54.14 0,0 1,54.14 54.14v467.74a54.14,54.14 0,0 1,-54.14 54.14H154.97a54.14,54.14 0,0 1,-54.14 -54.14V447.98a54.14,54.14 0,0 1,54.14 -54.14h714.07m0,-54.14H154.97a108.27,108.27 0,0 0,-108.27 108.27v467.74a108.27,108.27 0,0 0,108.27 108.27h714.07a108.27,108.27 0,0 0,108.27 -108.27V447.98a108.27,108.27 0,0 0,-108.27 -108.27z" />
<path
android:fillColor="#000000"
android:pathData="M817.87,362.72h-54.14v-56.84a251.74,251.74 0,1 0,-503.47 0v56.84h-54.14v-56.84a305.87,305.87 0,1 1,611.75 0z" />
<path
android:fillColor="#000000"
android:pathData="M438.64,520.53m71.46,0l3.79,0q71.46,0 71.46,71.46l0,3.79q0,71.46 -71.46,71.46l-3.79,0q-71.46,0 -71.46,-71.46l0,-3.79q0,-71.46 71.46,-71.46Z" />
<path
android:fillColor="#000000"
android:pathData="M483.04,567.63m28.15,0l1.35,0q28.15,0 28.15,28.15l0,204.37q0,28.15 -28.15,28.15l-1.35,0q-28.15,0 -28.15,-28.15l0,-204.37q0,-28.15 28.15,-28.15Z" />
</vector>

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#000000"
android:pathData="M640,542.72c76.8,-44.8 128,-128 128,-217.6 0,-140.8 -115.2,-256 -256,-256s-256,108.8 -256,249.6c0,96 51.2,172.8 128,217.6 -166.4,51.2 -281.6,204.8 -288,384 0,25.6 12.8,38.4 32,38.4s32,-12.8 32,-32c6.4,-192 160,-345.6 352,-345.6s345.6,153.6 352,345.6c0,19.2 12.8,32 32,32s32,-12.8 32,-32c-6.4,-179.2 -121.6,-332.8 -288,-384zM320,318.72c0,-108.8 83.2,-192 192,-192s192,83.2 192,192 -83.2,192 -192,192 -192,-83.2 -192,-192z" />
</vector>

@ -1,46 +0,0 @@
/*
* Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.idormy.sms.forwarder
import com.idormy.sms.forwarder.core.http.entity.TipInfo
import com.xuexiang.xhttp2.model.ApiResult
import com.xuexiang.xutil.net.JsonUtil
import org.junit.Assert
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see [Testing documentation](http://d.android.com/tools/testing)
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
Assert.assertEquals(4, (2 + 2).toLong())
val info = TipInfo()
info.title = "微信公众号"
info.content = "获取更多资讯欢迎关注我的微信公众号【我的Android开源之旅】"
val list: MutableList<TipInfo> = ArrayList()
for (i in 0..4) {
list.add(info)
}
val result = ApiResult<List<TipInfo>>()
result.data = list
println(JsonUtil.toJson(result))
}
}
Loading…
Cancel
Save