mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-02 17:07:41 +08:00
整理:code review & 精简无用资源
This commit is contained in:
parent
8953981d4e
commit
f5de522967
@ -249,7 +249,7 @@ dependencies {
|
||||
|
||||
//MarkdownView:https://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 }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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]×tamp=[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…
x
Reference in New Issue
Block a user