整理:Code Review

This commit is contained in:
pppscn 2023-02-14 16:43:24 +08:00
parent 6571775a0f
commit 9c6f404190
18 changed files with 1148 additions and 1136 deletions

View File

@ -291,3 +291,10 @@
-keep interface * implements com.xuexiang.xrouter.facade.template.IProvider -keep interface * implements com.xuexiang.xrouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
-keep class * implements com.xuexiang.xrouter.facade.template.IProvider -keep class * implements com.xuexiang.xrouter.facade.template.IProvider
-dontwarn com.alipay.sdk.**
-dontwarn com.android.org.conscrypt.**
-dontwarn java.awt.image.**
-dontwarn javax.lang.model.**
-dontwarn javax.naming.**
-dontwarn javax.naming.directory.**

View File

@ -8,6 +8,7 @@
tools:ignore="QueryAllPackagesPermission" /> tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@ -55,9 +56,6 @@
<uses-permission <uses-permission
android:name="android.permission.READ_LOGS" android:name="android.permission.READ_LOGS"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application <application
android:name=".App" android:name=".App"

View File

@ -1,269 +1,269 @@
package com.idormy.sms.forwarder.adapter.base.delegate package com.idormy.sms.forwarder.adapter.base.delegate
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.alibaba.android.vlayout.DelegateAdapter import com.alibaba.android.vlayout.DelegateAdapter
/** /**
* 基础DelegateAdapter * 基础DelegateAdapter
* *
* @author xuexiang * @author xuexiang
* @since 2020/3/20 12:17 AM * @since 2020/3/20 12:17 AM
*/ */
@Suppress("unused") @Suppress("unused", "WRONG_TYPE_PARAMETER_NULLABILITY_FOR_JAVA_OVERRIDE")
abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapter.Adapter<V> { abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapter.Adapter<V> {
/** /**
* 数据源 * 数据源
*/ */
private val mData: MutableList<T> = ArrayList() private val mData: MutableList<T> = ArrayList()
/** /**
* @return 当前列表的选中项 * @return 当前列表的选中项
*/ */
/** /**
* 当前点击的条目 * 当前点击的条目
*/ */
private var selectPosition = -1 private var selectPosition = -1
constructor() constructor()
constructor(list: Collection<T>?) { constructor(list: Collection<T>?) {
if (list != null) { if (list != null) {
mData.addAll(list) mData.addAll(list)
} }
} }
constructor(data: Array<T>?) { constructor(data: Array<T>?) {
if (data != null && data.isNotEmpty()) { if (data != null && data.isNotEmpty()) {
mData.addAll(listOf(*data)) mData.addAll(listOf(*data))
} }
} }
/** /**
* 构建自定义的ViewHolder * 构建自定义的ViewHolder
* *
* @param parent * @param parent
* @param viewType * @param viewType
* @return * @return
*/ */
protected abstract fun getViewHolder(parent: ViewGroup, viewType: Int): V protected abstract fun getViewHolder(parent: ViewGroup, viewType: Int): V
/** /**
* 绑定数据 * 绑定数据
* *
* @param holder * @param holder
* @param position 索引 * @param position 索引
* @param item 列表项 * @param item 列表项
*/ */
protected abstract fun bindData(holder: V, position: Int, item: T) protected abstract fun bindData(holder: V, position: Int, item: T)
/** /**
* 加载布局获取控件 * 加载布局获取控件
* *
* @param parent 父布局 * @param parent 父布局
* @param layoutId 布局ID * @param layoutId 布局ID
* @return * @return
*/ */
protected fun inflateView(parent: ViewGroup, @LayoutRes layoutId: Int): View { protected fun inflateView(parent: ViewGroup, @LayoutRes layoutId: Int): View {
return LayoutInflater.from(parent.context).inflate(layoutId, parent, false) return LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): V { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): V {
return getViewHolder(parent, viewType) return getViewHolder(parent, viewType)
} }
override fun onBindViewHolder(holder: V, position: Int) { override fun onBindViewHolder(holder: V, position: Int) {
bindData(holder, position, mData[position]) bindData(holder, position, mData[position])
} }
/** /**
* 获取列表项 * 获取列表项
* *
* @param position * @param position
* @return * @return
*/ */
private fun getItem(position: Int): T? { private fun getItem(position: Int): T? {
return if (checkPosition(position)) mData[position] else null return if (checkPosition(position)) mData[position] else null
} }
private fun checkPosition(position: Int): Boolean { private fun checkPosition(position: Int): Boolean {
return position >= 0 && position <= mData.size - 1 return position >= 0 && position <= mData.size - 1
} }
val isEmpty: Boolean val isEmpty: Boolean
get() = itemCount == 0 get() = itemCount == 0
override fun getItemCount(): Int { override fun getItemCount(): Int {
return mData.size return mData.size
} }
/** /**
* @return 数据源 * @return 数据源
*/ */
val data: List<T> val data: List<T>
get() = mData get() = mData
/** /**
* 给指定位置添加一项 * 给指定位置添加一项
* *
* @param pos * @param pos
* @param item * @param item
* @return * @return
*/ */
fun add(pos: Int, item: T): XDelegateAdapter<*, *> { fun add(pos: Int, item: T): XDelegateAdapter<*, *> {
mData.add(pos, item) mData.add(pos, item)
notifyItemInserted(pos) notifyItemInserted(pos)
return this return this
} }
/** /**
* 在列表末端增加一项 * 在列表末端增加一项
* *
* @param item * @param item
* @return * @return
*/ */
fun add(item: T): XDelegateAdapter<*, *> { fun add(item: T): XDelegateAdapter<*, *> {
mData.add(item) mData.add(item)
notifyItemInserted(mData.size - 1) notifyItemInserted(mData.size - 1)
return this return this
} }
/** /**
* 删除列表中指定索引的数据 * 删除列表中指定索引的数据
* *
* @param pos * @param pos
* @return * @return
*/ */
fun delete(pos: Int): XDelegateAdapter<*, *> { fun delete(pos: Int): XDelegateAdapter<*, *> {
mData.removeAt(pos) mData.removeAt(pos)
notifyItemRemoved(pos) notifyItemRemoved(pos)
return this return this
} }
/** /**
* 刷新列表中指定位置的数据 * 刷新列表中指定位置的数据
* *
* @param pos * @param pos
* @param item * @param item
* @return * @return
*/ */
fun refresh(pos: Int, item: T): XDelegateAdapter<*, *> { fun refresh(pos: Int, item: T): XDelegateAdapter<*, *> {
mData[pos] = item mData[pos] = item
notifyItemChanged(pos) notifyItemChanged(pos)
return this return this
} }
/** /**
* 刷新列表数据 * 刷新列表数据
* *
* @param collection * @param collection
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
open fun refresh(collection: Collection<T>?): XDelegateAdapter<*, *> { open fun refresh(collection: Collection<T>?): XDelegateAdapter<*, *> {
if (collection != null) { if (collection != null) {
mData.clear() mData.clear()
mData.addAll(collection) mData.addAll(collection)
selectPosition = -1 selectPosition = -1
notifyDataSetChanged() notifyDataSetChanged()
} }
return this return this
} }
/** /**
* 刷新列表数据 * 刷新列表数据
* *
* @param array * @param array
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun refresh(array: Array<T>?): XDelegateAdapter<*, *> { fun refresh(array: Array<T>?): XDelegateAdapter<*, *> {
if (array != null && array.isNotEmpty()) { if (array != null && array.isNotEmpty()) {
mData.clear() mData.clear()
mData.addAll(listOf(*array)) mData.addAll(listOf(*array))
selectPosition = -1 selectPosition = -1
notifyDataSetChanged() notifyDataSetChanged()
} }
return this return this
} }
/** /**
* 加载更多 * 加载更多
* *
* @param collection * @param collection
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun loadMore(collection: Collection<T>?): XDelegateAdapter<*, *> { fun loadMore(collection: Collection<T>?): XDelegateAdapter<*, *> {
if (collection != null) { if (collection != null) {
mData.addAll(collection) mData.addAll(collection)
notifyDataSetChanged() notifyDataSetChanged()
} }
return this return this
} }
/** /**
* 加载更多 * 加载更多
* *
* @param array * @param array
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun loadMore(array: Array<T>?): XDelegateAdapter<*, *> { fun loadMore(array: Array<T>?): XDelegateAdapter<*, *> {
if (array != null && array.isNotEmpty()) { if (array != null && array.isNotEmpty()) {
mData.addAll(listOf(*array)) mData.addAll(listOf(*array))
notifyDataSetChanged() notifyDataSetChanged()
} }
return this return this
} }
/** /**
* 添加一个 * 添加一个
* *
* @param item * @param item
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun load(item: T?): XDelegateAdapter<*, *> { fun load(item: T?): XDelegateAdapter<*, *> {
if (item != null) { if (item != null) {
mData.add(item) mData.add(item)
notifyDataSetChanged() notifyDataSetChanged()
} }
return this return this
} }
/** /**
* 设置当前列表的选中项 * 设置当前列表的选中项
* *
* @param selectPosition * @param selectPosition
* @return * @return
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun setSelectPosition(selectPosition: Int): XDelegateAdapter<*, *> { fun setSelectPosition(selectPosition: Int): XDelegateAdapter<*, *> {
this.selectPosition = selectPosition this.selectPosition = selectPosition
notifyDataSetChanged() notifyDataSetChanged()
return this return this
} }
/** /**
* 获取当前列表选中项 * 获取当前列表选中项
* *
* @return 当前列表选中项 * @return 当前列表选中项
*/ */
val selectItem: T? val selectItem: T?
get() = getItem(selectPosition) get() = getItem(selectPosition)
/** /**
* 清除数据 * 清除数据
*/ */
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun clear() { fun clear() {
if (!isEmpty) { if (!isEmpty) {
mData.clear() mData.clear()
selectPosition = -1 selectPosition = -1
notifyDataSetChanged() notifyDataSetChanged()
} }
} }
} }

View File

@ -1,157 +1,157 @@
package com.idormy.sms.forwarder.core package com.idormy.sms.forwarder.core
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.xuexiang.xpage.base.XPageActivity import com.xuexiang.xpage.base.XPageActivity
import com.xuexiang.xpage.base.XPageFragment import com.xuexiang.xpage.base.XPageFragment
import com.xuexiang.xpage.core.CoreSwitchBean import com.xuexiang.xpage.core.CoreSwitchBean
import com.xuexiang.xrouter.facade.service.SerializationService import com.xuexiang.xrouter.facade.service.SerializationService
import com.xuexiang.xrouter.launcher.XRouter import com.xuexiang.xrouter.launcher.XRouter
import com.xuexiang.xui.utils.ResUtils import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.slideback.SlideBack import com.xuexiang.xui.widget.slideback.SlideBack
import io.github.inflationx.viewpump.ViewPumpContextWrapper import io.github.inflationx.viewpump.ViewPumpContextWrapper
/** /**
* 基础容器Activity * 基础容器Activity
* *
* @author XUE * @author XUE
* @since 2019/3/22 11:21 * @since 2019/3/22 11:21
*/ */
@Suppress("MemberVisibilityCanBePrivate", "UNCHECKED_CAST") @Suppress("MemberVisibilityCanBePrivate", "UNCHECKED_CAST", "DEPRECATION")
open class BaseActivity<Binding : ViewBinding?> : XPageActivity() { open class BaseActivity<Binding : ViewBinding?> : XPageActivity() {
/** /**
* 获取Binding * 获取Binding
* *
* @return Binding * @return Binding
*/ */
/** /**
* ViewBinding * ViewBinding
*/ */
var binding: Binding? = null var binding: Binding? = null
protected set protected set
override fun attachBaseContext(newBase: Context) { override fun attachBaseContext(newBase: Context) {
//注入字体 //注入字体
super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)) super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase))
} }
override fun getCustomRootView(): View? { override fun getCustomRootView(): View? {
binding = viewBindingInflate(layoutInflater) binding = viewBindingInflate(layoutInflater)
return if (binding != null) binding!!.root else null return if (binding != null) binding!!.root else null
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
initStatusBarStyle() initStatusBarStyle()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
registerSlideBack() registerSlideBack()
} }
/** /**
* 构建ViewBinding * 构建ViewBinding
* *
* @param inflater inflater * @param inflater inflater
* @return ViewBinding * @return ViewBinding
*/ */
protected open fun viewBindingInflate(inflater: LayoutInflater?): Binding? { protected open fun viewBindingInflate(inflater: LayoutInflater?): Binding? {
return null return null
} }
/** /**
* 初始化状态栏的样式 * 初始化状态栏的样式
*/ */
protected open fun initStatusBarStyle() {} protected open fun initStatusBarStyle() {}
/** /**
* 打开fragment * 打开fragment
* *
* @param clazz 页面类 * @param clazz 页面类
* @param addToBackStack 是否添加到栈中 * @param addToBackStack 是否添加到栈中
* @return 打开的fragment对象 * @return 打开的fragment对象
*/ */
fun <T : XPageFragment?> openPage(clazz: Class<T>?, addToBackStack: Boolean): T { fun <T : XPageFragment?> openPage(clazz: Class<T>?, addToBackStack: Boolean): T {
val page = CoreSwitchBean(clazz) val page = CoreSwitchBean(clazz)
.setAddToBackStack(addToBackStack) .setAddToBackStack(addToBackStack)
return openPage(page) as T return openPage(page) as T
} }
/** /**
* 打开fragment * 打开fragment
* *
* @return 打开的fragment对象 * @return 打开的fragment对象
*/ */
fun <T : XPageFragment?> openNewPage(clazz: Class<T>?): T { fun <T : XPageFragment?> openNewPage(clazz: Class<T>?): T {
val page = CoreSwitchBean(clazz) val page = CoreSwitchBean(clazz)
.setNewActivity(true) .setNewActivity(true)
return openPage(page) as T return openPage(page) as T
} }
/** /**
* 切换fragment * 切换fragment
* *
* @param clazz 页面类 * @param clazz 页面类
* @return 打开的fragment对象 * @return 打开的fragment对象
*/ */
fun <T : XPageFragment?> switchPage(clazz: Class<T>?): T { fun <T : XPageFragment?> switchPage(clazz: Class<T>?): T {
return openPage(clazz, false) return openPage(clazz, false)
} }
/** /**
* 序列化对象 * 序列化对象
* *
* @param object * @param object
* @return * @return
*/ */
fun serializeObject(`object`: Any?): String { fun serializeObject(`object`: Any?): String {
return XRouter.getInstance().navigation(SerializationService::class.java) return XRouter.getInstance().navigation(SerializationService::class.java)
.object2Json(`object`) .object2Json(`object`)
} }
override fun onRelease() { override fun onRelease() {
unregisterSlideBack() unregisterSlideBack()
super.onRelease() super.onRelease()
} }
/** /**
* 注册侧滑回调 * 注册侧滑回调
*/ */
protected fun registerSlideBack() { protected fun registerSlideBack() {
if (isSupportSlideBack) { if (isSupportSlideBack) {
SlideBack.with(this) SlideBack.with(this)
.haveScroll(true) .haveScroll(true)
.edgeMode(if (ResUtils.isRtl()) SlideBack.EDGE_RIGHT else SlideBack.EDGE_LEFT) .edgeMode(if (ResUtils.isRtl()) SlideBack.EDGE_RIGHT else SlideBack.EDGE_LEFT)
.callBack { popPage() } .callBack { popPage() }
.register() .register()
} }
} }
/** /**
* 注销侧滑回调 * 注销侧滑回调
*/ */
protected fun unregisterSlideBack() { protected fun unregisterSlideBack() {
if (isSupportSlideBack) { if (isSupportSlideBack) {
SlideBack.unregister(this) SlideBack.unregister(this)
} }
} }
/** /**
* @return 是否支持侧滑返回 * @return 是否支持侧滑返回
*/ */
protected open val isSupportSlideBack: Boolean protected open val isSupportSlideBack: Boolean
get() { get() {
val page: CoreSwitchBean? = intent.getParcelableExtra(CoreSwitchBean.KEY_SWITCH_BEAN) val page: CoreSwitchBean? = intent.getParcelableExtra(CoreSwitchBean.KEY_SWITCH_BEAN)
return page == null || page.bundle == null || page.bundle.getBoolean( return page == null || page.bundle == null || page.bundle.getBoolean(
KEY_SUPPORT_SLIDE_BACK, KEY_SUPPORT_SLIDE_BACK,
true true
) )
} }
companion object { companion object {
/** /**
* 是否支持侧滑返回 * 是否支持侧滑返回
*/ */
const val KEY_SUPPORT_SLIDE_BACK = "key_support_slide_back" const val KEY_SUPPORT_SLIDE_BACK = "key_support_slide_back"
} }
} }

View File

@ -1,87 +1,87 @@
package com.idormy.sms.forwarder.core package com.idormy.sms.forwarder.core
import android.content.res.Configuration import android.content.res.Configuration
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import com.umeng.analytics.MobclickAgent import com.umeng.analytics.MobclickAgent
import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.base.XPageContainerListFragment import com.xuexiang.xpage.base.XPageContainerListFragment
import com.xuexiang.xui.widget.actionbar.TitleBar import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.actionbar.TitleUtils import com.xuexiang.xui.widget.actionbar.TitleUtils
/** /**
* 修改列表样式为主副标题显示 * 修改列表样式为主副标题显示
* *
* @author xuexiang * @author xuexiang
* @since 2018/11/22 上午11:26 * @since 2018/11/22 上午11:26
*/ */
@Suppress("unused") @Suppress("unused", "UNUSED_PARAMETER")
abstract class BaseContainerFragment : XPageContainerListFragment() { abstract class BaseContainerFragment : XPageContainerListFragment() {
override fun initPage() { override fun initPage() {
initTitle() initTitle()
initViews() initViews()
initListeners() initListeners()
} }
protected fun initTitle(): TitleBar { protected fun initTitle(): TitleBar {
return TitleUtils.addTitleBarDynamic( return TitleUtils.addTitleBarDynamic(
rootView as ViewGroup, rootView as ViewGroup,
pageTitle pageTitle
) { popToBack() } ) { popToBack() }
} }
override fun initData() { override fun initData() {
mSimpleData = initSimpleData(mSimpleData) mSimpleData = initSimpleData(mSimpleData)
val data: MutableList<Map<String?, String?>?> = ArrayList() val data: MutableList<Map<String?, String?>?> = ArrayList()
for (content in mSimpleData) { for (content in mSimpleData) {
val item: MutableMap<String?, String?> = HashMap() val item: MutableMap<String?, String?> = HashMap()
val index = content.indexOf("\n") val index = content.indexOf("\n")
if (index > 0) { if (index > 0) {
item[SimpleListAdapter.KEY_TITLE] = content.subSequence(0, index).toString() item[SimpleListAdapter.KEY_TITLE] = content.subSequence(0, index).toString()
item[SimpleListAdapter.KEY_SUB_TITLE] = item[SimpleListAdapter.KEY_SUB_TITLE] =
content.subSequence(index + 1, content.length).toString() content.subSequence(index + 1, content.length).toString()
} else { } else {
item[SimpleListAdapter.KEY_TITLE] = content item[SimpleListAdapter.KEY_TITLE] = content
item[SimpleListAdapter.KEY_SUB_TITLE] = "" item[SimpleListAdapter.KEY_SUB_TITLE] = ""
} }
data.add(item) data.add(item)
} }
listView.adapter = SimpleListAdapter(context, data) listView.adapter = SimpleListAdapter(context, data)
initSimply() initSimply()
} }
override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) { override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) {
onItemClick(view, position) onItemClick(view, position)
} }
@SingleClick @SingleClick
private fun onItemClick(view: View, position: Int) { private fun onItemClick(view: View, position: Int) {
onItemClick(position) onItemClick(position)
} }
override fun onDestroyView() { override fun onDestroyView() {
listView.onItemClickListener = null listView.onItemClickListener = null
super.onDestroyView() super.onDestroyView()
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
//屏幕旋转时刷新一下title //屏幕旋转时刷新一下title
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
val root = rootView as ViewGroup val root = rootView as ViewGroup
if (root.getChildAt(0) is TitleBar) { if (root.getChildAt(0) is TitleBar) {
root.removeViewAt(0) root.removeViewAt(0)
initTitle() initTitle()
} }
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
MobclickAgent.onPageStart(pageName) MobclickAgent.onPageStart(pageName)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
MobclickAgent.onPageEnd(pageName) MobclickAgent.onPageEnd(pageName)
} }
} }

View File

@ -1,52 +1,52 @@
package com.idormy.sms.forwarder.core.webview package com.idormy.sms.forwarder.core.webview
import android.annotation.TargetApi import android.annotation.TargetApi
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.util.AttributeSet import android.util.AttributeSet
import android.webkit.WebView import android.webkit.WebView
/** /**
* 修复 Android 5.0 & 5.1 打开 WebView 闪退问题 * 修复 Android 5.0 & 5.1 打开 WebView 闪退问题
* 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview * 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview
*/ */
@Suppress("unused") @Suppress("unused", "DEPRECATION")
class LollipopFixedWebView : WebView { class LollipopFixedWebView : WebView {
constructor(context: Context) : super(getFixedContext(context)) constructor(context: Context) : super(getFixedContext(context))
constructor(context: Context, attrs: AttributeSet?) : super(getFixedContext(context), attrs) constructor(context: Context, attrs: AttributeSet?) : super(getFixedContext(context), attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
getFixedContext(context), attrs, defStyleAttr getFixedContext(context), attrs, defStyleAttr
) )
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor( constructor(
context: Context, context: Context,
attrs: AttributeSet?, attrs: AttributeSet?,
defStyleAttr: Int, defStyleAttr: Int,
defStyleRes: Int, defStyleRes: Int,
) : super( ) : super(
getFixedContext(context), attrs, defStyleAttr, defStyleRes getFixedContext(context), attrs, defStyleAttr, defStyleRes
) )
constructor( constructor(
context: Context, context: Context,
attrs: AttributeSet?, attrs: AttributeSet?,
defStyleAttr: Int, defStyleAttr: Int,
privateBrowsing: Boolean, privateBrowsing: Boolean,
) : super( ) : super(
getFixedContext(context), attrs, defStyleAttr, privateBrowsing getFixedContext(context), attrs, defStyleAttr, privateBrowsing
) )
companion object { companion object {
fun getFixedContext(context: Context): Context { fun getFixedContext(context: Context): Context {
return if (isLollipopWebViewBug) { return if (isLollipopWebViewBug) {
// Avoid crashing on Android 5 and 6 (API level 21 to 23) // Avoid crashing on Android 5 and 6 (API level 21 to 23)
context.createConfigurationContext(Configuration()) context.createConfigurationContext(Configuration())
} else context } else context
} }
private val isLollipopWebViewBug: Boolean private val isLollipopWebViewBug: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M
} }
} }

View File

@ -1,132 +1,133 @@
package com.idormy.sms.forwarder.core.webview package com.idormy.sms.forwarder.core.webview
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.webview.WebViewInterceptDialog.Companion.show import com.idormy.sms.forwarder.core.webview.WebViewInterceptDialog.Companion.show
import com.just.agentweb.core.client.MiddlewareWebClientBase import com.just.agentweb.core.client.MiddlewareWebClientBase
import com.xuexiang.xui.utils.ResUtils import com.xuexiang.xui.utils.ResUtils
import java.util.* import java.util.*
/** /**
* 网络请求加载 * 网络请求加载
* WebClientWebViewClient 这个类主要帮助WebView处理各种通知url加载请求时间的中间件 * WebClientWebViewClient 这个类主要帮助WebView处理各种通知url加载请求时间的中间件
* *
* *
* *
* *
* 方法的执行顺序例如下面用了7个中间件一个 WebViewClient * 方法的执行顺序例如下面用了7个中间件一个 WebViewClient
* *
* *
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 1 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 1
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 2 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 2
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 3 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 3
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 4 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 4
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 5 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 5
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 6 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 6
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 7 * .useMiddlewareWebClient(getMiddlewareWebClient()) // 7
* DefaultWebClient // 8 * DefaultWebClient // 8
* .setWebViewClient(mWebViewClient) // 9 * .setWebViewClient(mWebViewClient) // 9
* *
* *
* *
* *
* 典型的洋葱模型 * 典型的洋葱模型
* 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1 * 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1
* *
* *
* *
* *
* 中断中间件的执行 删除super.methodName(...) 这行即可 * 中断中间件的执行 删除super.methodName(...) 这行即可
* *
* *
* 这里主要是做去广告的工作 * 这里主要是做去广告的工作
*/ */
open class MiddlewareWebViewClient : MiddlewareWebClientBase() { @Suppress("UNUSED_PARAMETER", "DEPRECATION", "OVERRIDE_DEPRECATION")
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) open class MiddlewareWebViewClient : MiddlewareWebClientBase() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
Log.i( override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
"Info", Log.i(
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.url.toString() + " c:" + count++ "Info",
) "MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.url.toString() + " c:" + count++
return if (shouldOverrideUrlLoadingByApp(view, request.url.toString())) { )
true return if (shouldOverrideUrlLoadingByApp(view, request.url.toString())) {
} else super.shouldOverrideUrlLoading(view, request) true
} } else super.shouldOverrideUrlLoading(view, request)
}
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
Log.i( override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
"Info", Log.i(
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + count++ "Info",
) "MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + count++
return if (shouldOverrideUrlLoadingByApp(view, url)) { )
true return if (shouldOverrideUrlLoadingByApp(view, url)) {
} else super.shouldOverrideUrlLoading(view, url) true
} } else super.shouldOverrideUrlLoading(view, url)
}
override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
val tUrl = url.lowercase(Locale.ROOT) override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
return if (!hasAdUrl(tUrl)) { val tUrl = url.lowercase(Locale.ROOT)
//正常加载 return if (!hasAdUrl(tUrl)) {
super.shouldInterceptRequest(view, tUrl) //正常加载
} else { super.shouldInterceptRequest(view, tUrl)
//含有广告资源屏蔽请求 } else {
WebResourceResponse(null, null, null) //含有广告资源屏蔽请求
} WebResourceResponse(null, null, null)
} }
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun shouldInterceptRequest( @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
view: WebView, override fun shouldInterceptRequest(
request: WebResourceRequest, view: WebView,
): WebResourceResponse? { request: WebResourceRequest,
val url = request.url.toString().lowercase(Locale.ROOT) ): WebResourceResponse? {
return if (!hasAdUrl(url)) { val url = request.url.toString().lowercase(Locale.ROOT)
//正常加载 return if (!hasAdUrl(url)) {
super.shouldInterceptRequest(view, request) //正常加载
} else { super.shouldInterceptRequest(view, request)
//含有广告资源屏蔽请求 } else {
WebResourceResponse(null, null, null) //含有广告资源屏蔽请求
} WebResourceResponse(null, null, null)
} }
}
/**
* 根据url的scheme处理跳转第三方app的业务,true代表拦截false代表不拦截 /**
*/ * 根据url的scheme处理跳转第三方app的业务,true代表拦截false代表不拦截
private fun shouldOverrideUrlLoadingByApp(webView: WebView, url: String): Boolean { */
if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) { private fun shouldOverrideUrlLoadingByApp(webView: WebView, url: String): Boolean {
//不拦截http, https, ftp的请求 if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) {
val uri = Uri.parse(url) //不拦截http, https, ftp的请求
if (uri != null && !(WebViewInterceptDialog.APP_LINK_HOST == uri.host && url.contains("xpage"))) { val uri = Uri.parse(url)
return false if (uri != null && !(WebViewInterceptDialog.APP_LINK_HOST == uri.host && url.contains("xpage"))) {
} return false
} }
show(url) }
return true show(url)
} return true
}
companion object {
private var count = 1 companion object {
private var count = 1
/**
* 判断是否存在广告的链接 /**
* * 判断是否存在广告的链接
* @param url *
* @return * @param url
*/ * @return
private fun hasAdUrl(url: String): Boolean { */
val adUrls = ResUtils.getStringArray(R.array.adBlockUrl) private fun hasAdUrl(url: String): Boolean {
for (adUrl in adUrls) { val adUrls = ResUtils.getStringArray(R.array.adBlockUrl)
if (url.contains(adUrl)) { for (adUrl in adUrls) {
return true if (url.contains(adUrl)) {
} return true
} }
return false }
} return false
} }
}
} }

View File

@ -1,88 +1,88 @@
package com.idormy.sms.forwarder.entity package com.idormy.sms.forwarder.entity
import android.graphics.Rect import android.graphics.Rect
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable.Creator import android.os.Parcelable.Creator
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.xuexiang.xui.utils.ResUtils import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.widget.imageview.preview.enitity.IPreviewInfo import com.xuexiang.xui.widget.imageview.preview.enitity.IPreviewInfo
/** /**
* 图片预览实体类 * 图片预览实体类
* *
* @author xuexiang * @author xuexiang
* @since 2018/12/7 下午5:34 * @since 2018/12/7 下午5:34
*/ */
@Suppress("unused") @Suppress("unused", "DEPRECATION")
data class ImageInfo( data class ImageInfo(
//图片地址 //图片地址
var mUrl: String, var mUrl: String,
//记录坐标 //记录坐标
var mBounds: Rect? = null, var mBounds: Rect? = null,
var mVideoUrl: String? = null, var mVideoUrl: String? = null,
var description: String? = ResUtils.getString(R.string.description), var description: String? = ResUtils.getString(R.string.description),
) : IPreviewInfo { ) : IPreviewInfo {
constructor(url: String) : this(mUrl = url) constructor(url: String) : this(mUrl = url)
constructor(url: String, bounds: Rect?) : this(mUrl = url, mBounds = bounds) constructor(url: String, bounds: Rect?) : this(mUrl = url, mBounds = bounds)
constructor(videoUrl: String?, url: String) : this(mUrl = url, mVideoUrl = videoUrl) constructor(videoUrl: String?, url: String) : this(mUrl = url, mVideoUrl = videoUrl)
override fun getUrl(): String { //将你的图片地址字段返回 override fun getUrl(): String { //将你的图片地址字段返回
return mUrl return mUrl
} }
fun setUrl(url: String) { fun setUrl(url: String) {
mUrl = url mUrl = url
} }
override fun getBounds(): Rect? { //将你的图片显示坐标字段返回 override fun getBounds(): Rect? { //将你的图片显示坐标字段返回
return mBounds return mBounds
} }
override fun getVideoUrl(): String? { override fun getVideoUrl(): String? {
return mVideoUrl return mVideoUrl
} }
fun setBounds(bounds: Rect) { fun setBounds(bounds: Rect) {
mBounds = bounds mBounds = bounds
} }
fun setVideoUrl(videoUrl: String) { fun setVideoUrl(videoUrl: String) {
mVideoUrl = videoUrl mVideoUrl = videoUrl
} }
override fun describeContents(): Int { override fun describeContents(): Int {
return 0 return 0
} }
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(mUrl) dest.writeString(mUrl)
dest.writeParcelable(mBounds, flags) dest.writeParcelable(mBounds, flags)
dest.writeString(description) dest.writeString(description)
dest.writeString(mVideoUrl) dest.writeString(mVideoUrl)
} }
constructor(`in`: Parcel) : this( constructor(`in`: Parcel) : this(
mUrl = `in`.readString()!!, mUrl = `in`.readString()!!,
mBounds = `in`.readParcelable(Rect::class.java.classLoader), mBounds = `in`.readParcelable(Rect::class.java.classLoader),
description = `in`.readString(), description = `in`.readString(),
mVideoUrl = `in`.readString() mVideoUrl = `in`.readString()
) )
companion object CREATOR : Creator<ImageInfo> { companion object CREATOR : Creator<ImageInfo> {
fun newInstance(url: String, bounds: Rect): List<ImageInfo> { fun newInstance(url: String, bounds: Rect): List<ImageInfo> {
return listOf(ImageInfo(url, bounds)) return listOf(ImageInfo(url, bounds))
} }
override fun createFromParcel(parcel: Parcel): ImageInfo { override fun createFromParcel(parcel: Parcel): ImageInfo {
return ImageInfo(parcel) return ImageInfo(parcel)
} }
override fun newArray(size: Int): Array<ImageInfo?> { override fun newArray(size: Int): Array<ImageInfo?> {
return arrayOfNulls(size) return arrayOfNulls(size)
} }
} }
} }

View File

@ -3,6 +3,7 @@ package com.idormy.sms.forwarder.entity.setting
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import java.io.Serializable import java.io.Serializable
@Suppress("SENSELESS_COMPARISON")
data class FeishuAppSetting( data class FeishuAppSetting(
var appId: String = "", var appId: String = "",
val appSecret: String = "", val appSecret: String = "",

View File

@ -1,146 +1,146 @@
package com.idormy.sms.forwarder.fragment package com.idormy.sms.forwarder.fragment
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color import android.graphics.Color
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.database.entity.Frpc import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
import com.idormy.sms.forwarder.databinding.FragmentFrpcEditBinding import com.idormy.sms.forwarder.databinding.FragmentFrpcEditBinding
import com.idormy.sms.forwarder.utils.EVENT_FRPC_UPDATE_CONFIG import com.idormy.sms.forwarder.utils.EVENT_FRPC_UPDATE_CONFIG
import com.idormy.sms.forwarder.utils.INTENT_FRPC_EDIT_FILE import com.idormy.sms.forwarder.utils.INTENT_FRPC_EDIT_FILE
import com.idormy.sms.forwarder.utils.XToastUtils import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus import com.jeremyliao.liveeventbus.LiveEventBus
import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.utils.ResUtils import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.ThemeUtils import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.widget.actionbar.TitleBar import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.button.switchbutton.SwitchButton import com.xuexiang.xui.widget.button.switchbutton.SwitchButton
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
@Suppress("PrivatePropertyName") @Suppress("PrivatePropertyName", "DEPRECATION")
@Page(name = "Frp内网穿透·编辑配置") @Page(name = "Frp内网穿透·编辑配置")
class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() { class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() {
var titleBar: TitleBar? = null var titleBar: TitleBar? = null
var frpc: Frpc? = null var frpc: Frpc? = null
private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) } private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) }
override fun initViews() { override fun initViews() {
val pairCompleteMap: MutableMap<Char, Char> = HashMap() val pairCompleteMap: MutableMap<Char, Char> = HashMap()
pairCompleteMap['{'] = '}' pairCompleteMap['{'] = '}'
pairCompleteMap['['] = ']' pairCompleteMap['['] = ']'
pairCompleteMap['('] = ')' pairCompleteMap['('] = ')'
pairCompleteMap['<'] = '>' pairCompleteMap['<'] = '>'
pairCompleteMap['"'] = '"' pairCompleteMap['"'] = '"'
binding!!.editText.enablePairComplete(true) binding!!.editText.enablePairComplete(true)
binding!!.editText.enablePairCompleteCenterCursor(true) binding!!.editText.enablePairCompleteCenterCursor(true)
binding!!.editText.setPairCompleteMap(pairCompleteMap) binding!!.editText.setPairCompleteMap(pairCompleteMap)
binding!!.editText.setEnableLineNumber(true) binding!!.editText.setEnableLineNumber(true)
binding!!.editText.setLineNumberTextColor(Color.LTGRAY) binding!!.editText.setLineNumberTextColor(Color.LTGRAY)
binding!!.editText.setLineNumberTextSize(24f) binding!!.editText.setLineNumberTextSize(24f)
binding!!.editText.textSize = 14f binding!!.editText.textSize = 14f
} }
override fun viewBindingInflate(inflater: LayoutInflater, container: ViewGroup): FragmentFrpcEditBinding { override fun viewBindingInflate(inflater: LayoutInflater, container: ViewGroup): FragmentFrpcEditBinding {
return FragmentFrpcEditBinding.inflate(inflater, container, false) return FragmentFrpcEditBinding.inflate(inflater, container, false)
} }
override fun initTitle(): TitleBar? { override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false) titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_frpc) titleBar!!.setTitle(R.string.menu_frpc)
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent)) titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_save) { titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_save) {
@SuppressLint("ResourceAsColor") @SuppressLint("ResourceAsColor")
@SingleClick @SingleClick
override fun performAction(view: View) { override fun performAction(view: View) {
if (frpc == null) return if (frpc == null) return
val dialogFrpc = View.inflate(requireContext(), R.layout.dialog_frpc_save, null) val dialogFrpc = View.inflate(requireContext(), R.layout.dialog_frpc_save, null)
val tvName = dialogFrpc.findViewById<MaterialEditText>(R.id.tv_name) val tvName = dialogFrpc.findViewById<MaterialEditText>(R.id.tv_name)
val sbAutorun = dialogFrpc.findViewById<SwitchButton>(R.id.sb_autorun) val sbAutorun = dialogFrpc.findViewById<SwitchButton>(R.id.sb_autorun)
tvName.setText(frpc!!.name) tvName.setText(frpc!!.name)
sbAutorun.setCheckedImmediately(frpc!!.autorun == 1) sbAutorun.setCheckedImmediately(frpc!!.autorun == 1)
frpc!!.config = binding!!.editText.text.toString() frpc!!.config = binding!!.editText.text.toString()
if (TextUtils.isEmpty(frpc!!.config)) { if (TextUtils.isEmpty(frpc!!.config)) {
XToastUtils.error(R.string.tips_input_config_content) XToastUtils.error(R.string.tips_input_config_content)
return return
} }
MaterialDialog.Builder(context!!) MaterialDialog.Builder(context!!)
.iconRes(R.drawable.ic_menu_frpc) .iconRes(R.drawable.ic_menu_frpc)
.title(R.string.title_save_config) .title(R.string.title_save_config)
.customView(dialogFrpc, true) .customView(dialogFrpc, true)
.cancelable(false) .cancelable(false)
.autoDismiss(false) .autoDismiss(false)
.neutralText(R.string.action_quit) .neutralText(R.string.action_quit)
.neutralColor(ResUtils.getColors(R.color.red)) .neutralColor(ResUtils.getColors(R.color.red))
.onNeutral { dialog: MaterialDialog?, _: DialogAction? -> .onNeutral { dialog: MaterialDialog?, _: DialogAction? ->
dialog?.dismiss() dialog?.dismiss()
activity?.onBackPressed() activity?.onBackPressed()
} }
.negativeText(R.string.action_back) .negativeText(R.string.action_back)
.negativeColor(ResUtils.getColors(R.color.colorBlueGrey)) .negativeColor(ResUtils.getColors(R.color.colorBlueGrey))
.onNegative { dialog: MaterialDialog?, _: DialogAction? -> .onNegative { dialog: MaterialDialog?, _: DialogAction? ->
dialog?.dismiss() dialog?.dismiss()
} }
.positiveText(R.string.action_save) .positiveText(R.string.action_save)
.onPositive { dialog: MaterialDialog?, _: DialogAction? -> .onPositive { dialog: MaterialDialog?, _: DialogAction? ->
try { try {
frpc!!.autorun = if (sbAutorun.isChecked) 1 else 0 frpc!!.autorun = if (sbAutorun.isChecked) 1 else 0
frpc!!.name = tvName.text.toString() frpc!!.name = tvName.text.toString()
if (TextUtils.isEmpty(frpc!!.name)) { if (TextUtils.isEmpty(frpc!!.name)) {
XToastUtils.error(R.string.tips_input_config_name) XToastUtils.error(R.string.tips_input_config_name)
return@onPositive return@onPositive
} }
if (TextUtils.isEmpty(frpc!!.uid)) { if (TextUtils.isEmpty(frpc!!.uid)) {
viewModel.insert(frpc!!) viewModel.insert(frpc!!)
} else { } else {
viewModel.update(frpc!!) viewModel.update(frpc!!)
} }
dialog?.dismiss() dialog?.dismiss()
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(frpc) LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(frpc)
XToastUtils.success(R.string.tipSaveSuccess) XToastUtils.success(R.string.tipSaveSuccess)
activity?.onBackPressed() activity?.onBackPressed()
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error(e.message.toString()) XToastUtils.error(e.message.toString())
} }
}.show() }.show()
} }
}) })
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_restore) { titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_restore) {
@SingleClick @SingleClick
override fun performAction(view: View) { override fun performAction(view: View) {
binding!!.editText.setText(frpc?.config!!) binding!!.editText.setText(frpc?.config!!)
XToastUtils.success(R.string.tipRestoreSuccess) XToastUtils.success(R.string.tipRestoreSuccess)
} }
}) })
return titleBar return titleBar
} }
override fun initListeners() { override fun initListeners() {
LiveEventBus.get(INTENT_FRPC_EDIT_FILE, Frpc::class.java).observeSticky(this) { value: Frpc -> LiveEventBus.get(INTENT_FRPC_EDIT_FILE, Frpc::class.java).observeSticky(this) { value: Frpc ->
frpc = value frpc = value
binding!!.editText.setText(value.config) binding!!.editText.setText(value.config)
titleBar!!.setTitle(if (TextUtils.isEmpty(value.name)) getString(R.string.noName) else value.name) titleBar!!.setTitle(if (TextUtils.isEmpty(value.name)) getString(R.string.noName) else value.name)
} }
} }
} }

View File

@ -39,7 +39,7 @@ import io.reactivex.schedulers.Schedulers
import java.util.* import java.util.*
@Page(name = "URL Scheme") @Page(name = "URL Scheme")
@Suppress("PrivatePropertyName") @Suppress("PrivatePropertyName", "DEPRECATION")
class UrlSchemeFragment : BaseFragment<FragmentSendersUrlSchemeBinding?>(), View.OnClickListener { class UrlSchemeFragment : BaseFragment<FragmentSendersUrlSchemeBinding?>(), View.OnClickListener {
private val TAG: String = UrlSchemeFragment::class.java.simpleName private val TAG: String = UrlSchemeFragment::class.java.simpleName

View File

@ -1,105 +1,106 @@
package com.idormy.sms.forwarder.utils package com.idormy.sms.forwarder.utils
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import java.io.File import java.io.File
import java.math.BigDecimal import java.math.BigDecimal
class CacheUtils private constructor() { @Suppress("DEPRECATION")
companion object { class CacheUtils private constructor() {
/** companion object {
* 获取缓存大小 /**
* * 获取缓存大小
* @param context 上下文 *
* @return 缓存大小 * @param context 上下文
*/ * @return 缓存大小
fun getTotalCacheSize(context: Context): String { */
return try { fun getTotalCacheSize(context: Context): String {
var cacheSize = getFolderSize(context.cacheDir) return try {
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { var cacheSize = getFolderSize(context.cacheDir)
cacheSize += getFolderSize(context.externalCacheDir) if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
} cacheSize += getFolderSize(context.externalCacheDir)
getFormatSize(cacheSize.toDouble()) }
} catch (e: Exception) { getFormatSize(cacheSize.toDouble())
e.printStackTrace() } catch (e: Exception) {
"0KB" e.printStackTrace()
} "0KB"
} }
}
/***
* 清理所有缓存 /***
* @param context 上下文 * 清理所有缓存
*/ * @param context 上下文
fun clearAllCache(context: Context) { */
deleteDir(context.cacheDir) fun clearAllCache(context: Context) {
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { deleteDir(context.cacheDir)
deleteDir(context.externalCacheDir) if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
} deleteDir(context.externalCacheDir)
} }
}
private fun deleteDir(dir: File?): Boolean {
if (dir != null && dir.isDirectory) { private fun deleteDir(dir: File?): Boolean {
val children = dir.list()!! if (dir != null && dir.isDirectory) {
for (child in children) { val children = dir.list()!!
val success = deleteDir(File(dir, child)) for (child in children) {
if (!success) { val success = deleteDir(File(dir, child))
return false if (!success) {
} return false
} }
} }
assert(dir != null) }
return dir!!.delete() assert(dir != null)
} return dir!!.delete()
}
// 获取文件
//Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据 // 获取文件
//Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据 //Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
private fun getFolderSize(file: File?): Long { //Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
var size: Long = 0 private fun getFolderSize(file: File?): Long {
try { var size: Long = 0
val fileList = file!!.listFiles()!! try {
for (value in fileList) { val fileList = file!!.listFiles()!!
// 如果下面还有文件 for (value in fileList) {
size = if (value.isDirectory) { // 如果下面还有文件
size + getFolderSize(value) size = if (value.isDirectory) {
} else { size + getFolderSize(value)
size + value.length() } else {
} size + value.length()
} }
} catch (e: Exception) { }
e.printStackTrace() } catch (e: Exception) {
} e.printStackTrace()
return size }
} return size
}
/**
* 格式化单位 /**
* * 格式化单位
* @param size 文件大小 *
* @return 结果 * @param size 文件大小
*/ * @return 结果
private fun getFormatSize(size: Double): String { */
val kiloByte = size / 1024 private fun getFormatSize(size: Double): String {
if (kiloByte < 1) { val kiloByte = size / 1024
return "0KB" if (kiloByte < 1) {
} return "0KB"
val megaByte = kiloByte / 1024 }
if (megaByte < 1) { val megaByte = kiloByte / 1024
val result1 = BigDecimal(kiloByte.toString()) if (megaByte < 1) {
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB" val result1 = BigDecimal(kiloByte.toString())
} return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"
val gigaByte = megaByte / 1024 }
if (gigaByte < 1) { val gigaByte = megaByte / 1024
val result2 = BigDecimal(megaByte.toString()) if (gigaByte < 1) {
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB" val result2 = BigDecimal(megaByte.toString())
} return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"
val teraBytes = gigaByte / 1024 }
if (teraBytes < 1) { val teraBytes = gigaByte / 1024
val result3 = BigDecimal(gigaByte.toString()) if (teraBytes < 1) {
return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB" val result3 = BigDecimal(gigaByte.toString())
} return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB"
val result4 = BigDecimal(teraBytes) }
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB" val result4 = BigDecimal(teraBytes)
} return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB"
} }
}
} }

View File

@ -1,51 +1,52 @@
package com.idormy.sms.forwarder.utils package com.idormy.sms.forwarder.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
class KeepAliveUtils private constructor() { @Suppress("DEPRECATION")
class KeepAliveUtils private constructor() {
companion object {
fun isIgnoreBatteryOptimization(activity: Activity): Boolean { companion object {
//安卓6.0以下没有忽略电池优化 fun isIgnoreBatteryOptimization(activity: Activity): Boolean {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { //安卓6.0以下没有忽略电池优化
true return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
} else try { true
val powerManager: PowerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager } else try {
powerManager.isIgnoringBatteryOptimizations(activity.packageName) val powerManager: PowerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
} catch (e: Exception) { powerManager.isIgnoringBatteryOptimizations(activity.packageName)
XToastUtils.error(R.string.unsupport) } catch (e: Exception) {
false XToastUtils.error(R.string.unsupport)
} false
} }
}
@RequiresApi(api = Build.VERSION_CODES.M)
fun ignoreBatteryOptimization(activity: Activity) { @RequiresApi(api = Build.VERSION_CODES.M)
try { fun ignoreBatteryOptimization(activity: Activity) {
if (isIgnoreBatteryOptimization(activity)) { try {
return if (isIgnoreBatteryOptimization(activity)) {
} return
@SuppressLint("BatteryLife") val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }
intent.data = Uri.parse("package:" + activity.packageName) @SuppressLint("BatteryLife") val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
val resolveInfo: ResolveInfo? = activity.packageManager.resolveActivity(intent, 0) intent.data = Uri.parse("package:" + activity.packageName)
if (resolveInfo != null) { val resolveInfo: ResolveInfo? = activity.packageManager.resolveActivity(intent, 0)
activity.startActivity(intent) if (resolveInfo != null) {
} else { activity.startActivity(intent)
XToastUtils.error(R.string.unsupport) } else {
} XToastUtils.error(R.string.unsupport)
} catch (e: Exception) { }
XToastUtils.error(R.string.unsupport) } catch (e: Exception) {
} XToastUtils.error(R.string.unsupport)
} }
}
}
}
} }

View File

@ -32,7 +32,7 @@ import com.xuexiang.xutil.resource.ResUtils
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@Suppress("PropertyName") @Suppress("PropertyName", "DEPRECATION")
class PhoneUtils private constructor() { class PhoneUtils private constructor() {
companion object { companion object {

View File

@ -7,6 +7,7 @@ import java.io.*
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@Suppress("unused", "UNCHECKED_CAST")
class SharedPreference<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> { class SharedPreference<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
companion object { companion object {

View File

@ -1,47 +1,49 @@
package com.idormy.sms.forwarder.utils.mail package com.idormy.sms.forwarder.utils.mail
import android.util.Log import android.util.Log
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.mail.Transport import javax.mail.Transport
/** /**
* 邮件发送器 * 邮件发送器
*/ */
object MailSender { object MailSender {
/** /**
* 获取单例 * 获取单例
*/ */
@JvmStatic @JvmStatic
fun getInstance() = this fun getInstance() = this
/** /**
* 发送邮件 * 发送邮件
*/ */
fun sendMail(mail: Mail, onMailSendListener: OnMailSendListener? = null) { fun sendMail(mail: Mail, onMailSendListener: OnMailSendListener? = null) {
val send = GlobalScope.async(Dispatchers.IO) { @Suppress("OPT_IN_USAGE")
Transport.send(MailUtil.createMailMessage(mail)) val send = GlobalScope.async(Dispatchers.IO) {
} Transport.send(MailUtil.createMailMessage(mail))
}
GlobalScope.launch(Dispatchers.Main) {
runCatching { @Suppress("OPT_IN_USAGE")
send.await() GlobalScope.launch(Dispatchers.Main) {
onMailSendListener?.onSuccess() runCatching {
}.onFailure { send.await()
Log.e("MailSender", it.message.toString()) onMailSendListener?.onSuccess()
onMailSendListener?.onError(it) }.onFailure {
} Log.e("MailSender", it.message.toString())
} onMailSendListener?.onError(it)
} }
}
/** }
* 发送回调
*/ /**
interface OnMailSendListener { * 发送回调
fun onSuccess() */
fun onError(e: Throwable) interface OnMailSendListener {
} fun onSuccess()
fun onError(e: Throwable)
}
} }

View File

@ -41,7 +41,7 @@ class UMengInit private constructor() {
return return
} }
UMConfigure.setLogEnabled(false) UMConfigure.setLogEnabled(false)
UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel(application)) UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel()) //getChannel(application)
// 用户同意了隐私协议 // 用户同意了隐私协议
if (isAgreePrivacy) { if (isAgreePrivacy) {
realInit(application) realInit(application)
@ -62,7 +62,7 @@ class UMengInit private constructor() {
UMConfigure.init( UMConfigure.init(
application, application,
BuildConfig.APP_ID_UMENG, BuildConfig.APP_ID_UMENG,
getChannel(application), getChannel(), //getChannel(application)
UMConfigure.DEVICE_TYPE_PHONE, UMConfigure.DEVICE_TYPE_PHONE,
"" ""
) )
@ -78,7 +78,7 @@ class UMengInit private constructor() {
* @param context * @param context
* @return * @return
*/ */
private fun getChannel(context: Context?): String { private fun getChannel(): String { //context: Context?
//return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID) //return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID)
return DEFAULT_CHANNEL_ID return DEFAULT_CHANNEL_ID
} }

View File

@ -4,7 +4,7 @@ buildscript {
apply from: './versions.gradle' apply from: './versions.gradle'
addRepos(repositories) // addRepos(repositories) //
dependencies { dependencies {
classpath "com.android.tools.build:gradle:$versions.android_gradle_plugin" classpath deps.android_gradle_plugin
classpath deps.android_maven_gradle_plugin classpath deps.android_maven_gradle_plugin
// //
classpath 'com.chenenyu:img-optimizer:1.2.0' classpath 'com.chenenyu:img-optimizer:1.2.0'
@ -58,9 +58,9 @@ allprojects {
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
FileTree tree = fileTree(dir: rootProject.getRootDir()) FileTree rootTree = fileTree(dir: rootDir)
tree.each { File file -> rootTree.each { File file ->
if (file.toString().contains("ajcore") && file.toString().endsWith(".txt")) { if ((file.toString().contains("ajcore") || file.toString().contains("mapping") || file.toString().contains("seeds") || file.toString().contains("unused")) && file.toString().endsWith(".txt")) {
delete file delete file
} }
} }