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

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

@ -249,7 +249,7 @@ dependencies {
//MarkdownViewhttps://github.com/tiagohm/MarkdownView //MarkdownViewhttps://github.com/tiagohm/MarkdownView
implementation 'com.github.tiagohm.MarkdownView:library:0.19.0' 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' def retrofit2_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version" 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, "Code": 0,
"Data": [ "Data": [
{ {
"title": "新用户必读", "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互助交流群里提问请清楚地描述问题并给出对应的配置截图与相关日志方便大家直观的判断问题 " "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互助交流群", "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>" "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": "打赏名单", "title": "打赏名单",
"content": "感谢热心网友们对开源项目的喜爱和支持!<a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427\"><font color=\"#800080\">查看赞助名单!</font></a>" "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.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@Suppress("PrivatePropertyName")
class App : Application(), CactusCallback, Configuration.Provider by Core { class App : Application(), CactusCallback, Configuration.Provider by Core {
val applicationScope = CoroutineScope(SupervisorJob()) val applicationScope = CoroutineScope(SupervisorJob())

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

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

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

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

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

@ -11,6 +11,7 @@ import com.idormy.sms.forwarder.database.repository.*
import com.idormy.sms.forwarder.service.ForegroundService import com.idormy.sms.forwarder.service.ForegroundService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Suppress("unused")
object Core : Configuration.Provider { object Core : Configuration.Provider {
lateinit var app: Application lateinit var app: Application
val frpc: FrpcRepository by lazy { (app as App).frpcRepository } 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.paging.PagingSource
import androidx.room.* import androidx.room.*
import com.idormy.sms.forwarder.database.entity.Rule 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.Completable
import io.reactivex.Single import io.reactivex.Single
@ -48,9 +48,9 @@ interface RuleDao {
@Query("SELECT * FROM Rule where type=:type ORDER BY id DESC") @Query("SELECT * FROM Rule where type=:type ORDER BY id DESC")
fun pagingSource(type: String): PagingSource<Int, Rule> fun pagingSource(type: String): PagingSource<Int, Rule>
@Transaction //@Transaction
@Query("SELECT * FROM Rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)") //@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> //suspend fun getRuleAndSender(type: String, status: Int, simSlot: String): List<RuleAndSender>
@Transaction @Transaction
@Query("SELECT * FROM Rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)") @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_status", defaultValue = "1") var forwardStatus: Int = 1,
@ColumnInfo(name = "forward_response", defaultValue = "") var forwardResponse: String = "", @ColumnInfo(name = "forward_response", defaultValue = "") var forwardResponse: String = "",
@ColumnInfo(name = "time") var time: Date = Date(), @ColumnInfo(name = "time") var time: Date = Date(),
) : Parcelable { ) : 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
}
}

@ -20,29 +20,6 @@ data class Sender(
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1, @ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
@ColumnInfo(name = "time") var time: Date = Date(), @ColumnInfo(name = "time") var time: Date = Date(),
) : Parcelable { ) : 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 val imageId: Int
get() = when (type) { get() = when (type) {

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

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

@ -8,7 +8,7 @@ class RuleRepository(
private val ruleDao: RuleDao, private val ruleDao: RuleDao,
) { ) {
var listener: Listener? = null private var listener: Listener? = null
@WorkerThread @WorkerThread
fun insert(rule: Rule) { fun insert(rule: Rule) {
@ -27,7 +27,7 @@ class RuleRepository(
@WorkerThread @WorkerThread
fun getOne(id: Long) = ruleDao.getOne(id) 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) 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) { class SenderRepository(private val senderDao: SenderDao) {
var listener: Listener? = null private var listener: Listener? = null
@WorkerThread @WorkerThread
fun insert(sender: Sender) = senderDao.insert(sender) 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.XUtil
import com.xuexiang.xutil.data.ConvertTools import com.xuexiang.xutil.data.ConvertTools
@Suppress("PrivatePropertyName", "PropertyName") @Suppress("PropertyName")
@Page(name = "主动控制·客户端") @Page(name = "主动控制·客户端")
class ClientFragment : BaseFragment<FragmentClientBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> { 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.dialog.materialdialog.MaterialDialog
import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
@Suppress("PrivatePropertyName", "DEPRECATION") @Suppress("DEPRECATION")
@Page(name = "Frp内网穿透·编辑配置") @Page(name = "Frp内网穿透·编辑配置")
class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() { class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() {

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

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

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

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

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

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

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

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

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

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

@ -22,7 +22,7 @@ import com.xuexiang.xutil.app.ServiceUtils
import com.xuexiang.xutil.net.NetworkUtils import com.xuexiang.xutil.net.NetworkUtils
import java.util.* import java.util.*
@Suppress("DEPRECATION", "DeferredResultUnused") @Suppress("DEPRECATION")
class NetworkStateService : Service() { class NetworkStateService : Service() {
override fun onBind(intent: Intent): IBinder? { 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 package com.idormy.sms.forwarder.utils
import com.idormy.sms.forwarder.entity.CallInfo import com.idormy.sms.forwarder.entity.CallInfo
import com.idormy.sms.forwarder.entity.ContactInfo import com.idormy.sms.forwarder.entity.ContactInfo
import com.idormy.sms.forwarder.entity.SmsInfo import com.idormy.sms.forwarder.entity.SmsInfo
import com.xuexiang.xaop.annotation.MemoryCache import com.xuexiang.xaop.annotation.MemoryCache
object DataProvider { object DataProvider {
//用于占位的空信息 //用于占位的空信息
@JvmStatic @JvmStatic
@get:MemoryCache @get:MemoryCache
val emptySmsInfo: List<SmsInfo> val emptySmsInfo: List<SmsInfo>
get() { get() {
val list: MutableList<SmsInfo> = ArrayList() val list: MutableList<SmsInfo> = ArrayList()
for (i in 0..5) { for (i in 0..5) {
list.add(SmsInfo()) list.add(SmsInfo())
} }
return list return list
} }
//用于占位的空信息 //用于占位的空信息
@JvmStatic @JvmStatic
@get:MemoryCache @get:MemoryCache
val emptyCallInfo: List<CallInfo> val emptyCallInfo: List<CallInfo>
get() { get() {
val list: MutableList<CallInfo> = ArrayList() val list: MutableList<CallInfo> = ArrayList()
for (i in 0..5) { for (i in 0..5) {
list.add(CallInfo()) list.add(CallInfo())
} }
return list return list
} }
//用于占位的空信息 //用于占位的空信息
@JvmStatic @JvmStatic
@get:MemoryCache @get:MemoryCache
val emptyContactInfo: List<ContactInfo> val emptyContactInfo: List<ContactInfo>
get() { get() {
val list: MutableList<ContactInfo> = ArrayList() val list: MutableList<ContactInfo> = ArrayList()
for (i in 0..5) { for (i in 0..5) {
list.add(ContactInfo()) list.add(ContactInfo())
} }
return list return list
} }
//获取时间段 //获取时间段
@JvmStatic @JvmStatic
@get:MemoryCache @get:MemoryCache
val timePeriodOption: List<String> val timePeriodOption: List<String>
get() { get() {
return getTimePeriod(24, 10) //修改时请注意会不会造成旧版下标越界 return getTimePeriod(24, 10) //修改时请注意会不会造成旧版下标越界
} }
/** /**
* 获取时间段 * 获取时间段
* *
* @param interval 时间间隔分钟 * @param interval 时间间隔分钟
* @return * @return
*/ */
@Suppress("UNCHECKED_CAST") private fun getTimePeriod(totalHour: Int, interval: Int): List<String> {
fun getTimePeriod(totalHour: Int, interval: Int): List<String> { val list: MutableList<String> = ArrayList()
val list: MutableList<String> = ArrayList() var point: Int
var point: Int var hour: Int
var hour: Int var min: Int
var min: Int for (i in 0..totalHour * 60 / interval) {
for (i in 0..totalHour * 60 / interval) { point = i * interval
point = i * interval hour = point / 60
hour = point / 60 min = point - hour * 60
min = point - hour * 60 list.add((if (hour <= 9) "0$hour" else "" + hour) + ":" + if (min <= 9) "0$min" else "" + min)
list.add((if (hour <= 9) "0$hour" else "" + hour) + ":" + if (min <= 9) "0$min" else "" + min) }
} return list
return list }
}
} }

@ -13,7 +13,7 @@ import kotlin.reflect.KProperty
* @author pppscn * @author pppscn
* @since 2022年5月9日 * @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> { class HistoryUtils<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
companion object { 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 { 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) 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? private fun getChannel(): String { //context: Context?
//return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID) //return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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