SmsForwarder V3.0.0
parent
4c1497490e
commit
0aa1abd826
@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
custom: https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427
|
@ -1,3 +0,0 @@
|
|||||||
[submodule "keystore"]
|
|
||||||
path = keystore
|
|
||||||
url = https://github.com/pppscn/keystore.git
|
|
@ -0,0 +1,25 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, pppscn
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1 +0,0 @@
|
|||||||
theme: jekyll-theme-cayman
|
|
@ -0,0 +1 @@
|
|||||||
|
/build
|
@ -0,0 +1,25 @@
|
|||||||
|
# 美团
|
||||||
|
meituan
|
||||||
|
# 三星
|
||||||
|
samsungapps
|
||||||
|
# 小米
|
||||||
|
xiaomi
|
||||||
|
# 91助手
|
||||||
|
91com
|
||||||
|
# 魅族
|
||||||
|
meizu
|
||||||
|
# 豌豆荚
|
||||||
|
wandou
|
||||||
|
# Google Play
|
||||||
|
googleplay
|
||||||
|
# 百度
|
||||||
|
baidu
|
||||||
|
# 360
|
||||||
|
360cn
|
||||||
|
# 应用宝
|
||||||
|
myapp
|
||||||
|
# 华为
|
||||||
|
huawei
|
||||||
|
# 蒲公英
|
||||||
|
pgyer
|
||||||
|
github
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<lint>
|
||||||
|
<issue id="IconDipSize">
|
||||||
|
<ignore path="src/main/res/mipmap-xhdpi/nav_banner.png" />
|
||||||
|
</issue>
|
||||||
|
<issue id="IconLauncherShape">
|
||||||
|
<ignore path="src/main/res/mipmap-hdpi/ic_launcher.png" />
|
||||||
|
</issue>
|
||||||
|
</lint>
|
@ -0,0 +1,10 @@
|
|||||||
|
apply plugin: 'walle'
|
||||||
|
|
||||||
|
walle {
|
||||||
|
// 指定渠道包的输出路径
|
||||||
|
apkOutputFolder = new File("${project.buildDir}/outputs/channels")
|
||||||
|
// 定制渠道包的APK的文件名称
|
||||||
|
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk'
|
||||||
|
// 渠道配置文件
|
||||||
|
channelFile = new File("${project.getProjectDir()}/channel")
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
<html>
|
||||||
|
Hello SmsForwarder!!!
|
||||||
|
</html>
|
@ -0,0 +1,3 @@
|
|||||||
|
<html>
|
||||||
|
Welcome to Main Page!!!
|
||||||
|
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 31 KiB |
@ -1,68 +0,0 @@
|
|||||||
package com.idormy.sms.forwarder;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class BaseActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
//启用menu
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
//menu点击事件
|
|
||||||
@SuppressLint("NonConstantResourceId")
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
Intent intent;
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.to_app_list:
|
|
||||||
intent = new Intent(this, AppListActivity.class);
|
|
||||||
break;
|
|
||||||
case R.id.to_clone:
|
|
||||||
intent = new Intent(this, CloneActivity.class);
|
|
||||||
break;
|
|
||||||
case R.id.to_about:
|
|
||||||
intent = new Intent(this, AboutActivity.class);
|
|
||||||
break;
|
|
||||||
case R.id.to_help:
|
|
||||||
intent = new Intent(this, HelpActivity.class);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//设置menu图标显示
|
|
||||||
@Override
|
|
||||||
public boolean onMenuOpened(int featureId, Menu menu) {
|
|
||||||
String TAG = "BaseActivity";
|
|
||||||
Log.d(TAG, "onMenuOpened, featureId=" + featureId);
|
|
||||||
if (menu != null) {
|
|
||||||
if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
|
|
||||||
try {
|
|
||||||
Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
|
|
||||||
m.setAccessible(true);
|
|
||||||
m.invoke(menu, true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Log.e(TAG, "onMenuOpened", e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onMenuOpened(featureId, menu);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package com.idormy.sms.forwarder;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.Window;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.idormy.sms.forwarder.utils.OnePixelManager;
|
|
||||||
|
|
||||||
public class OnePixelActivity extends Activity {
|
|
||||||
private static final String TAG = "OnePixelActivity";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Window window = getWindow();
|
|
||||||
window.setGravity(Gravity.START | Gravity.TOP);
|
|
||||||
WindowManager.LayoutParams params = window.getAttributes();
|
|
||||||
params.x = 0;
|
|
||||||
params.y = 0;
|
|
||||||
params.height = 1;
|
|
||||||
params.width = 1;
|
|
||||||
window.setAttributes(params);
|
|
||||||
OnePixelManager manager = new OnePixelManager();
|
|
||||||
manager.setKeepAliveReference(this);//将引用传给OnePixelManager
|
|
||||||
|
|
||||||
Log.e(TAG, "onCreate");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
Log.e(TAG, "onDestroy");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
Log.e(TAG, "onStop");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
Log.e(TAG, "onPause");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
Log.e(TAG, "onStart");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
Log.e(TAG, "onResume");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
|||||||
|
package com.idormy.sms.forwarder.activity
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.idormy.sms.forwarder.core.BaseActivity
|
||||||
|
import com.idormy.sms.forwarder.fragment.LoginFragment
|
||||||
|
import com.xuexiang.xui.utils.KeyboardUtils
|
||||||
|
import com.xuexiang.xui.utils.StatusBarUtils
|
||||||
|
import com.xuexiang.xutil.display.Colors
|
||||||
|
|
||||||
|
class LoginActivity : BaseActivity<ViewBinding?>() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
openPage(LoginFragment::class.java, intent.extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isSupportSlideBack: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun initStatusBarStyle() {
|
||||||
|
StatusBarUtils.initStatusBarStyle(this, false, Colors.WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
|
return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.idormy.sms.forwarder.activity
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.showPrivacyDialog
|
||||||
|
import com.idormy.sms.forwarder.utils.MMKVUtils
|
||||||
|
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.isAgreePrivacy
|
||||||
|
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.isFirstOpen
|
||||||
|
import com.xuexiang.xui.utils.KeyboardUtils
|
||||||
|
import com.xuexiang.xui.widget.activity.BaseSplashActivity
|
||||||
|
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
|
||||||
|
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
|
||||||
|
import com.xuexiang.xutil.app.ActivityUtils
|
||||||
|
import me.jessyan.autosize.internal.CancelAdapt
|
||||||
|
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
@SuppressLint("CustomSplashScreen")
|
||||||
|
class SplashActivity : BaseSplashActivity(), CancelAdapt {
|
||||||
|
|
||||||
|
val TAG: String = SplashActivity::class.java.simpleName
|
||||||
|
|
||||||
|
override fun getSplashDurationMillis(): Long {
|
||||||
|
return 500
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* activity启动后的初始化
|
||||||
|
*/
|
||||||
|
override fun onCreateActivity() {
|
||||||
|
initSplashView(R.drawable.xui_config_bg_splash)
|
||||||
|
startSplash(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动页结束后的动作
|
||||||
|
*/
|
||||||
|
override fun onSplashFinished() {
|
||||||
|
if (isFirstOpen) {
|
||||||
|
isFirstOpen = false
|
||||||
|
Log.d(TAG, "从SP迁移数据")
|
||||||
|
MMKVUtils.importSharedPreferences(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAgreePrivacy) {
|
||||||
|
loginOrGoMainPage()
|
||||||
|
} else {
|
||||||
|
showPrivacyDialog(this) { dialog: MaterialDialog, _: DialogAction? ->
|
||||||
|
dialog.dismiss()
|
||||||
|
isAgreePrivacy = true
|
||||||
|
loginOrGoMainPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loginOrGoMainPage() {
|
||||||
|
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SettingUtils.enableExcludeFromRecents) {
|
||||||
|
val intent = Intent(App.context, if (hasToken()) MainActivity::class.java else LoginActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||||
|
App.context.startActivity(intent)
|
||||||
|
} else {
|
||||||
|
if (hasToken()) {
|
||||||
|
ActivityUtils.startActivity(MainActivity::class.java)
|
||||||
|
} else {
|
||||||
|
ActivityUtils.startActivity(LoginActivity::class.java)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
ActivityUtils.startActivity(MainActivity::class.java)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单、返回键响应
|
||||||
|
*/
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
|
return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.adapter.base.broccoli.BroccoliRecyclerAdapter
|
||||||
|
import com.idormy.sms.forwarder.utils.PlaceholderHelper
|
||||||
|
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||||
|
import com.xuexiang.xui.widget.imageview.ImageLoader
|
||||||
|
import com.xuexiang.xutil.app.AppUtils
|
||||||
|
import com.xuexiang.xutil.app.AppUtils.AppInfo
|
||||||
|
import me.samlss.broccoli.Broccoli
|
||||||
|
|
||||||
|
class AppListAdapter(
|
||||||
|
/**
|
||||||
|
* 是否是加载占位
|
||||||
|
*/
|
||||||
|
private val mIsAnim: Boolean,
|
||||||
|
) : BroccoliRecyclerAdapter<AppInfo?>(AppUtils.getAppsInfo()) {
|
||||||
|
|
||||||
|
override fun getItemLayoutId(viewType: Int): Int {
|
||||||
|
return R.layout.adapter_app_list_item
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定控件
|
||||||
|
*
|
||||||
|
* @param holder
|
||||||
|
* @param model
|
||||||
|
* @param position
|
||||||
|
*/
|
||||||
|
override fun onBindData(holder: RecyclerViewHolder?, model: AppInfo?, position: Int) {
|
||||||
|
if (holder == null || model == null) return
|
||||||
|
val ivAppIcon = holder.findViewById<ImageView>(R.id.iv_app_icon)
|
||||||
|
ImageLoader.get().loadImage(ivAppIcon, model.icon)
|
||||||
|
holder.text(R.id.tv_app_name, model.name)
|
||||||
|
holder.text(R.id.tv_pkg_name, model.packageName)
|
||||||
|
holder.text(R.id.tv_ver_name, model.versionName)
|
||||||
|
//holder.text(R.id.tv_ver_code, model.versionCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定占位控件
|
||||||
|
*
|
||||||
|
* @param holder
|
||||||
|
* @param broccoli
|
||||||
|
*/
|
||||||
|
override fun onBindBroccoli(holder: RecyclerViewHolder?, broccoli: Broccoli?) {
|
||||||
|
if (holder == null || broccoli == null) return
|
||||||
|
if (mIsAnim) {
|
||||||
|
broccoli.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.iv_app_icon)))
|
||||||
|
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_app_name)))
|
||||||
|
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_pkg_name)))
|
||||||
|
.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_ver_name)))
|
||||||
|
//.addPlaceholder(PlaceholderHelper.getParameter(holder.findView(R.id.tv_ver_code)))
|
||||||
|
} else {
|
||||||
|
broccoli.addPlaceholders(
|
||||||
|
holder.findView(R.id.iv_app_icon),
|
||||||
|
holder.findView(R.id.tv_app_name),
|
||||||
|
holder.findView(R.id.tv_pkg_name),
|
||||||
|
holder.findView(R.id.tv_ver_name),
|
||||||
|
//holder.findView(R.id.tv_ver_code)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.adapter.FrpcPagingAdapter.MyViewHolder
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Frpc
|
||||||
|
import com.idormy.sms.forwarder.databinding.AdapterFrpcsCardViewListItemBinding
|
||||||
|
import com.xuexiang.xutil.resource.ResUtils.getColors
|
||||||
|
import frpclib.Frpclib
|
||||||
|
|
||||||
|
class FrpcPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Frpc, MyViewHolder>(diffCallback) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||||
|
val binding = AdapterFrpcsCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return MyViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
if (item != null) {
|
||||||
|
holder.binding.ivImage.setImageResource(R.drawable.ic_menu_frpc)
|
||||||
|
holder.binding.ivAutorun.setImageResource(item.autorunImageId)
|
||||||
|
holder.binding.tvName.text = item.name
|
||||||
|
|
||||||
|
if (item.connecting || Frpclib.isRunning(item.uid)) {
|
||||||
|
holder.binding.ivPlay.setImageResource(R.drawable.ic_stop)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStop)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.binding.ivPlay.setImageResource(R.drawable.ic_start)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
holder.binding.ivPlay.imageTintList = getColors(R.color.colorStart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
|
||||||
|
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
|
||||||
|
|
||||||
|
holder.binding.ivPlay.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivEdit.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivDelete.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyViewHolder(val binding: AdapterFrpcsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClicked(view: View?, item: Frpc)
|
||||||
|
fun onItemRemove(view: View?, id: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var diffCallback: DiffUtil.ItemCallback<Frpc> = object : DiffUtil.ItemCallback<Frpc>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
|
||||||
|
return oldItem.uid == newItem.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: Frpc, newItem: Frpc): Boolean {
|
||||||
|
return oldItem === newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.idormy.sms.forwarder.adapter.LogsPagingAdapter.MyViewHolder
|
||||||
|
import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Sender
|
||||||
|
import com.idormy.sms.forwarder.databinding.AdapterLogsCardViewListItemBinding
|
||||||
|
import com.xuexiang.xutil.data.DateUtils
|
||||||
|
|
||||||
|
class LogsPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<LogsAndRuleAndSender, MyViewHolder>(diffCallback) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||||
|
val binding = AdapterLogsCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return MyViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
if (item != null) {
|
||||||
|
holder.binding.tvFrom.text = item.logs.from
|
||||||
|
holder.binding.tvTime.text = DateUtils.getFriendlyTimeSpanByNow(item.logs.time)
|
||||||
|
holder.binding.tvContent.text = item.logs.content
|
||||||
|
holder.binding.ivSenderImage.setImageResource(Sender.getImageId(item.relation.sender.type))
|
||||||
|
holder.binding.ivStatusImage.setImageResource(item.logs.statusImageId)
|
||||||
|
holder.binding.ivSimImage.setImageResource(item.logs.simImageId)
|
||||||
|
|
||||||
|
holder.binding.cardView.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyViewHolder(val binding: AdapterLogsCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClicked(view: View?, item: LogsAndRuleAndSender)
|
||||||
|
fun onItemRemove(view: View?, id: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var diffCallback: DiffUtil.ItemCallback<LogsAndRuleAndSender> = object : DiffUtil.ItemCallback<LogsAndRuleAndSender>() {
|
||||||
|
override fun areItemsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||||
|
return oldItem.logs.id == newItem.logs.id
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: LogsAndRuleAndSender, newItem: LogsAndRuleAndSender): Boolean {
|
||||||
|
return oldItem.logs === newItem.logs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.adapter.RulePagingAdapter.MyViewHolder
|
||||||
|
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||||
|
import com.idormy.sms.forwarder.databinding.AdapterRulesCardViewListItemBinding
|
||||||
|
|
||||||
|
class RulePagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<RuleAndSender, MyViewHolder>(diffCallback) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||||
|
val binding = AdapterRulesCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return MyViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
if (item != null) {
|
||||||
|
holder.binding.ivRuleImage.setImageResource(item.rule.imageId)
|
||||||
|
holder.binding.ivRuleStatus.setImageResource(item.rule.statusImageId)
|
||||||
|
holder.binding.tvRuleMatch.text = item.rule.ruleMatch
|
||||||
|
holder.binding.ivSenderImage.setImageResource(item.sender.imageId)
|
||||||
|
holder.binding.ivSenderStatus.setImageResource(item.sender.statusImageId)
|
||||||
|
holder.binding.tvSenderName.text = item.sender.name
|
||||||
|
|
||||||
|
/*holder.binding.cardView.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}*/
|
||||||
|
holder.binding.ivCopy.setImageResource(R.drawable.ic_copy)
|
||||||
|
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
|
||||||
|
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
|
||||||
|
holder.binding.ivCopy.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivEdit.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivDelete.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyViewHolder(val binding: AdapterRulesCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClicked(view: View?, item: RuleAndSender)
|
||||||
|
fun onItemRemove(view: View?, id: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var diffCallback: DiffUtil.ItemCallback<RuleAndSender> = object : DiffUtil.ItemCallback<RuleAndSender>() {
|
||||||
|
override fun areItemsTheSame(oldItem: RuleAndSender, newItem: RuleAndSender): Boolean {
|
||||||
|
return oldItem.rule.id == newItem.rule.id
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: RuleAndSender, newItem: RuleAndSender): Boolean {
|
||||||
|
return oldItem.rule === newItem.rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.adapter.SenderPagingAdapter.MyViewHolder
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Sender
|
||||||
|
import com.idormy.sms.forwarder.databinding.AdapterSendersCardViewListItemBinding
|
||||||
|
|
||||||
|
class SenderPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Sender, MyViewHolder>(diffCallback) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||||
|
val binding = AdapterSendersCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return MyViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
if (item != null) {
|
||||||
|
holder.binding.ivImage.setImageResource(item.imageId)
|
||||||
|
holder.binding.ivStatus.setImageResource(item.statusImageId)
|
||||||
|
holder.binding.tvName.text = item.name
|
||||||
|
|
||||||
|
/*holder.binding.cardView.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}*/
|
||||||
|
holder.binding.ivCopy.setImageResource(R.drawable.ic_copy)
|
||||||
|
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
|
||||||
|
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
|
||||||
|
holder.binding.ivCopy.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivEdit.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
holder.binding.ivDelete.setOnClickListener { view: View? ->
|
||||||
|
itemClickListener.onItemClicked(view, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyViewHolder(val binding: AdapterSendersCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClicked(view: View?, item: Sender)
|
||||||
|
fun onItemRemove(view: View?, id: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var diffCallback: DiffUtil.ItemCallback<Sender> = object : DiffUtil.ItemCallback<Sender>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Sender, newItem: Sender): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiffUtilEquals")
|
||||||
|
override fun areContentsTheSame(oldItem: Sender, newItem: Sender): Boolean {
|
||||||
|
return oldItem === newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.xuexiang.xpage.model.PageInfo
|
||||||
|
import com.xuexiang.xui.adapter.recyclerview.BaseRecyclerAdapter
|
||||||
|
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||||
|
|
||||||
|
class WidgetItemAdapter(list: List<PageInfo>) : BaseRecyclerAdapter<PageInfo>(list) {
|
||||||
|
|
||||||
|
public override fun getItemLayoutId(viewType: Int): Int {
|
||||||
|
return R.layout.layout_widget_item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindData(holder: RecyclerViewHolder, position: Int, item: PageInfo) {
|
||||||
|
holder.text(R.id.item_name, item.name)
|
||||||
|
if (item.extra != 0) {
|
||||||
|
holder.image(R.id.item_icon, item.extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter.base.delegate
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用的DelegateAdapter适配器
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2020/3/20 12:44 AM
|
||||||
|
*/
|
||||||
|
abstract class BaseDelegateAdapter<T> : XDelegateAdapter<T, RecyclerViewHolder> {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(list: Collection<T>?) : super(list)
|
||||||
|
constructor(data: Array<T>?) : super(data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 适配的布局
|
||||||
|
*
|
||||||
|
* @param viewType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract fun getItemLayoutId(viewType: Int): Int
|
||||||
|
override fun getViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
|
||||||
|
return RecyclerViewHolder(inflateView(parent, getItemLayoutId(viewType)))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter.base.delegate
|
||||||
|
|
||||||
|
import com.alibaba.android.vlayout.LayoutHelper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简易DelegateAdapter适配器
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2020/3/20 12:55 AM
|
||||||
|
*/
|
||||||
|
abstract class SimpleDelegateAdapter<T> : BaseDelegateAdapter<T> {
|
||||||
|
private var mLayoutId: Int
|
||||||
|
private var mLayoutHelper: LayoutHelper
|
||||||
|
|
||||||
|
constructor(layoutId: Int, layoutHelper: LayoutHelper) : super() {
|
||||||
|
mLayoutId = layoutId
|
||||||
|
mLayoutHelper = layoutHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(layoutId: Int, layoutHelper: LayoutHelper, list: Collection<T>?) : super(list) {
|
||||||
|
mLayoutId = layoutId
|
||||||
|
mLayoutHelper = layoutHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(layoutId: Int, layoutHelper: LayoutHelper, data: Array<T>?) : super(data) {
|
||||||
|
mLayoutId = layoutId
|
||||||
|
mLayoutHelper = layoutHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemLayoutId(viewType: Int): Int {
|
||||||
|
return mLayoutId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateLayoutHelper(): LayoutHelper {
|
||||||
|
return mLayoutHelper
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter.spinner
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.xuexiang.xui.utils.CollectionUtils
|
||||||
|
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
|
||||||
|
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
|
||||||
|
|
||||||
|
@Suppress("unused", "NAME_SHADOWING", "SENSELESS_COMPARISON", "DEPRECATION")
|
||||||
|
class AppListSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
|
||||||
|
/**
|
||||||
|
* 选项的文字颜色
|
||||||
|
*/
|
||||||
|
private var mTextColor = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项的文字大小
|
||||||
|
*/
|
||||||
|
private var mTextSize = 0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 背景颜色
|
||||||
|
*/
|
||||||
|
private var mBackgroundSelector = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤关键词的选中颜色
|
||||||
|
*/
|
||||||
|
private var mFilterColor = "#F15C58"
|
||||||
|
private var mIsFilterKey = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param data 选项数据
|
||||||
|
*/
|
||||||
|
constructor(data: List<T>?) : super(data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param data 选项数据
|
||||||
|
*/
|
||||||
|
constructor(data: Array<T>?) : super(data)
|
||||||
|
|
||||||
|
override fun getEditSpinnerFilter(): EditSpinnerFilter {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
|
||||||
|
var convertView = convertView
|
||||||
|
val holder: ViewHolder
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
|
||||||
|
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
|
||||||
|
convertView.tag = holder
|
||||||
|
} else {
|
||||||
|
holder = convertView.tag as ViewHolder
|
||||||
|
}
|
||||||
|
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as AppListAdapterItem
|
||||||
|
holder.iconView.setImageDrawable(item.icon)
|
||||||
|
//holder.titleView.text = Html.fromHtml(item.toString())
|
||||||
|
holder.titleView.text = Html.fromHtml(getItem(position))
|
||||||
|
return convertView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFilter(keyword: String): Boolean {
|
||||||
|
mDisplayData.clear()
|
||||||
|
Log.d("AppListSpinnerAdapter", "keyword = $keyword")
|
||||||
|
Log.d("AppListSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
|
||||||
|
if (TextUtils.isEmpty(keyword)) {
|
||||||
|
initDisplayData(mDataSource)
|
||||||
|
for (i in mIndexs.indices) {
|
||||||
|
mIndexs[i] = i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
for (i in mDataSource.indices) {
|
||||||
|
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
|
||||||
|
mIndexs[mDisplayData.size] = i
|
||||||
|
if (mIsFilterKey) {
|
||||||
|
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
|
||||||
|
} else {
|
||||||
|
mDisplayData.add(getDataSourceString(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("AppListSpinnerAdapter", "mDisplayData = $mDisplayData")
|
||||||
|
notifyDataSetChanged()
|
||||||
|
return mDisplayData.size > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTextColor(@ColorInt textColor: Int): AppListSpinnerAdapter<*> {
|
||||||
|
mTextColor = textColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTextSize(textSize: Float): AppListSpinnerAdapter<*> {
|
||||||
|
mTextSize = textSize
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): AppListSpinnerAdapter<*> {
|
||||||
|
mBackgroundSelector = backgroundSelector
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFilterColor(filterColor: String): AppListSpinnerAdapter<*> {
|
||||||
|
mFilterColor = filterColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIsFilterKey(isFilterKey: Boolean): AppListSpinnerAdapter<*> {
|
||||||
|
mIsFilterKey = isFilterKey
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
|
||||||
|
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
|
||||||
|
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
|
||||||
|
val titleView: TextView = convertView.findViewById(R.id.tv_title)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (textColor > 0) titleView.setTextColor(textColor)
|
||||||
|
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
|
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
val config = convertView.resources.configuration
|
||||||
|
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||||
|
titleView.textDirection = View.TEXT_DIRECTION_RTL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
package com.idormy.sms.forwarder.adapter.spinner
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.STATUS_OFF
|
||||||
|
import com.xuexiang.xui.utils.CollectionUtils
|
||||||
|
import com.xuexiang.xui.utils.ResUtils
|
||||||
|
import com.xuexiang.xui.widget.spinner.editspinner.BaseEditSpinnerAdapter
|
||||||
|
import com.xuexiang.xui.widget.spinner.editspinner.EditSpinnerFilter
|
||||||
|
|
||||||
|
@Suppress("unused", "NAME_SHADOWING", "SENSELESS_COMPARISON", "DEPRECATION")
|
||||||
|
class SenderSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
|
||||||
|
/**
|
||||||
|
* 选项的文字颜色
|
||||||
|
*/
|
||||||
|
private var mTextColor = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项的文字大小
|
||||||
|
*/
|
||||||
|
private var mTextSize = 0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 背景颜色
|
||||||
|
*/
|
||||||
|
private var mBackgroundSelector = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤关键词的选中颜色
|
||||||
|
*/
|
||||||
|
private var mFilterColor = "#F15C58"
|
||||||
|
private var mIsFilterKey = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param data 选项数据
|
||||||
|
*/
|
||||||
|
constructor(data: List<T>?) : super(data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param data 选项数据
|
||||||
|
*/
|
||||||
|
constructor(data: Array<T>?) : super(data)
|
||||||
|
|
||||||
|
override fun getEditSpinnerFilter(): EditSpinnerFilter {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
|
||||||
|
var convertView = convertView
|
||||||
|
val holder: ViewHolder
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_spinner_with_icon, parent, false)
|
||||||
|
holder = ViewHolder(convertView, mTextColor, mTextSize, mBackgroundSelector)
|
||||||
|
convertView.tag = holder
|
||||||
|
} else {
|
||||||
|
holder = convertView.tag as ViewHolder
|
||||||
|
}
|
||||||
|
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderAdapterItem
|
||||||
|
holder.iconView.setImageDrawable(item.icon)
|
||||||
|
holder.statusView.setImageDrawable(
|
||||||
|
ResUtils.getDrawable(
|
||||||
|
when (item.status) {
|
||||||
|
STATUS_OFF -> R.drawable.icon_off
|
||||||
|
else -> R.drawable.icon_on
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
//holder.titleView.text = Html.fromHtml(item.toString())
|
||||||
|
holder.titleView.text = Html.fromHtml(getItem(position))
|
||||||
|
return convertView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFilter(keyword: String): Boolean {
|
||||||
|
mDisplayData.clear()
|
||||||
|
Log.d("SenderSpinnerAdapter", "keyword = $keyword")
|
||||||
|
Log.d("SenderSpinnerAdapter", "mIndexs.indices = ${mIndexs.indices}")
|
||||||
|
if (TextUtils.isEmpty(keyword)) {
|
||||||
|
initDisplayData(mDataSource)
|
||||||
|
for (i in mIndexs.indices) {
|
||||||
|
mIndexs[i] = i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
for (i in mDataSource.indices) {
|
||||||
|
if (getDataSourceString(i).contains(keyword, ignoreCase = true)) {
|
||||||
|
mIndexs[mDisplayData.size] = i
|
||||||
|
if (mIsFilterKey) {
|
||||||
|
mDisplayData.add(getDataSourceString(i).replaceFirst(keyword.toRegex(), "<font color=\"$mFilterColor\">$keyword</font>"))
|
||||||
|
} else {
|
||||||
|
mDisplayData.add(getDataSourceString(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("SenderSpinnerAdapter", "mDisplayData = $mDisplayData")
|
||||||
|
notifyDataSetChanged()
|
||||||
|
return mDisplayData.size > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTextColor(@ColorInt textColor: Int): SenderSpinnerAdapter<*> {
|
||||||
|
mTextColor = textColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTextSize(textSize: Float): SenderSpinnerAdapter<*> {
|
||||||
|
mTextSize = textSize
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackgroundSelector(@DrawableRes backgroundSelector: Int): SenderSpinnerAdapter<*> {
|
||||||
|
mBackgroundSelector = backgroundSelector
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFilterColor(filterColor: String): SenderSpinnerAdapter<*> {
|
||||||
|
mFilterColor = filterColor
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIsFilterKey(isFilterKey: Boolean): SenderSpinnerAdapter<*> {
|
||||||
|
mIsFilterKey = isFilterKey
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
private class ViewHolder(convertView: View, @ColorInt textColor: Int, textSize: Float, @DrawableRes backgroundSelector: Int) {
|
||||||
|
val iconView: ImageView = convertView.findViewById(R.id.iv_icon)
|
||||||
|
val statusView: ImageView = convertView.findViewById(R.id.iv_status)
|
||||||
|
val titleView: TextView = convertView.findViewById(R.id.tv_title)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (textColor > 0) titleView.setTextColor(textColor)
|
||||||
|
if (textSize > 0F) titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
|
if (backgroundSelector != 0) titleView.setBackgroundResource(backgroundSelector)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
val config = convertView.resources.configuration
|
||||||
|
if (config.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||||
|
titleView.textDirection = View.TEXT_DIRECTION_RTL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +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"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +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)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,286 @@
|
|||||||
|
package com.idormy.sms.forwarder.core
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.umeng.analytics.MobclickAgent
|
||||||
|
import com.xuexiang.xpage.base.XPageActivity
|
||||||
|
import com.xuexiang.xpage.base.XPageFragment
|
||||||
|
import com.xuexiang.xpage.base.XPageSimpleListFragment
|
||||||
|
import com.xuexiang.xpage.core.PageOption
|
||||||
|
import com.xuexiang.xpage.enums.CoreAnim
|
||||||
|
import com.xuexiang.xrouter.facade.service.SerializationService
|
||||||
|
import com.xuexiang.xrouter.launcher.XRouter
|
||||||
|
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||||
|
import com.xuexiang.xui.widget.actionbar.TitleUtils
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2018/12/29 下午12:41
|
||||||
|
*/
|
||||||
|
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
|
abstract class BaseSimpleListFragment : XPageSimpleListFragment() {
|
||||||
|
override fun initPage() {
|
||||||
|
initTitle()
|
||||||
|
initViews()
|
||||||
|
initListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun initTitle(): TitleBar {
|
||||||
|
return TitleUtils.addTitleBarDynamic(
|
||||||
|
rootView as ViewGroup,
|
||||||
|
pageTitle
|
||||||
|
) { popToBack() }
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
//==============================页面跳转api===================================//
|
||||||
|
/**
|
||||||
|
* 打开一个新的页面【建议只在主tab页使用】
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openNewPage(clazz: Class<T>?): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.setNewActivity(true)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开一个新的页面【建议只在主tab页使用】
|
||||||
|
*
|
||||||
|
* @param pageName 页面名
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openNewPage(pageName: String?): Fragment? {
|
||||||
|
return PageOption(pageName)
|
||||||
|
.setAnim(CoreAnim.slide)
|
||||||
|
.setNewActivity(true)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开一个新的页面【建议只在主tab页使用】
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param containActivityClazz 页面容器
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openNewPage(
|
||||||
|
clazz: Class<T>?,
|
||||||
|
containActivityClazz: Class<out XPageActivity?>,
|
||||||
|
): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.setNewActivity(true)
|
||||||
|
.setContainActivityClazz(containActivityClazz)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开一个新的页面【建议只在主tab页使用】
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openNewPage(clazz: Class<T>?, key: String?, value: Any?): Fragment? {
|
||||||
|
val option = PageOption(clazz).setNewActivity(true)
|
||||||
|
return openPage(option, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openPage(option: PageOption, key: String?, value: Any?): Fragment? {
|
||||||
|
when (value) {
|
||||||
|
is Int -> {
|
||||||
|
option.putInt(key, value)
|
||||||
|
}
|
||||||
|
is Float -> {
|
||||||
|
option.putFloat(key, value)
|
||||||
|
}
|
||||||
|
is String -> {
|
||||||
|
option.putString(key, value)
|
||||||
|
}
|
||||||
|
is Boolean -> {
|
||||||
|
option.putBoolean(key, value)
|
||||||
|
}
|
||||||
|
is Long -> {
|
||||||
|
option.putLong(key, value)
|
||||||
|
}
|
||||||
|
is Double -> {
|
||||||
|
option.putDouble(key, value)
|
||||||
|
}
|
||||||
|
is Parcelable -> {
|
||||||
|
option.putParcelable(key, value)
|
||||||
|
}
|
||||||
|
is Serializable -> {
|
||||||
|
option.putSerializable(key, value)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
option.putString(key, serializeObject(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return option.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param addToBackStack 是否加入回退栈
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPage(
|
||||||
|
clazz: Class<T>?,
|
||||||
|
addToBackStack: Boolean,
|
||||||
|
key: String?,
|
||||||
|
value: String?,
|
||||||
|
): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.setAddToBackStack(addToBackStack)
|
||||||
|
.putString(key, value)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPage(clazz: Class<T>?, key: String?, value: Any?): Fragment? {
|
||||||
|
return openPage(clazz, true, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param addToBackStack 是否加入回退栈
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPage(
|
||||||
|
clazz: Class<T>?,
|
||||||
|
addToBackStack: Boolean,
|
||||||
|
key: String?,
|
||||||
|
value: Any?,
|
||||||
|
): Fragment? {
|
||||||
|
val option = PageOption(clazz).setAddToBackStack(addToBackStack)
|
||||||
|
return openPage(option, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPage(clazz: Class<T>?, key: String?, value: String?): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.putString(key, value)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面,需要结果返回
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param requestCode 请求码
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPageForResult(
|
||||||
|
clazz: Class<T>?,
|
||||||
|
key: String?,
|
||||||
|
value: Any?,
|
||||||
|
requestCode: Int,
|
||||||
|
): Fragment? {
|
||||||
|
val option = PageOption(clazz).setRequestCode(requestCode)
|
||||||
|
return openPage(option, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面,需要结果返回
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param key 入参的键
|
||||||
|
* @param value 入参的值
|
||||||
|
* @param requestCode 请求码
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPageForResult(
|
||||||
|
clazz: Class<T>?,
|
||||||
|
key: String?,
|
||||||
|
value: String?,
|
||||||
|
requestCode: Int,
|
||||||
|
): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.setRequestCode(requestCode)
|
||||||
|
.putString(key, value)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开页面,需要结果返回
|
||||||
|
*
|
||||||
|
* @param clazz 页面的类
|
||||||
|
* @param requestCode 请求码
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
</T> */
|
||||||
|
fun <T : XPageFragment?> openPageForResult(clazz: Class<T>?, requestCode: Int): Fragment? {
|
||||||
|
return PageOption(clazz)
|
||||||
|
.setRequestCode(requestCode)
|
||||||
|
.open(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化对象
|
||||||
|
*
|
||||||
|
* @param object 需要序列化的对象
|
||||||
|
* @return 序列化结果
|
||||||
|
*/
|
||||||
|
fun serializeObject(`object`: Any?): String {
|
||||||
|
return XRouter.getInstance().navigation(SerializationService::class.java)
|
||||||
|
.object2Json(`object`)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.idormy.sms.forwarder.core
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.work.Configuration
|
||||||
|
import com.idormy.sms.forwarder.App
|
||||||
|
import com.idormy.sms.forwarder.BuildConfig
|
||||||
|
import com.idormy.sms.forwarder.database.repository.FrpcRepository
|
||||||
|
import com.idormy.sms.forwarder.database.repository.LogsRepository
|
||||||
|
import com.idormy.sms.forwarder.database.repository.RuleRepository
|
||||||
|
import com.idormy.sms.forwarder.database.repository.SenderRepository
|
||||||
|
import com.idormy.sms.forwarder.service.ForegroundService
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object Core : Configuration.Provider {
|
||||||
|
lateinit var app: Application
|
||||||
|
val frpc: FrpcRepository by lazy { (app as App).frpcRepository }
|
||||||
|
val logs: LogsRepository by lazy { (app as App).logsRepository }
|
||||||
|
val rule: RuleRepository by lazy { (app as App).ruleRepository }
|
||||||
|
val sender: SenderRepository by lazy { (app as App).senderRepository }
|
||||||
|
/*
|
||||||
|
val telephonyManager: TelephonyManager by lazy { app.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager }
|
||||||
|
val smsManager: SmsManager by lazy { app.getSystemService(SmsManager::class.java) }
|
||||||
|
val subscriptionManager: SubscriptionManager by lazy {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||||
|
SubscriptionManager.from(app)
|
||||||
|
} else {
|
||||||
|
app.getSystemService(SubscriptionManager::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val user by lazy { app.getSystemService<UserManager>()!! }*/
|
||||||
|
|
||||||
|
|
||||||
|
/*val directBootAware: Boolean get() = directBootSupported && dataStore.canToggleLocked
|
||||||
|
val directBootSupported by lazy {
|
||||||
|
Build.VERSION.SDK_INT >= 24 && try {
|
||||||
|
app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus ==
|
||||||
|
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
|
||||||
|
} catch (_: RuntimeException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fun init(app: Application) {
|
||||||
|
this.app = app
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWorkManagerConfiguration(): Configuration {
|
||||||
|
return Configuration.Builder().apply {
|
||||||
|
setDefaultProcessName(app.packageName + ":bg")
|
||||||
|
setMinimumLoggingLevel(if (BuildConfig.DEBUG) Log.VERBOSE else Log.INFO)
|
||||||
|
setExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||||
|
setTaskExecutor { (app as App).applicationScope.launch { it.run() } }
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startService() = ContextCompat.startForegroundService(app, Intent(app, ForegroundService::class.java))
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.idormy.sms.forwarder.core
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.xuexiang.xui.adapter.listview.BaseListAdapter
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主副标题显示适配器
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2018/12/19 上午12:19
|
||||||
|
*/
|
||||||
|
class SimpleListAdapter(context: Context?, data: List<Map<String?, String?>?>?) :
|
||||||
|
BaseListAdapter<Map<String?, String?>, SimpleListAdapter.ViewHolder>(context, data) {
|
||||||
|
override fun newViewHolder(convertView: View): ViewHolder {
|
||||||
|
val holder = ViewHolder()
|
||||||
|
holder.mTvTitle = convertView.findViewById(R.id.tv_title)
|
||||||
|
holder.mTvSubTitle = convertView.findViewById(R.id.tv_sub_title)
|
||||||
|
return holder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLayoutId(): Int {
|
||||||
|
return R.layout.adapter_item_simple_list
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(holder: ViewHolder, item: Map<String?, String?>, position: Int) {
|
||||||
|
holder.mTvTitle!!.text =
|
||||||
|
item[KEY_TITLE]
|
||||||
|
if (!StringUtils.isEmpty(item[KEY_SUB_TITLE])) {
|
||||||
|
holder.mTvSubTitle!!.text =
|
||||||
|
item[KEY_SUB_TITLE]
|
||||||
|
holder.mTvSubTitle!!.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.mTvSubTitle!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder {
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
var mTvTitle: TextView? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 副标题
|
||||||
|
*/
|
||||||
|
var mTvSubTitle: TextView? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY_TITLE = "key_title"
|
||||||
|
const val KEY_SUB_TITLE = "key_sub_title"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.idormy.sms.forwarder.core
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xrouter.annotation.AutoWired
|
||||||
|
import com.xuexiang.xrouter.annotation.Router
|
||||||
|
import com.xuexiang.xrouter.launcher.XRouter
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://xuexiangjys.club/xpage/transfer?pageName=xxxxx&....
|
||||||
|
* applink的中转
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-07-06 9:37
|
||||||
|
*/
|
||||||
|
@Router(path = "/xpage/transfer")
|
||||||
|
class XPageTransferActivity : BaseActivity<ViewBinding?>() {
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@AutoWired(name = "pageName")
|
||||||
|
var pageName: Nothing? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
XRouter.getInstance().inject(this)
|
||||||
|
if (!StringUtils.isEmpty(pageName)) {
|
||||||
|
if (openPage(pageName, intent.extras) == null) {
|
||||||
|
XToastUtils.error(getString(R.string.page_not_found))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XToastUtils.error(getString(R.string.page_not_found))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.api
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.core.http.entity.TipInfo
|
||||||
|
import com.xuexiang.xhttp2.model.ApiResult
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import retrofit2.http.GET
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2021/1/9 7:01 PM
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
class ApiService {
|
||||||
|
/**
|
||||||
|
* 使用的是retrofit的接口定义
|
||||||
|
*/
|
||||||
|
interface IGetService {
|
||||||
|
/**
|
||||||
|
* 获得小贴士
|
||||||
|
*/
|
||||||
|
@get:GET("/pp/SmsForwarder.wiki/raw/master/tips.json")
|
||||||
|
val tips: Observable<ApiResult<List<TipInfo?>?>>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.callback
|
||||||
|
|
||||||
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
import com.xuexiang.xhttp2.model.XHttpRequest
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
import com.xuexiang.xutil.common.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不带错误提示的网络请求回调
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:02
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class NoTipCallBack<T> : SimpleCallBack<T> {
|
||||||
|
/**
|
||||||
|
* 记录一下请求的url,确定出错的请求是哪个请求
|
||||||
|
*/
|
||||||
|
private var mUrl: String? = null
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
constructor(req: XHttpRequest) : this(req.url)
|
||||||
|
constructor(url: String?) {
|
||||||
|
mUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: ApiException) {
|
||||||
|
if (!StringUtils.isEmpty(mUrl)) {
|
||||||
|
Logger.e("网络请求的url:$mUrl", e)
|
||||||
|
} else {
|
||||||
|
Logger.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.callback
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xhttp2.callback.SimpleCallBack
|
||||||
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
import com.xuexiang.xhttp2.model.XHttpRequest
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
import com.xuexiang.xutil.common.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带错误toast提示的网络请求回调
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:02
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class TipCallBack<T> : SimpleCallBack<T> {
|
||||||
|
/**
|
||||||
|
* 记录一下请求的url,确定出错的请求是哪个请求
|
||||||
|
*/
|
||||||
|
private var mUrl: String? = null
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
constructor(req: XHttpRequest) : this(req.url)
|
||||||
|
constructor(url: String?) {
|
||||||
|
mUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: ApiException) {
|
||||||
|
XToastUtils.error(e)
|
||||||
|
if (!StringUtils.isEmpty(mUrl)) {
|
||||||
|
Logger.e("网络请求的url:$mUrl", e)
|
||||||
|
} else {
|
||||||
|
Logger.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.callback
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.core.BaseFragment
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xhttp2.callback.ProgressLoadingCallBack
|
||||||
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
import com.xuexiang.xhttp2.model.XHttpRequest
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
import com.xuexiang.xutil.common.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带错误toast提示和加载进度条的网络请求回调
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:16
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class TipProgressLoadingCallBack<T> : ProgressLoadingCallBack<T> {
|
||||||
|
/**
|
||||||
|
* 记录一下请求的url,确定出错的请求是哪个请求
|
||||||
|
*/
|
||||||
|
private var mUrl: String? = null
|
||||||
|
|
||||||
|
constructor(fragment: BaseFragment<*>) : super(fragment.progressLoader)
|
||||||
|
constructor(iProgressLoader: IProgressLoader?) : super(iProgressLoader)
|
||||||
|
constructor(req: XHttpRequest, iProgressLoader: IProgressLoader?) : this(
|
||||||
|
req.url,
|
||||||
|
iProgressLoader
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(url: String?, iProgressLoader: IProgressLoader?) : super(iProgressLoader) {
|
||||||
|
mUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: ApiException) {
|
||||||
|
super.onError(e)
|
||||||
|
XToastUtils.error(e)
|
||||||
|
if (!StringUtils.isEmpty(mUrl)) {
|
||||||
|
Logger.e("网络请求的url:$mUrl", e)
|
||||||
|
} else {
|
||||||
|
Logger.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IProgressLoader的创建工厂实现接口
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:17
|
||||||
|
*/
|
||||||
|
interface IProgressLoaderFactory {
|
||||||
|
/**
|
||||||
|
* 创建进度加载者
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun create(context: Context?): IProgressLoader?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建进度加载者
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param message 默认提示
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun create(context: Context?, message: String?): IProgressLoader?
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.OnProgressCancelListener
|
||||||
|
import com.xuexiang.xui.widget.dialog.MiniLoadingDialog
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认进度加载
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:07
|
||||||
|
*/
|
||||||
|
class MiniLoadingDialogLoader @JvmOverloads constructor(
|
||||||
|
context: Context?,
|
||||||
|
msg: String? = "请求中...",
|
||||||
|
) : IProgressLoader {
|
||||||
|
/**
|
||||||
|
* 进度loading弹窗
|
||||||
|
*/
|
||||||
|
private val mDialog: MiniLoadingDialog?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度框取消监听
|
||||||
|
*/
|
||||||
|
private var mOnProgressCancelListener: OnProgressCancelListener? = null
|
||||||
|
override fun isLoading(): Boolean {
|
||||||
|
return mDialog != null && mDialog.isShowing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateMessage(msg: String) {
|
||||||
|
mDialog?.updateMessage(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showLoading() {
|
||||||
|
if (mDialog != null && !mDialog.isShowing) {
|
||||||
|
mDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dismissLoading() {
|
||||||
|
if (mDialog != null && mDialog.isShowing) {
|
||||||
|
mDialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCancelable(flag: Boolean) {
|
||||||
|
mDialog!!.setCancelable(flag)
|
||||||
|
if (flag) {
|
||||||
|
mDialog.setOnCancelListener {
|
||||||
|
if (mOnProgressCancelListener != null) {
|
||||||
|
mOnProgressCancelListener!!.onCancelProgress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setOnProgressCancelListener(listener: OnProgressCancelListener) {
|
||||||
|
mOnProgressCancelListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
mDialog = MiniLoadingDialog(context, msg)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迷你加载框创建工厂
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:23
|
||||||
|
*/
|
||||||
|
class MiniProgressLoaderFactory : IProgressLoaderFactory {
|
||||||
|
override fun create(context: Context?): IProgressLoader {
|
||||||
|
return MiniLoadingDialogLoader(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(context: Context?, message: String?): IProgressLoader {
|
||||||
|
return MiniLoadingDialogLoader(context, message)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建进度加载者
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-07-02 12:51
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
class ProgressLoader private constructor() {
|
||||||
|
companion object {
|
||||||
|
private var sIProgressLoaderFactory: IProgressLoaderFactory = MiniProgressLoaderFactory()
|
||||||
|
fun setIProgressLoaderFactory(sIProgressLoaderFactory: IProgressLoaderFactory) {
|
||||||
|
Companion.sIProgressLoaderFactory = sIProgressLoaderFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建进度加载者
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun create(context: Context?): IProgressLoader? {
|
||||||
|
return sIProgressLoaderFactory.create(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建进度加载者
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param message 默认提示信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun create(context: Context?, message: String?): IProgressLoader? {
|
||||||
|
return sIProgressLoaderFactory.create(context, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
throw UnsupportedOperationException("u can't instantiate me...")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.subscriber
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.core.BaseFragment
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
import com.xuexiang.xhttp2.model.XHttpRequest
|
||||||
|
import com.xuexiang.xhttp2.subsciber.ProgressLoadingSubscriber
|
||||||
|
import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
import com.xuexiang.xutil.common.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带错误toast提示和加载进度条的网络请求订阅
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:11
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class TipProgressLoadingSubscriber<T> : ProgressLoadingSubscriber<T> {
|
||||||
|
/**
|
||||||
|
* 记录一下请求的url,确定出错的请求是哪个请求
|
||||||
|
*/
|
||||||
|
private var mUrl: String? = null
|
||||||
|
|
||||||
|
constructor() : super()
|
||||||
|
constructor(fragment: BaseFragment<*>) : super(fragment.progressLoader)
|
||||||
|
constructor(iProgressLoader: IProgressLoader?) : super(iProgressLoader)
|
||||||
|
constructor(req: XHttpRequest) : this(req.url)
|
||||||
|
constructor(url: String?) : super() {
|
||||||
|
mUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: ApiException) {
|
||||||
|
super.onError(e)
|
||||||
|
XToastUtils.error(e)
|
||||||
|
if (!StringUtils.isEmpty(mUrl)) {
|
||||||
|
Logger.e("网络请求的url:$mUrl", e)
|
||||||
|
} else {
|
||||||
|
Logger.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.http.subscriber
|
||||||
|
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xhttp2.exception.ApiException
|
||||||
|
import com.xuexiang.xhttp2.model.XHttpRequest
|
||||||
|
import com.xuexiang.xhttp2.subsciber.BaseSubscriber
|
||||||
|
import com.xuexiang.xutil.common.StringUtils
|
||||||
|
import com.xuexiang.xutil.common.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带错误toast提示的网络请求订阅
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-11-18 23:10
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
abstract class TipRequestSubscriber<T> : BaseSubscriber<T> {
|
||||||
|
/**
|
||||||
|
* 记录一下请求的url,确定出错的请求是哪个请求
|
||||||
|
*/
|
||||||
|
private var mUrl: String? = null
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
constructor(req: XHttpRequest) : this(req.url)
|
||||||
|
constructor(url: String?) {
|
||||||
|
mUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onError(e: ApiException) {
|
||||||
|
XToastUtils.error(e)
|
||||||
|
if (!StringUtils.isEmpty(mUrl)) {
|
||||||
|
Logger.e("网络请求的url:$mUrl", e)
|
||||||
|
} else {
|
||||||
|
Logger.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.webview
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xrouter.facade.Postcard
|
||||||
|
import com.xuexiang.xrouter.facade.callback.NavCallback
|
||||||
|
import com.xuexiang.xrouter.launcher.XRouter
|
||||||
|
import com.xuexiang.xui.widget.slideback.SlideBack
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 壳浏览器
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019/1/5 上午12:15
|
||||||
|
*/
|
||||||
|
class AgentWebActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_agent_web)
|
||||||
|
SlideBack.with(this)
|
||||||
|
.haveScroll(true)
|
||||||
|
.callBack { finish() }
|
||||||
|
.register()
|
||||||
|
val uri = intent.data
|
||||||
|
if (uri != null) {
|
||||||
|
XRouter.getInstance().build(uri).navigation(this, object : NavCallback() {
|
||||||
|
override fun onArrival(postcard: Postcard) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLost(postcard: Postcard) {
|
||||||
|
loadUrl(uri.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
val url = intent.getStringExtra(AgentWebFragment.KEY_URL)
|
||||||
|
loadUrl(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadUrl(url: String?) {
|
||||||
|
if (url != null) {
|
||||||
|
openFragment(url)
|
||||||
|
} else {
|
||||||
|
XToastUtils.error(getString(R.string.data_error))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mAgentWebFragment: AgentWebFragment? = null
|
||||||
|
private fun openFragment(url: String) {
|
||||||
|
val ft = supportFragmentManager.beginTransaction()
|
||||||
|
ft.add(
|
||||||
|
R.id.container_frame_layout,
|
||||||
|
AgentWebFragment.getInstance(url).also { mAgentWebFragment = it })
|
||||||
|
ft.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
|
val agentWebFragment = mAgentWebFragment
|
||||||
|
return if (agentWebFragment != null) {
|
||||||
|
if ((agentWebFragment as FragmentKeyDown).onFragmentKeyDown(keyCode, event)) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
} else super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
SlideBack.unregister(this)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 请求浏览器
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
fun goWeb(context: Context?, url: String?) {
|
||||||
|
val intent = Intent(context, AgentWebActivity::class.java)
|
||||||
|
intent.putExtra(AgentWebFragment.KEY_URL, url)
|
||||||
|
context?.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.webview
|
||||||
|
|
||||||
|
import android.view.KeyEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019/1/4 下午11:32
|
||||||
|
*/
|
||||||
|
interface FragmentKeyDown {
|
||||||
|
/**
|
||||||
|
* fragment按键监听
|
||||||
|
* @param keyCode
|
||||||
|
* @param event
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun onFragmentKeyDown(keyCode: Int, event: KeyEvent?): Boolean
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.webview
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.WebView
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.just.agentweb.widget.IWebLayout
|
||||||
|
import com.scwang.smartrefresh.layout.SmartRefreshLayout
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义支持下来回弹的WebView
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019/1/5 上午2:01
|
||||||
|
*/
|
||||||
|
class WebLayout(activity: Activity?) : IWebLayout<WebView?, ViewGroup?> {
|
||||||
|
private val mSmartRefreshLayout: SmartRefreshLayout = LayoutInflater.from(activity)
|
||||||
|
.inflate(R.layout.fragment_pulldown_web, null) as SmartRefreshLayout
|
||||||
|
private val mWebView: WebView = mSmartRefreshLayout.findViewById(R.id.webView)
|
||||||
|
override fun getLayout(): ViewGroup {
|
||||||
|
return mSmartRefreshLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWebView(): WebView {
|
||||||
|
return mWebView
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package com.idormy.sms.forwarder.core.webview
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||||
|
import com.xuexiang.xui.utils.ResUtils
|
||||||
|
import com.xuexiang.xui.widget.dialog.DialogLoader
|
||||||
|
import com.xuexiang.xutil.XUtil
|
||||||
|
import com.xuexiang.xutil.app.ActivityUtils
|
||||||
|
import java.net.URISyntaxException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebView拦截提示
|
||||||
|
*
|
||||||
|
* @author xuexiang
|
||||||
|
* @since 2019-10-21 9:51
|
||||||
|
*/
|
||||||
|
class WebViewInterceptDialog : AppCompatActivity(), DialogInterface.OnDismissListener {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val url = intent.getStringExtra(KEY_INTERCEPT_URL).toString()
|
||||||
|
DialogLoader.getInstance().showConfirmDialog(
|
||||||
|
this,
|
||||||
|
getOpenTitle(url),
|
||||||
|
ResUtils.getString(R.string.lab_yes),
|
||||||
|
{ dialog: DialogInterface, _: Int ->
|
||||||
|
dialog.dismiss()
|
||||||
|
if (isAppLink(url)) {
|
||||||
|
openAppLink(this, url)
|
||||||
|
} else {
|
||||||
|
openApp(url)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ResUtils.getString(R.string.lab_no)
|
||||||
|
) { dialog: DialogInterface, _: Int -> dialog.dismiss() }.setOnDismissListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOpenTitle(url: String): String {
|
||||||
|
val scheme = getScheme(url)
|
||||||
|
return if ("mqqopensdkapi" == scheme) {
|
||||||
|
"是否允许页面打开\"QQ\"?"
|
||||||
|
} else {
|
||||||
|
ResUtils.getString(R.string.lab_open_third_app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getScheme(url: String): String? {
|
||||||
|
try {
|
||||||
|
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
|
||||||
|
return intent.scheme
|
||||||
|
} catch (e: URISyntaxException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAppLink(url: String): Boolean {
|
||||||
|
val uri = Uri.parse(url)
|
||||||
|
return uri != null && APP_LINK_HOST == uri.host && (url.startsWith("http") || url.startsWith(
|
||||||
|
"https"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openApp(url: String) {
|
||||||
|
val intent: Intent
|
||||||
|
try {
|
||||||
|
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
|
XUtil.getContext().startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
XToastUtils.error(getString(R.string.third_party_app_not_installed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openAppLink(context: Context, url: String) {
|
||||||
|
try {
|
||||||
|
val intent = Intent(APP_LINK_ACTION)
|
||||||
|
intent.data = Uri.parse(url)
|
||||||
|
context.startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
XToastUtils.error(getString(R.string.third_party_app_not_installed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDismiss(dialog: DialogInterface) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY_INTERCEPT_URL = "key_intercept_url"
|
||||||
|
|
||||||
|
// TODO: 2019-10-30 这里修改你的applink
|
||||||
|
const val APP_LINK_HOST = "xuexiangjys.club"
|
||||||
|
const val APP_LINK_ACTION = "com.xuexiang.xui.applink"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示WebView拦截提示
|
||||||
|
*
|
||||||
|
* @param url 需要拦截处理的url
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun show(url: String?) {
|
||||||
|
ActivityUtils.startActivity(WebViewInterceptDialog::class.java, KEY_INTERCEPT_URL, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
package com.idormy.sms.forwarder.database;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.room.Database;
|
|
||||||
import androidx.room.Room;
|
|
||||||
import androidx.room.RoomDatabase;
|
|
||||||
|
|
||||||
@Database(entities = {Config.class}, version = 2, exportSchema = false)
|
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
|
||||||
private static volatile AppDatabase instance;
|
|
||||||
|
|
||||||
public abstract ConfigDao configDao();
|
|
||||||
|
|
||||||
public static AppDatabase getInstance(Context context) {
|
|
||||||
if (instance == null) {
|
|
||||||
synchronized (AppDatabase.class) {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = Room.databaseBuilder(context, AppDatabase.class, "sms_forwarder.db").build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package com.idormy.sms.forwarder.database;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.room.Entity;
|
|
||||||
import androidx.room.Ignore;
|
|
||||||
import androidx.room.PrimaryKey;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
|
|
||||||
public class Config {
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
@NonNull
|
|
||||||
private String uid;
|
|
||||||
private String name;
|
|
||||||
private String cfg;
|
|
||||||
@Ignore
|
|
||||||
private Boolean connecting;
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
public Config() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
public Config(String cfg) {
|
|
||||||
this.cfg = cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config(@NonNull String uid, String name, String cfg) {
|
|
||||||
this.uid = uid;
|
|
||||||
this.name = name;
|
|
||||||
this.cfg = cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public String getUid() {
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config setUid(@NonNull String uid) {
|
|
||||||
this.uid = uid;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getConnecting() {
|
|
||||||
return connecting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config setConnecting(Boolean connecting) {
|
|
||||||
this.connecting = connecting;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCfg() {
|
|
||||||
return cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config setCfg(String cfg) {
|
|
||||||
this.cfg = cfg;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Config config = (Config) o;
|
|
||||||
return Objects.equals(uid, config.uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Config{" +
|
|
||||||
"uid='" + uid + '\'' +
|
|
||||||
", cfg='" + cfg + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package com.idormy.sms.forwarder.database;
|
|
||||||
|
|
||||||
import androidx.room.Dao;
|
|
||||||
import androidx.room.Delete;
|
|
||||||
import androidx.room.Insert;
|
|
||||||
import androidx.room.Query;
|
|
||||||
import androidx.room.Update;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
import io.reactivex.Single;
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
public interface ConfigDao {
|
|
||||||
@Query("SELECT * FROM config")
|
|
||||||
Single<List<Config>> getAll();
|
|
||||||
|
|
||||||
@Query("SELECT * FROM config where uid=:uid")
|
|
||||||
Single<Config> getConfigByUid(String uid);
|
|
||||||
|
|
||||||
@Update
|
|
||||||
Completable update(Config config);
|
|
||||||
|
|
||||||
@Insert
|
|
||||||
Completable insert(Config config);
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
Completable delete(Config config);
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Frpc
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface FrpcDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(frpc: Frpc)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(frpc: Frpc)
|
||||||
|
|
||||||
|
@Query("DELETE FROM Frpc where uid=:uid")
|
||||||
|
fun delete(uid: String)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(frpc: Frpc)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Frpc where uid=:uid")
|
||||||
|
fun get(uid: String): Single<Frpc>
|
||||||
|
|
||||||
|
//TODO:允许主线程访问,后面再优化
|
||||||
|
@Query("SELECT * FROM Frpc where autorun=1")
|
||||||
|
fun getAutorun(): List<Frpc>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Frpc ORDER BY time DESC")
|
||||||
|
fun pagingSource(): PagingSource<Int, Frpc>
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Logs
|
||||||
|
import com.idormy.sms.forwarder.database.entity.LogsAndRuleAndSender
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface LogsDao {
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
suspend fun insert(logs: Logs): Long
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(logs: Logs): Completable
|
||||||
|
|
||||||
|
@Query("DELETE FROM Logs where id=:id")
|
||||||
|
fun delete(id: Long)
|
||||||
|
|
||||||
|
@Query("DELETE FROM Logs where type=:type")
|
||||||
|
fun deleteAll(type: String): Completable
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(logs: Logs): Completable
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Logs where id=:id")
|
||||||
|
fun get(id: Long): Single<Logs>
|
||||||
|
|
||||||
|
@Query("SELECT count(*) FROM Logs where type=:type and forward_status=:forwardStatus")
|
||||||
|
fun count(type: String, forwardStatus: Int): Single<Int>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM Logs WHERE type = :type ORDER BY id DESC")
|
||||||
|
fun pagingSource(type: String): PagingSource<Int, LogsAndRuleAndSender>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"UPDATE Logs SET forward_status=:status" +
|
||||||
|
",forward_response=CASE WHEN (trim(forward_response) = '' or trim(forward_response) = 'ok')" +
|
||||||
|
" THEN :response" +
|
||||||
|
" ELSE forward_response || '\n--------------------\n' || :response" +
|
||||||
|
" END" +
|
||||||
|
" where id=:id"
|
||||||
|
)
|
||||||
|
fun updateStatus(id: Long, status: Int, response: String): Int
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Rule
|
||||||
|
import com.idormy.sms.forwarder.database.entity.RuleAndSender
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface RuleDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(rule: Rule)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(rule: Rule): Completable
|
||||||
|
|
||||||
|
@Query("DELETE FROM Rule where id=:id")
|
||||||
|
fun delete(id: Long)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(rule: Rule)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Rule where id=:id")
|
||||||
|
fun get(id: Long): Single<Rule>
|
||||||
|
|
||||||
|
@Query("SELECT count(*) FROM Rule where type=:type and status=:status")
|
||||||
|
fun count(type: String, status: Int): Single<Int>
|
||||||
|
|
||||||
|
/*@Query(
|
||||||
|
"SELECT Rule.*," +
|
||||||
|
"Sender.name as sender_name,Sender.type as sender_type" +
|
||||||
|
" FROM Rule" +
|
||||||
|
" LEFT JOIN Sender ON Rule.sender_id = Sender.id" +
|
||||||
|
" where Rule.type=:type" +
|
||||||
|
" ORDER BY Rule.time DESC"
|
||||||
|
)
|
||||||
|
fun pagingSource(type: String): PagingSource<Int, Rule>*/
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM Rule where type=:type ORDER BY id DESC")
|
||||||
|
fun pagingSource(type: String): PagingSource<Int, RuleAndSender>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM rule where type=:type and status=:status and (sim_slot='ALL' or sim_slot=:simSlot)")
|
||||||
|
suspend fun getRuleAndSender(type: String, status: Int, simSlot: String): List<RuleAndSender>
|
||||||
|
|
||||||
|
//TODO:允许主线程访问,后面再优化
|
||||||
|
@Query("SELECT * FROM rule ORDER BY id ASC")
|
||||||
|
fun getAll(): List<Rule>
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Sender
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Single
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface SenderDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(sender: Sender)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(sender: Sender): Completable
|
||||||
|
|
||||||
|
@Query("DELETE FROM Sender where id=:id")
|
||||||
|
fun delete(id: Long)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(sender: Sender)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Sender where id=:id")
|
||||||
|
fun get(id: Long): Single<Sender>
|
||||||
|
|
||||||
|
@Query("SELECT count(*) FROM Sender where type=:type and status=:status")
|
||||||
|
fun count(type: String, status: Int): Single<Int>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Sender where status=:status ORDER BY id DESC")
|
||||||
|
fun pagingSource(status: Int): PagingSource<Int, Sender>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM sender ORDER BY id DESC")
|
||||||
|
fun getAll(): Single<List<Sender>>
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(id) FROM sender WHERE status = 1")
|
||||||
|
fun getOnCount(): Flow<Long>
|
||||||
|
|
||||||
|
//TODO:允许主线程访问,后面再优化
|
||||||
|
@Query("SELECT * FROM sender ORDER BY id ASC")
|
||||||
|
fun getAll2(): List<Sender>
|
||||||
|
|
||||||
|
@Query("DELETE FROM Sender")
|
||||||
|
fun deleteAll()
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.entity
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.utils.STATUS_ON
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@Entity(tableName = "Frpc")
|
||||||
|
data class Frpc(
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "uid") var uid: String,
|
||||||
|
@ColumnInfo(name = "name") var name: String,
|
||||||
|
@ColumnInfo(name = "config") var config: String,
|
||||||
|
@ColumnInfo(name = "autorun", defaultValue = "0") var autorun: Int = 0,
|
||||||
|
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||||
|
@Ignore var connecting: Boolean = false,
|
||||||
|
) : Parcelable {
|
||||||
|
constructor() : this("", "", "", 0, Date(), false)
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
constructor(config: String) : this("", "", config, 0, Date(), false)
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
constructor(uid: String, name: String, config: String) : this(uid, name, config, 0, Date(), false)
|
||||||
|
|
||||||
|
fun setConnecting(connecting: Boolean): Frpc {
|
||||||
|
this.connecting = connecting
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
val autorunImageId: Int
|
||||||
|
get() = when (autorun) {
|
||||||
|
STATUS_ON -> R.drawable.ic_autorun
|
||||||
|
else -> R.drawable.ic_manual
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.entity
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@Entity(
|
||||||
|
tableName = "Logs",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Rule::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["rule_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE, //级联操作
|
||||||
|
onUpdate = ForeignKey.CASCADE //级联操作
|
||||||
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["id"], unique = true),
|
||||||
|
Index(value = ["rule_id"])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Logs(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ColumnInfo(name = "id") var id: Long,
|
||||||
|
@ColumnInfo(name = "type", defaultValue = "sms") var type: String,
|
||||||
|
@ColumnInfo(name = "from", defaultValue = "") var from: String,
|
||||||
|
@ColumnInfo(name = "content", defaultValue = "") var content: String,
|
||||||
|
@ColumnInfo(name = "rule_id", defaultValue = "0") var ruleId: Long = 0,
|
||||||
|
@ColumnInfo(name = "sim_info", defaultValue = "") var simInfo: String = "",
|
||||||
|
@ColumnInfo(name = "forward_status", defaultValue = "1") var forwardStatus: Int = 1,
|
||||||
|
@ColumnInfo(name = "forward_response", defaultValue = "") var forwardResponse: String = "",
|
||||||
|
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
|
val simImageId: Int
|
||||||
|
get() {
|
||||||
|
if (simInfo.isNotEmpty()) {
|
||||||
|
if (simInfo.replace("-", "").startsWith("SIM2")) {
|
||||||
|
return R.drawable.ic_sim2 //mipmap
|
||||||
|
} else if (simInfo.replace("-", "").startsWith("SIM1")) {
|
||||||
|
return R.drawable.ic_sim1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return R.drawable.ic_sim
|
||||||
|
}
|
||||||
|
|
||||||
|
val statusImageId: Int
|
||||||
|
get() {
|
||||||
|
if (forwardStatus == 1) {
|
||||||
|
return R.drawable.ic_round_warning
|
||||||
|
} else if (forwardStatus == 2) {
|
||||||
|
return R.drawable.ic_round_check
|
||||||
|
}
|
||||||
|
return R.drawable.ic_round_cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.entity
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class LogsAndRuleAndSender(
|
||||||
|
@Embedded val logs: Logs,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
entity = Rule::class,
|
||||||
|
parentColumn = "rule_id",
|
||||||
|
entityColumn = "id"
|
||||||
|
)
|
||||||
|
val relation: RuleAndSender,
|
||||||
|
) : Parcelable
|
@ -0,0 +1,180 @@
|
|||||||
|
package com.idormy.sms.forwarder.database.entity
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.*
|
||||||
|
import com.idormy.sms.forwarder.R
|
||||||
|
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||||
|
import com.idormy.sms.forwarder.utils.*
|
||||||
|
import com.xuexiang.xui.utils.ResUtils.getString
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.*
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import java.util.regex.PatternSyntaxException
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@Entity(
|
||||||
|
tableName = "Rule",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Sender::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["sender_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE, //级联操作
|
||||||
|
onUpdate = ForeignKey.CASCADE //级联操作
|
||||||
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["id"], unique = true),
|
||||||
|
Index(value = ["sender_id"])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Rule(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ColumnInfo(name = "id") var id: Long,
|
||||||
|
@ColumnInfo(name = "type", defaultValue = "sms") var type: String,
|
||||||
|
@ColumnInfo(name = "filed", defaultValue = "transpond_all") var filed: String,
|
||||||
|
@ColumnInfo(name = "check", defaultValue = "is") var check: String,
|
||||||
|
@ColumnInfo(name = "value", defaultValue = "") var value: String,
|
||||||
|
@ColumnInfo(name = "sender_id", defaultValue = "0") var senderId: Long = 0,
|
||||||
|
@ColumnInfo(name = "sms_template", defaultValue = "") var smsTemplate: String = "",
|
||||||
|
@ColumnInfo(name = "regex_replace", defaultValue = "") var regexReplace: String = "",
|
||||||
|
@ColumnInfo(name = "sim_slot", defaultValue = "ALL") var simSlot: String = "",
|
||||||
|
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1,
|
||||||
|
@ColumnInfo(name = "time") var time: Date = Date(),
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val TAG: String = Rule::class.java.simpleName
|
||||||
|
|
||||||
|
fun getRuleMatch(filed: String?, check: String?, value: String?, simSlot: String?): Any {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append(SIM_SLOT_MAP[simSlot]).append(getString(R.string.rule_card))
|
||||||
|
if (filed == null || filed == FILED_TRANSPOND_ALL) {
|
||||||
|
sb.append(getString(R.string.rule_all_fw_to))
|
||||||
|
} else {
|
||||||
|
sb.append(getString(R.string.rule_when)).append(FILED_MAP[filed]).append(CHECK_MAP[check]).append(value).append(getString(R.string.rule_fw_to))
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val ruleMatch: String
|
||||||
|
get() {
|
||||||
|
val simStr = if ("app" == type) "" else SIM_SLOT_MAP[simSlot].toString() + getString(R.string.rule_card)
|
||||||
|
return if (filed == FILED_TRANSPOND_ALL) {
|
||||||
|
simStr + getString(R.string.rule_all_fw_to)
|
||||||
|
} else {
|
||||||
|
simStr + getString(R.string.rule_when) + FILED_MAP[filed] + CHECK_MAP[check] + value + getString(R.string.rule_fw_to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val statusChecked: Boolean
|
||||||
|
get() = status != STATUS_OFF
|
||||||
|
|
||||||
|
val imageId: Int
|
||||||
|
get() = when (simSlot) {
|
||||||
|
CHECK_SIM_SLOT_1 -> R.drawable.ic_sim1
|
||||||
|
CHECK_SIM_SLOT_2 -> R.drawable.ic_sim2
|
||||||
|
CHECK_SIM_SLOT_ALL -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||||
|
else -> if (type == "app") R.drawable.ic_app else R.drawable.ic_sim
|
||||||
|
}
|
||||||
|
|
||||||
|
val statusImageId: Int
|
||||||
|
get() = when (status) {
|
||||||
|
STATUS_OFF -> R.drawable.icon_off
|
||||||
|
else -> R.drawable.icon_on
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSimSlotCheckId(): Int {
|
||||||
|
return when (simSlot) {
|
||||||
|
CHECK_SIM_SLOT_1 -> R.id.rb_sim_slot_1
|
||||||
|
CHECK_SIM_SLOT_2 -> R.id.rb_sim_slot_2
|
||||||
|
else -> R.id.rb_sim_slot_all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFiledCheckId(): Int {
|
||||||
|
return when (filed) {
|
||||||
|
FILED_MSG_CONTENT -> R.id.rb_content
|
||||||
|
FILED_PHONE_NUM -> R.id.rb_phone
|
||||||
|
FILED_PACKAGE_NAME -> R.id.rb_package_name
|
||||||
|
FILED_INFORM_CONTENT -> R.id.rb_inform_content
|
||||||
|
FILED_MULTI_MATCH -> R.id.rb_multi_match
|
||||||
|
else -> R.id.rb_transpond_all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCheckCheckId(): Int {
|
||||||
|
return when (check) {
|
||||||
|
CHECK_CONTAIN -> R.id.rb_contain
|
||||||
|
CHECK_NOT_CONTAIN -> R.id.rb_not_contain
|
||||||
|
CHECK_START_WITH -> R.id.rb_start_with
|
||||||
|
CHECK_END_WITH -> R.id.rb_end_with
|
||||||
|
CHECK_REGEX -> R.id.rb_regex
|
||||||
|
else -> R.id.rb_is
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//字段分支
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun checkMsg(msg: MsgInfo?): Boolean {
|
||||||
|
|
||||||
|
//检查这一行和上一行合并的结果是否命中
|
||||||
|
var mixChecked = false
|
||||||
|
if (msg != null) {
|
||||||
|
//先检查规则是否命中
|
||||||
|
when (this.filed) {
|
||||||
|
FILED_TRANSPOND_ALL -> mixChecked = true
|
||||||
|
FILED_PHONE_NUM, FILED_PACKAGE_NAME -> mixChecked = checkValue(msg.from)
|
||||||
|
FILED_MSG_CONTENT, FILED_INFORM_CONTENT -> mixChecked = checkValue(msg.content)
|
||||||
|
FILED_MULTI_MATCH -> mixChecked = RuleLineUtils.checkRuleLines(msg, this.value)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "rule:$this checkMsg:$msg checked:$mixChecked")
|
||||||
|
return mixChecked
|
||||||
|
}
|
||||||
|
|
||||||
|
//内容分支
|
||||||
|
private fun checkValue(msgValue: String?): Boolean {
|
||||||
|
var checked = false
|
||||||
|
when (this.check) {
|
||||||
|
CHECK_IS -> checked = this.value == msgValue
|
||||||
|
CHECK_NOT_IS -> checked = this.value != msgValue
|
||||||
|
CHECK_CONTAIN -> if (msgValue != null) {
|
||||||
|
checked = msgValue.contains(this.value)
|
||||||
|
}
|
||||||
|
CHECK_NOT_CONTAIN -> if (msgValue != null) {
|
||||||
|
checked = !msgValue.contains(this.value)
|
||||||
|
}
|
||||||
|
CHECK_START_WITH -> if (msgValue != null) {
|
||||||
|
checked = msgValue.startsWith(this.value)
|
||||||
|
}
|
||||||
|
CHECK_END_WITH -> if (msgValue != null) {
|
||||||
|
checked = msgValue.endsWith(this.value)
|
||||||
|
}
|
||||||
|
CHECK_REGEX -> if (msgValue != null) {
|
||||||
|
try {
|
||||||
|
//checked = Pattern.matches(this.value, msgValue);
|
||||||
|
val pattern = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE)
|
||||||
|
val matcher = pattern.matcher(msgValue)
|
||||||
|
while (matcher.find()) {
|
||||||
|
checked = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (e: PatternSyntaxException) {
|
||||||
|
Log.d(TAG, "PatternSyntaxException: ")
|
||||||
|
Log.d(TAG, "Description: " + e.description)
|
||||||
|
Log.d(TAG, "Index: " + e.index)
|
||||||
|
Log.d(TAG, "Message: " + e.message)
|
||||||
|
Log.d(TAG, "Pattern: " + e.pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "checkValue " + msgValue + " " + this.check + " " + this.value + " checked:" + checked)
|
||||||
|
return checked
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue