整理:Code Review
parent
6571775a0f
commit
9c6f404190
@ -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,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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue