diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b0c483e2..30973e29 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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.** diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cb804787..88db84ad 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ tools:ignore="QueryAllPackagesPermission" /> + @@ -55,9 +56,6 @@ - - - : DelegateAdapter.Adapter { - /** - * 数据源 - */ - private val mData: MutableList = ArrayList() - /** - * @return 当前列表的选中项 - */ - /** - * 当前点击的条目 - */ - private var selectPosition = -1 - - constructor() - constructor(list: Collection?) { - if (list != null) { - mData.addAll(list) - } - } - - constructor(data: Array?) { - 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 - 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?): XDelegateAdapter<*, *> { - if (collection != null) { - mData.clear() - mData.addAll(collection) - selectPosition = -1 - notifyDataSetChanged() - } - return this - } - - /** - * 刷新列表数据 - * - * @param array - * @return - */ - @SuppressLint("NotifyDataSetChanged") - fun refresh(array: Array?): 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?): XDelegateAdapter<*, *> { - if (collection != null) { - mData.addAll(collection) - notifyDataSetChanged() - } - return this - } - - /** - * 加载更多 - * - * @param array - * @return - */ - @SuppressLint("NotifyDataSetChanged") - fun loadMore(array: Array?): 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 : DelegateAdapter.Adapter { + /** + * 数据源 + */ + private val mData: MutableList = ArrayList() + /** + * @return 当前列表的选中项 + */ + /** + * 当前点击的条目 + */ + private var selectPosition = -1 + + constructor() + constructor(list: Collection?) { + if (list != null) { + mData.addAll(list) + } + } + + constructor(data: Array?) { + 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 + 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?): XDelegateAdapter<*, *> { + if (collection != null) { + mData.clear() + mData.addAll(collection) + selectPosition = -1 + notifyDataSetChanged() + } + return this + } + + /** + * 刷新列表数据 + * + * @param array + * @return + */ + @SuppressLint("NotifyDataSetChanged") + fun refresh(array: Array?): 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?): XDelegateAdapter<*, *> { + if (collection != null) { + mData.addAll(collection) + notifyDataSetChanged() + } + return this + } + + /** + * 加载更多 + * + * @param array + * @return + */ + @SuppressLint("NotifyDataSetChanged") + fun loadMore(array: Array?): 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() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/core/BaseActivity.kt b/app/src/main/java/com/idormy/sms/forwarder/core/BaseActivity.kt index a36e7205..345e78f2 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/core/BaseActivity.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/core/BaseActivity.kt @@ -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 : 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 openPage(clazz: Class?, addToBackStack: Boolean): T { - val page = CoreSwitchBean(clazz) - .setAddToBackStack(addToBackStack) - return openPage(page) as T - } - - /** - * 打开fragment - * - * @return 打开的fragment对象 - */ - fun openNewPage(clazz: Class?): T { - val page = CoreSwitchBean(clazz) - .setNewActivity(true) - return openPage(page) as T - } - - /** - * 切换fragment - * - * @param clazz 页面类 - * @return 打开的fragment对象 - */ - fun switchPage(clazz: Class?): 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 : 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 openPage(clazz: Class?, addToBackStack: Boolean): T { + val page = CoreSwitchBean(clazz) + .setAddToBackStack(addToBackStack) + return openPage(page) as T + } + + /** + * 打开fragment + * + * @return 打开的fragment对象 + */ + fun openNewPage(clazz: Class?): T { + val page = CoreSwitchBean(clazz) + .setNewActivity(true) + return openPage(page) as T + } + + /** + * 切换fragment + * + * @param clazz 页面类 + * @return 打开的fragment对象 + */ + fun switchPage(clazz: Class?): 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" + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/core/BaseContainerFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/core/BaseContainerFragment.kt index 053eae9d..b10d014e 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/core/BaseContainerFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/core/BaseContainerFragment.kt @@ -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?> = ArrayList() - for (content in mSimpleData) { - val item: MutableMap = 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?> = ArrayList() + for (content in mSimpleData) { + val item: MutableMap = 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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/core/webview/LollipopFixedWebView.kt b/app/src/main/java/com/idormy/sms/forwarder/core/webview/LollipopFixedWebView.kt index 270a27d2..809c6b81 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/core/webview/LollipopFixedWebView.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/core/webview/LollipopFixedWebView.kt @@ -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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/core/webview/MiddlewareWebViewClient.kt b/app/src/main/java/com/idormy/sms/forwarder/core/webview/MiddlewareWebViewClient.kt index 0d6174b8..48aedb3a 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/core/webview/MiddlewareWebViewClient.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/core/webview/MiddlewareWebViewClient.kt @@ -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 + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/ImageInfo.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/ImageInfo.kt index 44f2dbbe..4cf18520 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/entity/ImageInfo.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/ImageInfo.kt @@ -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 { - - fun newInstance(url: String, bounds: Rect): List { - return listOf(ImageInfo(url, bounds)) - } - - override fun createFromParcel(parcel: Parcel): ImageInfo { - return ImageInfo(parcel) - } - - override fun newArray(size: Int): Array { - 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 { + + fun newInstance(url: String, bounds: Rect): List { + return listOf(ImageInfo(url, bounds)) + } + + override fun createFromParcel(parcel: Parcel): ImageInfo { + return ImageInfo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt index 6ae98b68..5463ee0b 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/setting/FeishuAppSetting.kt @@ -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 = "", diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/FrpcEditFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/FrpcEditFragment.kt index fd99300f..8e7ceb50 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/FrpcEditFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/FrpcEditFragment.kt @@ -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() { - - var titleBar: TitleBar? = null - var frpc: Frpc? = null - private val viewModel by viewModels { BaseViewModelFactory(context) } - - override fun initViews() { - val pairCompleteMap: MutableMap = 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(R.id.tv_name) - val sbAutorun = dialogFrpc.findViewById(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(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() { + + var titleBar: TitleBar? = null + var frpc: Frpc? = null + private val viewModel by viewModels { BaseViewModelFactory(context) } + + override fun initViews() { + val pairCompleteMap: MutableMap = 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(R.id.tv_name) + val sbAutorun = dialogFrpc.findViewById(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(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) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/UrlSchemeFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/UrlSchemeFragment.kt index 057d45e8..3830d5c6 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/UrlSchemeFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/senders/UrlSchemeFragment.kt @@ -39,7 +39,7 @@ import io.reactivex.schedulers.Schedulers import java.util.* @Page(name = "URL Scheme") -@Suppress("PrivatePropertyName") +@Suppress("PrivatePropertyName", "DEPRECATION") class UrlSchemeFragment : BaseFragment(), View.OnClickListener { private val TAG: String = UrlSchemeFragment::class.java.simpleName diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/CacheUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/CacheUtils.kt index 7789cb53..8fde334e 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/CacheUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/CacheUtils.kt @@ -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" + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/KeepAliveUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/KeepAliveUtils.kt index 1e6dddda..86bb8559 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/KeepAliveUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/KeepAliveUtils.kt @@ -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) + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt index 4bbdb696..dae9f4a0 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt @@ -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 { diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/SharedPreference.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/SharedPreference.kt index 16950adb..be21c1c8 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/SharedPreference.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/SharedPreference.kt @@ -7,6 +7,7 @@ import java.io.* import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty +@Suppress("unused", "UNCHECKED_CAST") class SharedPreference(private val name: String, private val default: T) : ReadWriteProperty { companion object { diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/mail/MailSender.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/mail/MailSender.kt index 6d1c475f..6c51ab65 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/mail/MailSender.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/mail/MailSender.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/sdkinit/UMengInit.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/sdkinit/UMengInit.kt index c15da807..52d90dc7 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/sdkinit/UMengInit.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/sdkinit/UMengInit.kt @@ -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 } diff --git a/build.gradle b/build.gradle index b790963e..d803f1a2 100644 --- a/build.gradle +++ b/build.gradle @@ -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 } }