整理:Code Review
This commit is contained in:
parent
6571775a0f
commit
9c6f404190
|
@ -291,3 +291,10 @@
|
|||
-keep interface * implements com.xuexiang.xrouter.facade.template.IProvider
|
||||
# 如果使用了 单类注入,即不定义接口实现 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.**
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<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_BACKGROUND_LOCATION" />
|
||||
<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_WIFI_STATE" />
|
||||
|
@ -55,9 +56,6 @@
|
|||
<uses-permission
|
||||
android:name="android.permission.READ_LOGS"
|
||||
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
|
||||
android:name=".App"
|
||||
|
|
|
@ -1,269 +1,269 @@
|
|||
package com.idormy.sms.forwarder.adapter.base.delegate
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
|
||||
/**
|
||||
* 基础DelegateAdapter
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2020/3/20 12:17 AM
|
||||
*/
|
||||
@Suppress("unused")
|
||||
abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapter.Adapter<V> {
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
private val mData: MutableList<T> = ArrayList()
|
||||
/**
|
||||
* @return 当前列表的选中项
|
||||
*/
|
||||
/**
|
||||
* 当前点击的条目
|
||||
*/
|
||||
private var selectPosition = -1
|
||||
|
||||
constructor()
|
||||
constructor(list: Collection<T>?) {
|
||||
if (list != null) {
|
||||
mData.addAll(list)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(data: Array<T>?) {
|
||||
if (data != null && data.isNotEmpty()) {
|
||||
mData.addAll(listOf(*data))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建自定义的ViewHolder
|
||||
*
|
||||
* @param parent
|
||||
* @param viewType
|
||||
* @return
|
||||
*/
|
||||
protected abstract fun getViewHolder(parent: ViewGroup, viewType: Int): V
|
||||
|
||||
/**
|
||||
* 绑定数据
|
||||
*
|
||||
* @param holder
|
||||
* @param position 索引
|
||||
* @param item 列表项
|
||||
*/
|
||||
protected abstract fun bindData(holder: V, position: Int, item: T)
|
||||
|
||||
/**
|
||||
* 加载布局获取控件
|
||||
*
|
||||
* @param parent 父布局
|
||||
* @param layoutId 布局ID
|
||||
* @return
|
||||
*/
|
||||
protected fun inflateView(parent: ViewGroup, @LayoutRes layoutId: Int): View {
|
||||
return LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): V {
|
||||
return getViewHolder(parent, viewType)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: V, position: Int) {
|
||||
bindData(holder, position, mData[position])
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表项
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
private fun getItem(position: Int): T? {
|
||||
return if (checkPosition(position)) mData[position] else null
|
||||
}
|
||||
|
||||
private fun checkPosition(position: Int): Boolean {
|
||||
return position >= 0 && position <= mData.size - 1
|
||||
}
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = itemCount == 0
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return mData.size
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 数据源
|
||||
*/
|
||||
val data: List<T>
|
||||
get() = mData
|
||||
|
||||
/**
|
||||
* 给指定位置添加一项
|
||||
*
|
||||
* @param pos
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun add(pos: Int, item: T): XDelegateAdapter<*, *> {
|
||||
mData.add(pos, item)
|
||||
notifyItemInserted(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 在列表末端增加一项
|
||||
*
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun add(item: T): XDelegateAdapter<*, *> {
|
||||
mData.add(item)
|
||||
notifyItemInserted(mData.size - 1)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除列表中指定索引的数据
|
||||
*
|
||||
* @param pos
|
||||
* @return
|
||||
*/
|
||||
fun delete(pos: Int): XDelegateAdapter<*, *> {
|
||||
mData.removeAt(pos)
|
||||
notifyItemRemoved(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表中指定位置的数据
|
||||
*
|
||||
* @param pos
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun refresh(pos: Int, item: T): XDelegateAdapter<*, *> {
|
||||
mData[pos] = item
|
||||
notifyItemChanged(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表数据
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
open fun refresh(collection: Collection<T>?): XDelegateAdapter<*, *> {
|
||||
if (collection != null) {
|
||||
mData.clear()
|
||||
mData.addAll(collection)
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表数据
|
||||
*
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun refresh(array: Array<T>?): XDelegateAdapter<*, *> {
|
||||
if (array != null && array.isNotEmpty()) {
|
||||
mData.clear()
|
||||
mData.addAll(listOf(*array))
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun loadMore(collection: Collection<T>?): XDelegateAdapter<*, *> {
|
||||
if (collection != null) {
|
||||
mData.addAll(collection)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多
|
||||
*
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun loadMore(array: Array<T>?): XDelegateAdapter<*, *> {
|
||||
if (array != null && array.isNotEmpty()) {
|
||||
mData.addAll(listOf(*array))
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个
|
||||
*
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun load(item: T?): XDelegateAdapter<*, *> {
|
||||
if (item != null) {
|
||||
mData.add(item)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前列表的选中项
|
||||
*
|
||||
* @param selectPosition
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setSelectPosition(selectPosition: Int): XDelegateAdapter<*, *> {
|
||||
this.selectPosition = selectPosition
|
||||
notifyDataSetChanged()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前列表选中项
|
||||
*
|
||||
* @return 当前列表选中项
|
||||
*/
|
||||
val selectItem: T?
|
||||
get() = getItem(selectPosition)
|
||||
|
||||
/**
|
||||
* 清除数据
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun clear() {
|
||||
if (!isEmpty) {
|
||||
mData.clear()
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.adapter.base.delegate
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.android.vlayout.DelegateAdapter
|
||||
|
||||
/**
|
||||
* 基础DelegateAdapter
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2020/3/20 12:17 AM
|
||||
*/
|
||||
@Suppress("unused", "WRONG_TYPE_PARAMETER_NULLABILITY_FOR_JAVA_OVERRIDE")
|
||||
abstract class XDelegateAdapter<T, V : RecyclerView.ViewHolder?> : DelegateAdapter.Adapter<V> {
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
private val mData: MutableList<T> = ArrayList()
|
||||
/**
|
||||
* @return 当前列表的选中项
|
||||
*/
|
||||
/**
|
||||
* 当前点击的条目
|
||||
*/
|
||||
private var selectPosition = -1
|
||||
|
||||
constructor()
|
||||
constructor(list: Collection<T>?) {
|
||||
if (list != null) {
|
||||
mData.addAll(list)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(data: Array<T>?) {
|
||||
if (data != null && data.isNotEmpty()) {
|
||||
mData.addAll(listOf(*data))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建自定义的ViewHolder
|
||||
*
|
||||
* @param parent
|
||||
* @param viewType
|
||||
* @return
|
||||
*/
|
||||
protected abstract fun getViewHolder(parent: ViewGroup, viewType: Int): V
|
||||
|
||||
/**
|
||||
* 绑定数据
|
||||
*
|
||||
* @param holder
|
||||
* @param position 索引
|
||||
* @param item 列表项
|
||||
*/
|
||||
protected abstract fun bindData(holder: V, position: Int, item: T)
|
||||
|
||||
/**
|
||||
* 加载布局获取控件
|
||||
*
|
||||
* @param parent 父布局
|
||||
* @param layoutId 布局ID
|
||||
* @return
|
||||
*/
|
||||
protected fun inflateView(parent: ViewGroup, @LayoutRes layoutId: Int): View {
|
||||
return LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): V {
|
||||
return getViewHolder(parent, viewType)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: V, position: Int) {
|
||||
bindData(holder, position, mData[position])
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表项
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
private fun getItem(position: Int): T? {
|
||||
return if (checkPosition(position)) mData[position] else null
|
||||
}
|
||||
|
||||
private fun checkPosition(position: Int): Boolean {
|
||||
return position >= 0 && position <= mData.size - 1
|
||||
}
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = itemCount == 0
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return mData.size
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 数据源
|
||||
*/
|
||||
val data: List<T>
|
||||
get() = mData
|
||||
|
||||
/**
|
||||
* 给指定位置添加一项
|
||||
*
|
||||
* @param pos
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun add(pos: Int, item: T): XDelegateAdapter<*, *> {
|
||||
mData.add(pos, item)
|
||||
notifyItemInserted(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 在列表末端增加一项
|
||||
*
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun add(item: T): XDelegateAdapter<*, *> {
|
||||
mData.add(item)
|
||||
notifyItemInserted(mData.size - 1)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除列表中指定索引的数据
|
||||
*
|
||||
* @param pos
|
||||
* @return
|
||||
*/
|
||||
fun delete(pos: Int): XDelegateAdapter<*, *> {
|
||||
mData.removeAt(pos)
|
||||
notifyItemRemoved(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表中指定位置的数据
|
||||
*
|
||||
* @param pos
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun refresh(pos: Int, item: T): XDelegateAdapter<*, *> {
|
||||
mData[pos] = item
|
||||
notifyItemChanged(pos)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表数据
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
open fun refresh(collection: Collection<T>?): XDelegateAdapter<*, *> {
|
||||
if (collection != null) {
|
||||
mData.clear()
|
||||
mData.addAll(collection)
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新列表数据
|
||||
*
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun refresh(array: Array<T>?): XDelegateAdapter<*, *> {
|
||||
if (array != null && array.isNotEmpty()) {
|
||||
mData.clear()
|
||||
mData.addAll(listOf(*array))
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun loadMore(collection: Collection<T>?): XDelegateAdapter<*, *> {
|
||||
if (collection != null) {
|
||||
mData.addAll(collection)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多
|
||||
*
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun loadMore(array: Array<T>?): XDelegateAdapter<*, *> {
|
||||
if (array != null && array.isNotEmpty()) {
|
||||
mData.addAll(listOf(*array))
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个
|
||||
*
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun load(item: T?): XDelegateAdapter<*, *> {
|
||||
if (item != null) {
|
||||
mData.add(item)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前列表的选中项
|
||||
*
|
||||
* @param selectPosition
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setSelectPosition(selectPosition: Int): XDelegateAdapter<*, *> {
|
||||
this.selectPosition = selectPosition
|
||||
notifyDataSetChanged()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前列表选中项
|
||||
*
|
||||
* @return 当前列表选中项
|
||||
*/
|
||||
val selectItem: T?
|
||||
get() = getItem(selectPosition)
|
||||
|
||||
/**
|
||||
* 清除数据
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun clear() {
|
||||
if (!isEmpty) {
|
||||
mData.clear()
|
||||
selectPosition = -1
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,157 +1,157 @@
|
|||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.CoreSwitchBean
|
||||
import com.xuexiang.xrouter.facade.service.SerializationService
|
||||
import com.xuexiang.xrouter.launcher.XRouter
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.slideback.SlideBack
|
||||
import io.github.inflationx.viewpump.ViewPumpContextWrapper
|
||||
|
||||
/**
|
||||
* 基础容器Activity
|
||||
*
|
||||
* @author XUE
|
||||
* @since 2019/3/22 11:21
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "UNCHECKED_CAST")
|
||||
open class BaseActivity<Binding : ViewBinding?> : XPageActivity() {
|
||||
/**
|
||||
* 获取Binding
|
||||
*
|
||||
* @return Binding
|
||||
*/
|
||||
/**
|
||||
* ViewBinding
|
||||
*/
|
||||
var binding: Binding? = null
|
||||
protected set
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
//注入字体
|
||||
super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase))
|
||||
}
|
||||
|
||||
override fun getCustomRootView(): View? {
|
||||
binding = viewBindingInflate(layoutInflater)
|
||||
return if (binding != null) binding!!.root else null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
initStatusBarStyle()
|
||||
super.onCreate(savedInstanceState)
|
||||
registerSlideBack()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建ViewBinding
|
||||
*
|
||||
* @param inflater inflater
|
||||
* @return ViewBinding
|
||||
*/
|
||||
protected open fun viewBindingInflate(inflater: LayoutInflater?): Binding? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化状态栏的样式
|
||||
*/
|
||||
protected open fun initStatusBarStyle() {}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @param addToBackStack 是否添加到栈中
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> openPage(clazz: Class<T>?, addToBackStack: Boolean): T {
|
||||
val page = CoreSwitchBean(clazz)
|
||||
.setAddToBackStack(addToBackStack)
|
||||
return openPage(page) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> openNewPage(clazz: Class<T>?): T {
|
||||
val page = CoreSwitchBean(clazz)
|
||||
.setNewActivity(true)
|
||||
return openPage(page) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> switchPage(clazz: Class<T>?): T {
|
||||
return openPage(clazz, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化对象
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
fun serializeObject(`object`: Any?): String {
|
||||
return XRouter.getInstance().navigation(SerializationService::class.java)
|
||||
.object2Json(`object`)
|
||||
}
|
||||
|
||||
override fun onRelease() {
|
||||
unregisterSlideBack()
|
||||
super.onRelease()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册侧滑回调
|
||||
*/
|
||||
protected fun registerSlideBack() {
|
||||
if (isSupportSlideBack) {
|
||||
SlideBack.with(this)
|
||||
.haveScroll(true)
|
||||
.edgeMode(if (ResUtils.isRtl()) SlideBack.EDGE_RIGHT else SlideBack.EDGE_LEFT)
|
||||
.callBack { popPage() }
|
||||
.register()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销侧滑回调
|
||||
*/
|
||||
protected fun unregisterSlideBack() {
|
||||
if (isSupportSlideBack) {
|
||||
SlideBack.unregister(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否支持侧滑返回
|
||||
*/
|
||||
protected open val isSupportSlideBack: Boolean
|
||||
get() {
|
||||
val page: CoreSwitchBean? = intent.getParcelableExtra(CoreSwitchBean.KEY_SWITCH_BEAN)
|
||||
return page == null || page.bundle == null || page.bundle.getBoolean(
|
||||
KEY_SUPPORT_SLIDE_BACK,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 是否支持侧滑返回
|
||||
*/
|
||||
const val KEY_SUPPORT_SLIDE_BACK = "key_support_slide_back"
|
||||
}
|
||||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.xuexiang.xpage.base.XPageActivity
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
import com.xuexiang.xpage.core.CoreSwitchBean
|
||||
import com.xuexiang.xrouter.facade.service.SerializationService
|
||||
import com.xuexiang.xrouter.launcher.XRouter
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.slideback.SlideBack
|
||||
import io.github.inflationx.viewpump.ViewPumpContextWrapper
|
||||
|
||||
/**
|
||||
* 基础容器Activity
|
||||
*
|
||||
* @author XUE
|
||||
* @since 2019/3/22 11:21
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "UNCHECKED_CAST", "DEPRECATION")
|
||||
open class BaseActivity<Binding : ViewBinding?> : XPageActivity() {
|
||||
/**
|
||||
* 获取Binding
|
||||
*
|
||||
* @return Binding
|
||||
*/
|
||||
/**
|
||||
* ViewBinding
|
||||
*/
|
||||
var binding: Binding? = null
|
||||
protected set
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
//注入字体
|
||||
super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase))
|
||||
}
|
||||
|
||||
override fun getCustomRootView(): View? {
|
||||
binding = viewBindingInflate(layoutInflater)
|
||||
return if (binding != null) binding!!.root else null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
initStatusBarStyle()
|
||||
super.onCreate(savedInstanceState)
|
||||
registerSlideBack()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建ViewBinding
|
||||
*
|
||||
* @param inflater inflater
|
||||
* @return ViewBinding
|
||||
*/
|
||||
protected open fun viewBindingInflate(inflater: LayoutInflater?): Binding? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化状态栏的样式
|
||||
*/
|
||||
protected open fun initStatusBarStyle() {}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @param addToBackStack 是否添加到栈中
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> openPage(clazz: Class<T>?, addToBackStack: Boolean): T {
|
||||
val page = CoreSwitchBean(clazz)
|
||||
.setAddToBackStack(addToBackStack)
|
||||
return openPage(page) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> openNewPage(clazz: Class<T>?): T {
|
||||
val page = CoreSwitchBean(clazz)
|
||||
.setNewActivity(true)
|
||||
return openPage(page) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
fun <T : XPageFragment?> switchPage(clazz: Class<T>?): T {
|
||||
return openPage(clazz, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化对象
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
fun serializeObject(`object`: Any?): String {
|
||||
return XRouter.getInstance().navigation(SerializationService::class.java)
|
||||
.object2Json(`object`)
|
||||
}
|
||||
|
||||
override fun onRelease() {
|
||||
unregisterSlideBack()
|
||||
super.onRelease()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册侧滑回调
|
||||
*/
|
||||
protected fun registerSlideBack() {
|
||||
if (isSupportSlideBack) {
|
||||
SlideBack.with(this)
|
||||
.haveScroll(true)
|
||||
.edgeMode(if (ResUtils.isRtl()) SlideBack.EDGE_RIGHT else SlideBack.EDGE_LEFT)
|
||||
.callBack { popPage() }
|
||||
.register()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销侧滑回调
|
||||
*/
|
||||
protected fun unregisterSlideBack() {
|
||||
if (isSupportSlideBack) {
|
||||
SlideBack.unregister(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否支持侧滑返回
|
||||
*/
|
||||
protected open val isSupportSlideBack: Boolean
|
||||
get() {
|
||||
val page: CoreSwitchBean? = intent.getParcelableExtra(CoreSwitchBean.KEY_SWITCH_BEAN)
|
||||
return page == null || page.bundle == null || page.bundle.getBoolean(
|
||||
KEY_SUPPORT_SLIDE_BACK,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 是否支持侧滑返回
|
||||
*/
|
||||
const val KEY_SUPPORT_SLIDE_BACK = "key_support_slide_back"
|
||||
}
|
||||
}
|
|
@ -1,87 +1,87 @@
|
|||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import com.umeng.analytics.MobclickAgent
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.base.XPageContainerListFragment
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.actionbar.TitleUtils
|
||||
|
||||
/**
|
||||
* 修改列表样式为主副标题显示
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2018/11/22 上午11:26
|
||||
*/
|
||||
@Suppress("unused")
|
||||
abstract class BaseContainerFragment : XPageContainerListFragment() {
|
||||
override fun initPage() {
|
||||
initTitle()
|
||||
initViews()
|
||||
initListeners()
|
||||
}
|
||||
|
||||
protected fun initTitle(): TitleBar {
|
||||
return TitleUtils.addTitleBarDynamic(
|
||||
rootView as ViewGroup,
|
||||
pageTitle
|
||||
) { popToBack() }
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
mSimpleData = initSimpleData(mSimpleData)
|
||||
val data: MutableList<Map<String?, String?>?> = ArrayList()
|
||||
for (content in mSimpleData) {
|
||||
val item: MutableMap<String?, String?> = HashMap()
|
||||
val index = content.indexOf("\n")
|
||||
if (index > 0) {
|
||||
item[SimpleListAdapter.KEY_TITLE] = content.subSequence(0, index).toString()
|
||||
item[SimpleListAdapter.KEY_SUB_TITLE] =
|
||||
content.subSequence(index + 1, content.length).toString()
|
||||
} else {
|
||||
item[SimpleListAdapter.KEY_TITLE] = content
|
||||
item[SimpleListAdapter.KEY_SUB_TITLE] = ""
|
||||
}
|
||||
data.add(item)
|
||||
}
|
||||
listView.adapter = SimpleListAdapter(context, data)
|
||||
initSimply()
|
||||
}
|
||||
|
||||
override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
onItemClick(view, position)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
private fun onItemClick(view: View, position: Int) {
|
||||
onItemClick(position)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
listView.onItemClickListener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
//屏幕旋转时刷新一下title
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val root = rootView as ViewGroup
|
||||
if (root.getChildAt(0) is TitleBar) {
|
||||
root.removeViewAt(0)
|
||||
initTitle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
MobclickAgent.onPageStart(pageName)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
MobclickAgent.onPageEnd(pageName)
|
||||
}
|
||||
package com.idormy.sms.forwarder.core
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import com.umeng.analytics.MobclickAgent
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.base.XPageContainerListFragment
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.actionbar.TitleUtils
|
||||
|
||||
/**
|
||||
* 修改列表样式为主副标题显示
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2018/11/22 上午11:26
|
||||
*/
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
abstract class BaseContainerFragment : XPageContainerListFragment() {
|
||||
override fun initPage() {
|
||||
initTitle()
|
||||
initViews()
|
||||
initListeners()
|
||||
}
|
||||
|
||||
protected fun initTitle(): TitleBar {
|
||||
return TitleUtils.addTitleBarDynamic(
|
||||
rootView as ViewGroup,
|
||||
pageTitle
|
||||
) { popToBack() }
|
||||
}
|
||||
|
||||
override fun initData() {
|
||||
mSimpleData = initSimpleData(mSimpleData)
|
||||
val data: MutableList<Map<String?, String?>?> = ArrayList()
|
||||
for (content in mSimpleData) {
|
||||
val item: MutableMap<String?, String?> = HashMap()
|
||||
val index = content.indexOf("\n")
|
||||
if (index > 0) {
|
||||
item[SimpleListAdapter.KEY_TITLE] = content.subSequence(0, index).toString()
|
||||
item[SimpleListAdapter.KEY_SUB_TITLE] =
|
||||
content.subSequence(index + 1, content.length).toString()
|
||||
} else {
|
||||
item[SimpleListAdapter.KEY_TITLE] = content
|
||||
item[SimpleListAdapter.KEY_SUB_TITLE] = ""
|
||||
}
|
||||
data.add(item)
|
||||
}
|
||||
listView.adapter = SimpleListAdapter(context, data)
|
||||
initSimply()
|
||||
}
|
||||
|
||||
override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
onItemClick(view, position)
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
private fun onItemClick(view: View, position: Int) {
|
||||
onItemClick(position)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
listView.onItemClickListener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
//屏幕旋转时刷新一下title
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val root = rootView as ViewGroup
|
||||
if (root.getChildAt(0) is TitleBar) {
|
||||
root.removeViewAt(0)
|
||||
initTitle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
MobclickAgent.onPageStart(pageName)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
MobclickAgent.onPageEnd(pageName)
|
||||
}
|
||||
}
|
|
@ -1,52 +1,52 @@
|
|||
package com.idormy.sms.forwarder.core.webview
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.webkit.WebView
|
||||
|
||||
/**
|
||||
* 修复 Android 5.0 & 5.1 打开 WebView 闪退问题:
|
||||
* 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class LollipopFixedWebView : WebView {
|
||||
constructor(context: Context) : super(getFixedContext(context))
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(getFixedContext(context), attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr
|
||||
)
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int,
|
||||
) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr, defStyleRes
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
privateBrowsing: Boolean,
|
||||
) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr, privateBrowsing
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getFixedContext(context: Context): Context {
|
||||
return if (isLollipopWebViewBug) {
|
||||
// Avoid crashing on Android 5 and 6 (API level 21 to 23)
|
||||
context.createConfigurationContext(Configuration())
|
||||
} else context
|
||||
}
|
||||
|
||||
private val isLollipopWebViewBug: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||||
}
|
||||
package com.idormy.sms.forwarder.core.webview
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.webkit.WebView
|
||||
|
||||
/**
|
||||
* 修复 Android 5.0 & 5.1 打开 WebView 闪退问题:
|
||||
* 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview
|
||||
*/
|
||||
@Suppress("unused", "DEPRECATION")
|
||||
class LollipopFixedWebView : WebView {
|
||||
constructor(context: Context) : super(getFixedContext(context))
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(getFixedContext(context), attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr
|
||||
)
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int,
|
||||
) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr, defStyleRes
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
privateBrowsing: Boolean,
|
||||
) : super(
|
||||
getFixedContext(context), attrs, defStyleAttr, privateBrowsing
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getFixedContext(context: Context): Context {
|
||||
return if (isLollipopWebViewBug) {
|
||||
// Avoid crashing on Android 5 and 6 (API level 21 to 23)
|
||||
context.createConfigurationContext(Configuration())
|
||||
} else context
|
||||
}
|
||||
|
||||
private val isLollipopWebViewBug: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||||
}
|
||||
}
|
|
@ -1,132 +1,133 @@
|
|||
package com.idormy.sms.forwarder.core.webview
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.webview.WebViewInterceptDialog.Companion.show
|
||||
import com.just.agentweb.core.client.MiddlewareWebClientBase
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 【网络请求、加载】
|
||||
* WebClient(WebViewClient 这个类主要帮助WebView处理各种通知、url加载,请求时间的)中间件
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 方法的执行顺序,例如下面用了7个中间件一个 WebViewClient
|
||||
*
|
||||
*
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 1
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 2
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 3
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 4
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 5
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 6
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 7
|
||||
* DefaultWebClient // 8
|
||||
* .setWebViewClient(mWebViewClient) // 9
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 典型的洋葱模型
|
||||
* 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 中断中间件的执行, 删除super.methodName(...) 这行即可
|
||||
*
|
||||
*
|
||||
* 这里主要是做去广告的工作
|
||||
*/
|
||||
open class MiddlewareWebViewClient : MiddlewareWebClientBase() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
Log.i(
|
||||
"Info",
|
||||
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.url.toString() + " c:" + count++
|
||||
)
|
||||
return if (shouldOverrideUrlLoadingByApp(view, request.url.toString())) {
|
||||
true
|
||||
} else super.shouldOverrideUrlLoading(view, request)
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
Log.i(
|
||||
"Info",
|
||||
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + count++
|
||||
)
|
||||
return if (shouldOverrideUrlLoadingByApp(view, url)) {
|
||||
true
|
||||
} else super.shouldOverrideUrlLoading(view, url)
|
||||
}
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||
val tUrl = url.lowercase(Locale.ROOT)
|
||||
return if (!hasAdUrl(tUrl)) {
|
||||
//正常加载
|
||||
super.shouldInterceptRequest(view, tUrl)
|
||||
} else {
|
||||
//含有广告资源屏蔽请求
|
||||
WebResourceResponse(null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView,
|
||||
request: WebResourceRequest,
|
||||
): WebResourceResponse? {
|
||||
val url = request.url.toString().lowercase(Locale.ROOT)
|
||||
return if (!hasAdUrl(url)) {
|
||||
//正常加载
|
||||
super.shouldInterceptRequest(view, request)
|
||||
} else {
|
||||
//含有广告资源屏蔽请求
|
||||
WebResourceResponse(null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据url的scheme处理跳转第三方app的业务,true代表拦截,false代表不拦截
|
||||
*/
|
||||
private fun shouldOverrideUrlLoadingByApp(webView: WebView, url: String): Boolean {
|
||||
if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) {
|
||||
//不拦截http, https, ftp的请求
|
||||
val uri = Uri.parse(url)
|
||||
if (uri != null && !(WebViewInterceptDialog.APP_LINK_HOST == uri.host && url.contains("xpage"))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
show(url)
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var count = 1
|
||||
|
||||
/**
|
||||
* 判断是否存在广告的链接
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private fun hasAdUrl(url: String): Boolean {
|
||||
val adUrls = ResUtils.getStringArray(R.array.adBlockUrl)
|
||||
for (adUrl in adUrls) {
|
||||
if (url.contains(adUrl)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.core.webview
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.webview.WebViewInterceptDialog.Companion.show
|
||||
import com.just.agentweb.core.client.MiddlewareWebClientBase
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 【网络请求、加载】
|
||||
* WebClient(WebViewClient 这个类主要帮助WebView处理各种通知、url加载,请求时间的)中间件
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 方法的执行顺序,例如下面用了7个中间件一个 WebViewClient
|
||||
*
|
||||
*
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 1
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 2
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 3
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 4
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 5
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 6
|
||||
* .useMiddlewareWebClient(getMiddlewareWebClient()) // 7
|
||||
* DefaultWebClient // 8
|
||||
* .setWebViewClient(mWebViewClient) // 9
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 典型的洋葱模型
|
||||
* 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* 中断中间件的执行, 删除super.methodName(...) 这行即可
|
||||
*
|
||||
*
|
||||
* 这里主要是做去广告的工作
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER", "DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
open class MiddlewareWebViewClient : MiddlewareWebClientBase() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
Log.i(
|
||||
"Info",
|
||||
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.url.toString() + " c:" + count++
|
||||
)
|
||||
return if (shouldOverrideUrlLoadingByApp(view, request.url.toString())) {
|
||||
true
|
||||
} else super.shouldOverrideUrlLoading(view, request)
|
||||
}
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
Log.i(
|
||||
"Info",
|
||||
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + count++
|
||||
)
|
||||
return if (shouldOverrideUrlLoadingByApp(view, url)) {
|
||||
true
|
||||
} else super.shouldOverrideUrlLoading(view, url)
|
||||
}
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||
val tUrl = url.lowercase(Locale.ROOT)
|
||||
return if (!hasAdUrl(tUrl)) {
|
||||
//正常加载
|
||||
super.shouldInterceptRequest(view, tUrl)
|
||||
} else {
|
||||
//含有广告资源屏蔽请求
|
||||
WebResourceResponse(null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
override fun shouldInterceptRequest(
|
||||
view: WebView,
|
||||
request: WebResourceRequest,
|
||||
): WebResourceResponse? {
|
||||
val url = request.url.toString().lowercase(Locale.ROOT)
|
||||
return if (!hasAdUrl(url)) {
|
||||
//正常加载
|
||||
super.shouldInterceptRequest(view, request)
|
||||
} else {
|
||||
//含有广告资源屏蔽请求
|
||||
WebResourceResponse(null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据url的scheme处理跳转第三方app的业务,true代表拦截,false代表不拦截
|
||||
*/
|
||||
private fun shouldOverrideUrlLoadingByApp(webView: WebView, url: String): Boolean {
|
||||
if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) {
|
||||
//不拦截http, https, ftp的请求
|
||||
val uri = Uri.parse(url)
|
||||
if (uri != null && !(WebViewInterceptDialog.APP_LINK_HOST == uri.host && url.contains("xpage"))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
show(url)
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var count = 1
|
||||
|
||||
/**
|
||||
* 判断是否存在广告的链接
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private fun hasAdUrl(url: String): Boolean {
|
||||
val adUrls = ResUtils.getStringArray(R.array.adBlockUrl)
|
||||
for (adUrl in adUrls) {
|
||||
if (url.contains(adUrl)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +1,88 @@
|
|||
package com.idormy.sms.forwarder.entity
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable.Creator
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.imageview.preview.enitity.IPreviewInfo
|
||||
|
||||
/**
|
||||
* 图片预览实体类
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2018/12/7 下午5:34
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class ImageInfo(
|
||||
//图片地址
|
||||
var mUrl: String,
|
||||
//记录坐标
|
||||
var mBounds: Rect? = null,
|
||||
var mVideoUrl: String? = null,
|
||||
var description: String? = ResUtils.getString(R.string.description),
|
||||
) : IPreviewInfo {
|
||||
|
||||
constructor(url: String) : this(mUrl = url)
|
||||
|
||||
constructor(url: String, bounds: Rect?) : this(mUrl = url, mBounds = bounds)
|
||||
|
||||
constructor(videoUrl: String?, url: String) : this(mUrl = url, mVideoUrl = videoUrl)
|
||||
|
||||
override fun getUrl(): String { //将你的图片地址字段返回
|
||||
return mUrl
|
||||
}
|
||||
|
||||
fun setUrl(url: String) {
|
||||
mUrl = url
|
||||
}
|
||||
|
||||
override fun getBounds(): Rect? { //将你的图片显示坐标字段返回
|
||||
return mBounds
|
||||
}
|
||||
|
||||
override fun getVideoUrl(): String? {
|
||||
return mVideoUrl
|
||||
}
|
||||
|
||||
fun setBounds(bounds: Rect) {
|
||||
mBounds = bounds
|
||||
}
|
||||
|
||||
fun setVideoUrl(videoUrl: String) {
|
||||
mVideoUrl = videoUrl
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeString(mUrl)
|
||||
dest.writeParcelable(mBounds, flags)
|
||||
dest.writeString(description)
|
||||
dest.writeString(mVideoUrl)
|
||||
}
|
||||
|
||||
constructor(`in`: Parcel) : this(
|
||||
mUrl = `in`.readString()!!,
|
||||
mBounds = `in`.readParcelable(Rect::class.java.classLoader),
|
||||
description = `in`.readString(),
|
||||
mVideoUrl = `in`.readString()
|
||||
)
|
||||
|
||||
companion object CREATOR : Creator<ImageInfo> {
|
||||
|
||||
fun newInstance(url: String, bounds: Rect): List<ImageInfo> {
|
||||
return listOf(ImageInfo(url, bounds))
|
||||
}
|
||||
|
||||
override fun createFromParcel(parcel: Parcel): ImageInfo {
|
||||
return ImageInfo(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ImageInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.entity
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable.Creator
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.widget.imageview.preview.enitity.IPreviewInfo
|
||||
|
||||
/**
|
||||
* 图片预览实体类
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2018/12/7 下午5:34
|
||||
*/
|
||||
@Suppress("unused", "DEPRECATION")
|
||||
data class ImageInfo(
|
||||
//图片地址
|
||||
var mUrl: String,
|
||||
//记录坐标
|
||||
var mBounds: Rect? = null,
|
||||
var mVideoUrl: String? = null,
|
||||
var description: String? = ResUtils.getString(R.string.description),
|
||||
) : IPreviewInfo {
|
||||
|
||||
constructor(url: String) : this(mUrl = url)
|
||||
|
||||
constructor(url: String, bounds: Rect?) : this(mUrl = url, mBounds = bounds)
|
||||
|
||||
constructor(videoUrl: String?, url: String) : this(mUrl = url, mVideoUrl = videoUrl)
|
||||
|
||||
override fun getUrl(): String { //将你的图片地址字段返回
|
||||
return mUrl
|
||||
}
|
||||
|
||||
fun setUrl(url: String) {
|
||||
mUrl = url
|
||||
}
|
||||
|
||||
override fun getBounds(): Rect? { //将你的图片显示坐标字段返回
|
||||
return mBounds
|
||||
}
|
||||
|
||||
override fun getVideoUrl(): String? {
|
||||
return mVideoUrl
|
||||
}
|
||||
|
||||
fun setBounds(bounds: Rect) {
|
||||
mBounds = bounds
|
||||
}
|
||||
|
||||
fun setVideoUrl(videoUrl: String) {
|
||||
mVideoUrl = videoUrl
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeString(mUrl)
|
||||
dest.writeParcelable(mBounds, flags)
|
||||
dest.writeString(description)
|
||||
dest.writeString(mVideoUrl)
|
||||
}
|
||||
|
||||
constructor(`in`: Parcel) : this(
|
||||
mUrl = `in`.readString()!!,
|
||||
mBounds = `in`.readParcelable(Rect::class.java.classLoader),
|
||||
description = `in`.readString(),
|
||||
mVideoUrl = `in`.readString()
|
||||
)
|
||||
|
||||
companion object CREATOR : Creator<ImageInfo> {
|
||||
|
||||
fun newInstance(url: String, bounds: Rect): List<ImageInfo> {
|
||||
return listOf(ImageInfo(url, bounds))
|
||||
}
|
||||
|
||||
override fun createFromParcel(parcel: Parcel): ImageInfo {
|
||||
return ImageInfo(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ImageInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.idormy.sms.forwarder.entity.setting
|
|||
import com.idormy.sms.forwarder.R
|
||||
import java.io.Serializable
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
data class FeishuAppSetting(
|
||||
var appId: String = "",
|
||||
val appSecret: String = "",
|
||||
|
|
|
@ -1,146 +1,146 @@
|
|||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.Frpc
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentFrpcEditBinding
|
||||
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.XToastUtils
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.ThemeUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
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")
|
||||
@Page(name = "Frp内网穿透·编辑配置")
|
||||
class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() {
|
||||
|
||||
var titleBar: TitleBar? = null
|
||||
var frpc: Frpc? = null
|
||||
private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) }
|
||||
|
||||
override fun initViews() {
|
||||
val pairCompleteMap: MutableMap<Char, Char> = HashMap()
|
||||
pairCompleteMap['{'] = '}'
|
||||
pairCompleteMap['['] = ']'
|
||||
pairCompleteMap['('] = ')'
|
||||
pairCompleteMap['<'] = '>'
|
||||
pairCompleteMap['"'] = '"'
|
||||
|
||||
binding!!.editText.enablePairComplete(true)
|
||||
binding!!.editText.enablePairCompleteCenterCursor(true)
|
||||
binding!!.editText.setPairCompleteMap(pairCompleteMap)
|
||||
|
||||
binding!!.editText.setEnableLineNumber(true)
|
||||
binding!!.editText.setLineNumberTextColor(Color.LTGRAY)
|
||||
binding!!.editText.setLineNumberTextSize(24f)
|
||||
binding!!.editText.textSize = 14f
|
||||
}
|
||||
|
||||
override fun viewBindingInflate(inflater: LayoutInflater, container: ViewGroup): FragmentFrpcEditBinding {
|
||||
return FragmentFrpcEditBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar!!.setTitle(R.string.menu_frpc)
|
||||
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_save) {
|
||||
@SuppressLint("ResourceAsColor")
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
if (frpc == null) return
|
||||
|
||||
val dialogFrpc = View.inflate(requireContext(), R.layout.dialog_frpc_save, null)
|
||||
val tvName = dialogFrpc.findViewById<MaterialEditText>(R.id.tv_name)
|
||||
val sbAutorun = dialogFrpc.findViewById<SwitchButton>(R.id.sb_autorun)
|
||||
|
||||
tvName.setText(frpc!!.name)
|
||||
sbAutorun.setCheckedImmediately(frpc!!.autorun == 1)
|
||||
|
||||
frpc!!.config = binding!!.editText.text.toString()
|
||||
|
||||
if (TextUtils.isEmpty(frpc!!.config)) {
|
||||
XToastUtils.error(R.string.tips_input_config_content)
|
||||
return
|
||||
}
|
||||
|
||||
MaterialDialog.Builder(context!!)
|
||||
.iconRes(R.drawable.ic_menu_frpc)
|
||||
.title(R.string.title_save_config)
|
||||
.customView(dialogFrpc, true)
|
||||
.cancelable(false)
|
||||
.autoDismiss(false)
|
||||
.neutralText(R.string.action_quit)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
dialog?.dismiss()
|
||||
activity?.onBackPressed()
|
||||
}
|
||||
.negativeText(R.string.action_back)
|
||||
.negativeColor(ResUtils.getColors(R.color.colorBlueGrey))
|
||||
.onNegative { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
dialog?.dismiss()
|
||||
}
|
||||
.positiveText(R.string.action_save)
|
||||
.onPositive { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
try {
|
||||
frpc!!.autorun = if (sbAutorun.isChecked) 1 else 0
|
||||
frpc!!.name = tvName.text.toString()
|
||||
if (TextUtils.isEmpty(frpc!!.name)) {
|
||||
XToastUtils.error(R.string.tips_input_config_name)
|
||||
return@onPositive
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(frpc!!.uid)) {
|
||||
viewModel.insert(frpc!!)
|
||||
} else {
|
||||
viewModel.update(frpc!!)
|
||||
}
|
||||
|
||||
dialog?.dismiss()
|
||||
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(frpc)
|
||||
XToastUtils.success(R.string.tipSaveSuccess)
|
||||
|
||||
activity?.onBackPressed()
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
})
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_restore) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.editText.setText(frpc?.config!!)
|
||||
XToastUtils.success(R.string.tipRestoreSuccess)
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
LiveEventBus.get(INTENT_FRPC_EDIT_FILE, Frpc::class.java).observeSticky(this) { value: Frpc ->
|
||||
frpc = value
|
||||
binding!!.editText.setText(value.config)
|
||||
titleBar!!.setTitle(if (TextUtils.isEmpty(value.name)) getString(R.string.noName) else value.name)
|
||||
}
|
||||
}
|
||||
|
||||
package com.idormy.sms.forwarder.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.database.entity.Frpc
|
||||
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentFrpcEditBinding
|
||||
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.XToastUtils
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.utils.ResUtils
|
||||
import com.xuexiang.xui.utils.ThemeUtils
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
import com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
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")
|
||||
@Page(name = "Frp内网穿透·编辑配置")
|
||||
class FrpcEditFragment : BaseFragment<FragmentFrpcEditBinding?>() {
|
||||
|
||||
var titleBar: TitleBar? = null
|
||||
var frpc: Frpc? = null
|
||||
private val viewModel by viewModels<FrpcViewModel> { BaseViewModelFactory(context) }
|
||||
|
||||
override fun initViews() {
|
||||
val pairCompleteMap: MutableMap<Char, Char> = HashMap()
|
||||
pairCompleteMap['{'] = '}'
|
||||
pairCompleteMap['['] = ']'
|
||||
pairCompleteMap['('] = ')'
|
||||
pairCompleteMap['<'] = '>'
|
||||
pairCompleteMap['"'] = '"'
|
||||
|
||||
binding!!.editText.enablePairComplete(true)
|
||||
binding!!.editText.enablePairCompleteCenterCursor(true)
|
||||
binding!!.editText.setPairCompleteMap(pairCompleteMap)
|
||||
|
||||
binding!!.editText.setEnableLineNumber(true)
|
||||
binding!!.editText.setLineNumberTextColor(Color.LTGRAY)
|
||||
binding!!.editText.setLineNumberTextSize(24f)
|
||||
binding!!.editText.textSize = 14f
|
||||
}
|
||||
|
||||
override fun viewBindingInflate(inflater: LayoutInflater, container: ViewGroup): FragmentFrpcEditBinding {
|
||||
return FragmentFrpcEditBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
titleBar = super.initTitle()!!.setImmersive(false)
|
||||
titleBar!!.setTitle(R.string.menu_frpc)
|
||||
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_save) {
|
||||
@SuppressLint("ResourceAsColor")
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
if (frpc == null) return
|
||||
|
||||
val dialogFrpc = View.inflate(requireContext(), R.layout.dialog_frpc_save, null)
|
||||
val tvName = dialogFrpc.findViewById<MaterialEditText>(R.id.tv_name)
|
||||
val sbAutorun = dialogFrpc.findViewById<SwitchButton>(R.id.sb_autorun)
|
||||
|
||||
tvName.setText(frpc!!.name)
|
||||
sbAutorun.setCheckedImmediately(frpc!!.autorun == 1)
|
||||
|
||||
frpc!!.config = binding!!.editText.text.toString()
|
||||
|
||||
if (TextUtils.isEmpty(frpc!!.config)) {
|
||||
XToastUtils.error(R.string.tips_input_config_content)
|
||||
return
|
||||
}
|
||||
|
||||
MaterialDialog.Builder(context!!)
|
||||
.iconRes(R.drawable.ic_menu_frpc)
|
||||
.title(R.string.title_save_config)
|
||||
.customView(dialogFrpc, true)
|
||||
.cancelable(false)
|
||||
.autoDismiss(false)
|
||||
.neutralText(R.string.action_quit)
|
||||
.neutralColor(ResUtils.getColors(R.color.red))
|
||||
.onNeutral { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
dialog?.dismiss()
|
||||
activity?.onBackPressed()
|
||||
}
|
||||
.negativeText(R.string.action_back)
|
||||
.negativeColor(ResUtils.getColors(R.color.colorBlueGrey))
|
||||
.onNegative { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
dialog?.dismiss()
|
||||
}
|
||||
.positiveText(R.string.action_save)
|
||||
.onPositive { dialog: MaterialDialog?, _: DialogAction? ->
|
||||
try {
|
||||
frpc!!.autorun = if (sbAutorun.isChecked) 1 else 0
|
||||
frpc!!.name = tvName.text.toString()
|
||||
if (TextUtils.isEmpty(frpc!!.name)) {
|
||||
XToastUtils.error(R.string.tips_input_config_name)
|
||||
return@onPositive
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(frpc!!.uid)) {
|
||||
viewModel.insert(frpc!!)
|
||||
} else {
|
||||
viewModel.update(frpc!!)
|
||||
}
|
||||
|
||||
dialog?.dismiss()
|
||||
LiveEventBus.get<Frpc>(EVENT_FRPC_UPDATE_CONFIG).post(frpc)
|
||||
XToastUtils.success(R.string.tipSaveSuccess)
|
||||
|
||||
activity?.onBackPressed()
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(e.message.toString())
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
})
|
||||
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_restore) {
|
||||
@SingleClick
|
||||
override fun performAction(view: View) {
|
||||
binding!!.editText.setText(frpc?.config!!)
|
||||
XToastUtils.success(R.string.tipRestoreSuccess)
|
||||
}
|
||||
})
|
||||
return titleBar
|
||||
}
|
||||
|
||||
override fun initListeners() {
|
||||
LiveEventBus.get(INTENT_FRPC_EDIT_FILE, Frpc::class.java).observeSticky(this) { value: Frpc ->
|
||||
frpc = value
|
||||
binding!!.editText.setText(value.config)
|
||||
titleBar!!.setTitle(if (TextUtils.isEmpty(value.name)) getString(R.string.noName) else value.name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -39,7 +39,7 @@ import io.reactivex.schedulers.Schedulers
|
|||
import java.util.*
|
||||
|
||||
@Page(name = "URL Scheme")
|
||||
@Suppress("PrivatePropertyName")
|
||||
@Suppress("PrivatePropertyName", "DEPRECATION")
|
||||
class UrlSchemeFragment : BaseFragment<FragmentSendersUrlSchemeBinding?>(), View.OnClickListener {
|
||||
|
||||
private val TAG: String = UrlSchemeFragment::class.java.simpleName
|
||||
|
|
|
@ -1,105 +1,106 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
import java.math.BigDecimal
|
||||
|
||||
class CacheUtils private constructor() {
|
||||
companion object {
|
||||
/**
|
||||
* 获取缓存大小
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 缓存大小
|
||||
*/
|
||||
fun getTotalCacheSize(context: Context): String {
|
||||
return try {
|
||||
var cacheSize = getFolderSize(context.cacheDir)
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
cacheSize += getFolderSize(context.externalCacheDir)
|
||||
}
|
||||
getFormatSize(cacheSize.toDouble())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
"0KB"
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 清理所有缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearAllCache(context: Context) {
|
||||
deleteDir(context.cacheDir)
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
deleteDir(context.externalCacheDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteDir(dir: File?): Boolean {
|
||||
if (dir != null && dir.isDirectory) {
|
||||
val children = dir.list()!!
|
||||
for (child in children) {
|
||||
val success = deleteDir(File(dir, child))
|
||||
if (!success) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(dir != null)
|
||||
return dir!!.delete()
|
||||
}
|
||||
|
||||
// 获取文件
|
||||
//Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
|
||||
//Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
|
||||
private fun getFolderSize(file: File?): Long {
|
||||
var size: Long = 0
|
||||
try {
|
||||
val fileList = file!!.listFiles()!!
|
||||
for (value in fileList) {
|
||||
// 如果下面还有文件
|
||||
size = if (value.isDirectory) {
|
||||
size + getFolderSize(value)
|
||||
} else {
|
||||
size + value.length()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化单位
|
||||
*
|
||||
* @param size 文件大小
|
||||
* @return 结果
|
||||
*/
|
||||
private fun getFormatSize(size: Double): String {
|
||||
val kiloByte = size / 1024
|
||||
if (kiloByte < 1) {
|
||||
return "0KB"
|
||||
}
|
||||
val megaByte = kiloByte / 1024
|
||||
if (megaByte < 1) {
|
||||
val result1 = BigDecimal(kiloByte.toString())
|
||||
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"
|
||||
}
|
||||
val gigaByte = megaByte / 1024
|
||||
if (gigaByte < 1) {
|
||||
val result2 = BigDecimal(megaByte.toString())
|
||||
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"
|
||||
}
|
||||
val teraBytes = gigaByte / 1024
|
||||
if (teraBytes < 1) {
|
||||
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"
|
||||
}
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class CacheUtils private constructor() {
|
||||
companion object {
|
||||
/**
|
||||
* 获取缓存大小
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 缓存大小
|
||||
*/
|
||||
fun getTotalCacheSize(context: Context): String {
|
||||
return try {
|
||||
var cacheSize = getFolderSize(context.cacheDir)
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
cacheSize += getFolderSize(context.externalCacheDir)
|
||||
}
|
||||
getFormatSize(cacheSize.toDouble())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
"0KB"
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 清理所有缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearAllCache(context: Context) {
|
||||
deleteDir(context.cacheDir)
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
deleteDir(context.externalCacheDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteDir(dir: File?): Boolean {
|
||||
if (dir != null && dir.isDirectory) {
|
||||
val children = dir.list()!!
|
||||
for (child in children) {
|
||||
val success = deleteDir(File(dir, child))
|
||||
if (!success) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(dir != null)
|
||||
return dir!!.delete()
|
||||
}
|
||||
|
||||
// 获取文件
|
||||
//Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
|
||||
//Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
|
||||
private fun getFolderSize(file: File?): Long {
|
||||
var size: Long = 0
|
||||
try {
|
||||
val fileList = file!!.listFiles()!!
|
||||
for (value in fileList) {
|
||||
// 如果下面还有文件
|
||||
size = if (value.isDirectory) {
|
||||
size + getFolderSize(value)
|
||||
} else {
|
||||
size + value.length()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化单位
|
||||
*
|
||||
* @param size 文件大小
|
||||
* @return 结果
|
||||
*/
|
||||
private fun getFormatSize(size: Double): String {
|
||||
val kiloByte = size / 1024
|
||||
if (kiloByte < 1) {
|
||||
return "0KB"
|
||||
}
|
||||
val megaByte = kiloByte / 1024
|
||||
if (megaByte < 1) {
|
||||
val result1 = BigDecimal(kiloByte.toString())
|
||||
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"
|
||||
}
|
||||
val gigaByte = megaByte / 1024
|
||||
if (gigaByte < 1) {
|
||||
val result2 = BigDecimal(megaByte.toString())
|
||||
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"
|
||||
}
|
||||
val teraBytes = gigaByte / 1024
|
||||
if (teraBytes < 1) {
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +1,52 @@
|
|||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.idormy.sms.forwarder.R
|
||||
|
||||
class KeepAliveUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
fun isIgnoreBatteryOptimization(activity: Activity): Boolean {
|
||||
//安卓6.0以下没有忽略电池优化
|
||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
true
|
||||
} else try {
|
||||
val powerManager: PowerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
powerManager.isIgnoringBatteryOptimizations(activity.packageName)
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun ignoreBatteryOptimization(activity: Activity) {
|
||||
try {
|
||||
if (isIgnoreBatteryOptimization(activity)) {
|
||||
return
|
||||
}
|
||||
@SuppressLint("BatteryLife") val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
|
||||
intent.data = Uri.parse("package:" + activity.packageName)
|
||||
val resolveInfo: ResolveInfo? = activity.packageManager.resolveActivity(intent, 0)
|
||||
if (resolveInfo != null) {
|
||||
activity.startActivity(intent)
|
||||
} else {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.idormy.sms.forwarder.R
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class KeepAliveUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
fun isIgnoreBatteryOptimization(activity: Activity): Boolean {
|
||||
//安卓6.0以下没有忽略电池优化
|
||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
true
|
||||
} else try {
|
||||
val powerManager: PowerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
powerManager.isIgnoringBatteryOptimizations(activity.packageName)
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun ignoreBatteryOptimization(activity: Activity) {
|
||||
try {
|
||||
if (isIgnoreBatteryOptimization(activity)) {
|
||||
return
|
||||
}
|
||||
@SuppressLint("BatteryLife") val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
|
||||
intent.data = Uri.parse("package:" + activity.packageName)
|
||||
val resolveInfo: ResolveInfo? = activity.packageManager.resolveActivity(intent, 0)
|
||||
if (resolveInfo != null) {
|
||||
activity.startActivity(intent)
|
||||
} else {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(R.string.unsupport)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ import com.xuexiang.xutil.resource.ResUtils
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Suppress("PropertyName", "DEPRECATION")
|
||||
class PhoneUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.io.*
|
|||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused", "UNCHECKED_CAST")
|
||||
class SharedPreference<T>(private val name: String, private val default: T) : ReadWriteProperty<Any?, T> {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
package com.idormy.sms.forwarder.utils.mail
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.mail.Transport
|
||||
|
||||
/**
|
||||
* 邮件发送器
|
||||
*/
|
||||
object MailSender {
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getInstance() = this
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*/
|
||||
fun sendMail(mail: Mail, onMailSendListener: OnMailSendListener? = null) {
|
||||
val send = GlobalScope.async(Dispatchers.IO) {
|
||||
Transport.send(MailUtil.createMailMessage(mail))
|
||||
}
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
runCatching {
|
||||
send.await()
|
||||
onMailSendListener?.onSuccess()
|
||||
}.onFailure {
|
||||
Log.e("MailSender", it.message.toString())
|
||||
onMailSendListener?.onError(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送回调
|
||||
*/
|
||||
interface OnMailSendListener {
|
||||
fun onSuccess()
|
||||
fun onError(e: Throwable)
|
||||
}
|
||||
package com.idormy.sms.forwarder.utils.mail
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.mail.Transport
|
||||
|
||||
/**
|
||||
* 邮件发送器
|
||||
*/
|
||||
object MailSender {
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getInstance() = this
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*/
|
||||
fun sendMail(mail: Mail, onMailSendListener: OnMailSendListener? = null) {
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
val send = GlobalScope.async(Dispatchers.IO) {
|
||||
Transport.send(MailUtil.createMailMessage(mail))
|
||||
}
|
||||
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
runCatching {
|
||||
send.await()
|
||||
onMailSendListener?.onSuccess()
|
||||
}.onFailure {
|
||||
Log.e("MailSender", it.message.toString())
|
||||
onMailSendListener?.onError(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送回调
|
||||
*/
|
||||
interface OnMailSendListener {
|
||||
fun onSuccess()
|
||||
fun onError(e: Throwable)
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ class UMengInit private constructor() {
|
|||
return
|
||||
}
|
||||
UMConfigure.setLogEnabled(false)
|
||||
UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel(application))
|
||||
UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel()) //getChannel(application)
|
||||
// 用户同意了隐私协议
|
||||
if (isAgreePrivacy) {
|
||||
realInit(application)
|
||||
|
@ -62,7 +62,7 @@ class UMengInit private constructor() {
|
|||
UMConfigure.init(
|
||||
application,
|
||||
BuildConfig.APP_ID_UMENG,
|
||||
getChannel(application),
|
||||
getChannel(), //getChannel(application)
|
||||
UMConfigure.DEVICE_TYPE_PHONE,
|
||||
""
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ class UMengInit private constructor() {
|
|||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private fun getChannel(context: Context?): String {
|
||||
private fun getChannel(): String { //context: Context?
|
||||
//return WalleChannelReader.getChannel(context!!, DEFAULT_CHANNEL_ID)
|
||||
return DEFAULT_CHANNEL_ID
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ buildscript {
|
|||
apply from: './versions.gradle'
|
||||
addRepos(repositories) //增加代码仓库
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:$versions.android_gradle_plugin"
|
||||
classpath deps.android_gradle_plugin
|
||||
classpath deps.android_maven_gradle_plugin
|
||||
//图片压缩
|
||||
classpath 'com.chenenyu:img-optimizer:1.2.0'
|
||||
|
@ -58,9 +58,9 @@ allprojects {
|
|||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
|
||||
FileTree tree = fileTree(dir: rootProject.getRootDir())
|
||||
tree.each { File file ->
|
||||
if (file.toString().contains("ajcore") && file.toString().endsWith(".txt")) {
|
||||
FileTree rootTree = fileTree(dir: rootDir)
|
||||
rootTree.each { File file ->
|
||||
if ((file.toString().contains("ajcore") || file.toString().contains("mapping") || file.toString().contains("seeds") || file.toString().contains("unused")) && file.toString().endsWith(".txt")) {
|
||||
delete file
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue