Compare commits

...

30 Commits
v3.3.2 ... main

Author SHA1 Message Date
Brian-Lynn
751f1b3163
优化:Bark发送通道消息链接支持插入变量标签(#604) 2025-05-11 19:50:55 +08:00
lphgor
45dc37aea9
整理:字典错别字 (#608) 2025-05-11 19:46:28 +08:00
pppscn
de9509fead 整理:字典错别字 2025-04-29 13:35:36 +08:00
pppscn
114052d1c6 新增:自动任务-快捷指令-警报提醒动作添加闪烁手机的功能 #581 2025-03-21 19:12:06 +08:00
pppscn
0391868790 优化:Telegram 发送通道支持 MarkdownV2解析模式 #570 2025-03-02 21:56:34 +08:00
pppscn
44728b858a 整理:更新版本号 3.3.3 2025-02-14 14:17:17 +08:00
pppscn
23acf01c61 新增: {{PHONE_AREA}} 标签用于反查{{FROM}}对应的归属地 #564 2025-02-13 19:18:05 +08:00
pppscn
b733fc2d4c 新增: {{CONTACT_NAME}} 标签用于反查{{FROM}}对应的通讯录姓名 #582 2025-02-13 17:34:24 +08:00
pppscn
992cabe323 优化:手机号匹配通讯录联系人算法改进 #580 2025-02-13 17:07:55 +08:00
pppscn
8b37862121 修复:未授权访问应用列表时进入应用列表异常提示 #577 2025-02-02 22:01:39 +08:00
pppscn
f49310027e 整理:Gradle依赖库 #576 2025-02-02 19:12:34 +08:00
kvii
cc86c0718f
优化:添加周一到周日的繁体翻译 (#568)
* 优化:添加周一到周日的繁体翻译

* 优化:把週一到週日改成了星期一到星期日
2024-12-17 13:26:40 +08:00
pppscn
2a910fe504 优化:统一管理自定义模板可用变量标签插入按钮 #559 2024-11-21 09:44:22 +08:00
pppscn
0a3f9f53f1 优化:统一管理自定义模板可用变量标签插入按钮 #559 2024-11-21 03:47:26 +08:00
pppscn
a77f630524 整理:感谢 JetBrains 全家桶授权 2024-11-18 16:29:21 +08:00
pppscn
7c33566696 新增:自动任务-快捷指令-警报提醒动作添加振动提醒的功能 #554 #482 2024-11-15 15:37:21 +08:00
pppscn
f029048d09 升级:android-mail升级为jakarta.mail
新增:添加iCloud邮箱支持 #541
2024-11-15 14:27:26 +08:00
pppscn
b05d856f26 新增:bark发送通道允许自定义自动复制copy字段 #538 2024-11-14 19:15:24 +08:00
pppscn
3a341f6c4e 新增:自动任务-快捷指令-警报提醒动作添加振动提醒的功能 #554 #482 2024-11-14 18:34:58 +08:00
pppscn
c86f20c6de 优化:首页弹窗tips多语言适配 #544 2024-11-11 20:49:38 +08:00
sensi
aa8ab646f5 新增:免打扰支持指定星期 2024-11-11 16:47:17 +08:00
kvii
8e7c425fdc 优化:红米手机品牌问题 2024-11-11 16:38:48 +08:00
kvii
dc3726d0de 优化:单 sim 卡槽手机不显示 sim2 设置。 2024-11-07 19:57:04 +08:00
kvii
3adfffee4f 优化:typo 2024-11-07 19:54:26 +08:00
pppscn
c56ac0e425 整理:更新打赏链接 2024-11-02 15:20:11 +08:00
Easy
3cc2311600 兼容Server酱³Sendkey 2024-10-25 16:52:03 +08:00
pppscn
5c19c10121 新增:webhook 发送通道通过Content-Type=text/plain、text/html、text/css、text/javascript、text/xml指定请求体为文本格式 2024-10-17 22:41:59 +08:00
pppscn
785a3a2364 新增:bark通道的持续提醒功能 #528 2024-10-01 23:38:33 +08:00
pppscn
66831865cb 修复:转发规则测试模拟的通话类型显示错误 #IAG059 2024-08-26 14:28:38 +08:00
pppscn
d4ed59b37f 整理:新用户必读 2024-08-23 16:13:25 +08:00
78 changed files with 2111 additions and 1501 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,3 +1,3 @@
# These are supported funding model platforms
custom: ["https://foruda.gitee.com/images/1705068554951915754/89c6e226_16273.png", "https://afdian.net/a/pppscn", "https://github.com/pppscn/SmsForwarder/wiki/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95", "https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427"]
custom: ["https://foruda.gitee.com/images/1730529431184709105/20bfc86c_16273.gif", "https://ifdian.net/a/pppscn", "https://github.com/pppscn/SmsForwarder/wiki/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95", "https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4912193&doc_id=1821427"]

1
.gitignore vendored
View File

@ -28,3 +28,4 @@
/pic/*.drawio
/pic/*.vsdx
/psd
/pic/*.psd

View File

@ -101,7 +101,7 @@
+ https://github.com/yanzhenjie/AndServer (HttpServer)
+ https://github.com/jenly1314/Location (Location)
+ https://gitee.com/xuankaicat/kmnkt (socket通信)
+ [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_ga=2.126618957.1361252949.1638261367-1417196221.1635638144&_gl=1*1pfl3dq*_ga*MTQxNzE5NjIyMS4xNjM1NjM4MTQ0*_ga_V0XZL7QHEB*MTYzODMzMjA4OC43LjAuMTYzODMzMjA5Ny4w" alt="GitHub license" style="width96px" width="96" />](https://jb.gg/OpenSourceSupport) (License Certificate for JetBrains All Products Pack)
+ [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="GitHub license" style="width159px; height: 32px" width="159" height="32" />](https://jb.gg/OpenSourceSupport) (License Certificate for JetBrains All Products Pack)
--------

View File

@ -97,7 +97,7 @@ See more screenshotshttps://github.com/pppscn/SmsForwarder/wiki
+ https://github.com/yanzhenjie/AndServer (HttpServer)
+ https://github.com/jenly1314/Location (Location)
+ https://gitee.com/xuankaicat/kmnkt (socket)
+ [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_ga=2.126618957.1361252949.1638261367-1417196221.1635638144&_gl=1*1pfl3dq*_ga*MTQxNzE5NjIyMS4xNjM1NjM4MTQ0*_ga_V0XZL7QHEB*MTYzODMzMjA4OC43LjAuMTYzODMzMjA5Ny4w" alt="GitHub license" style="width96px" width="96" />](https://jb.gg/OpenSourceSupport) (License Certificate for JetBrains All Products Pack)
+ [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="GitHub license" style="width159px; height: 32px" width="159" height="32" />](https://jb.gg/OpenSourceSupport) (License Certificate for JetBrains All Products Pack)
--------

View File

@ -277,8 +277,8 @@ dependencies {
//vLayouthttps://github.com/alibaba/vlayout
implementation 'com.alibaba.android:vlayout:1.3.0'
//
implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-header:1.1.5'
implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-layout:1.1.5'
implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-header:1.1.4'
implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-layout:1.1.4'
//WebView
implementation 'com.github.xuexiangjys.AgentWeb:agentweb-core:1.0.1'
implementation 'com.github.xuexiangjys.AgentWeb:agentweb-download:1.0.1'//
@ -286,7 +286,7 @@ dependencies {
implementation 'me.jessyan:autosize:1.2.1'
//
implementation 'com.umeng.umsdk:common:9.6.8'
implementation 'com.umeng.umsdk:asms:1.8.2'
implementation 'com.umeng.umsdk:asms:1.8.6'
//
implementation 'me.samlss:broccoli:1.0.0'
@ -323,8 +323,8 @@ dependencies {
implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0'
//MarkdownViewhttps://github.com/tiagohm/MarkdownView
implementation 'com.github.tiagohm.MarkdownView:library:0.19.0'
//implementation 'com.github.tiagohm.MarkdownView:emoji:0.19.0'
implementation 'com.github.pppscn.MarkdownView:library:0.19.0'
//implementation 'com.github.pppscn.MarkdownView:emoji:0.19.0'
def retrofit2_version = '2.9.0'
//noinspection GradleDependency
@ -342,13 +342,14 @@ dependencies {
testImplementation "androidx.paging:paging-common-ktx:$paging_version"
//https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:18.62'
implementation 'com.github.getActivity:XXPermissions:20.0'
//https://github.com/getActivity/MultiLanguages
implementation 'com.github.getActivity:MultiLanguages:b47f7be' //9.3
def mail_version = '1.6.7'
implementation "com.sun.mail:android-mail:$mail_version"
implementation "com.sun.mail:android-activation:$mail_version"
// https://jakartaee.github.io/mail-api/Android
def mail_version = '2.0.1'
implementation "com.sun.mail:jakarta.mail:$mail_version"
implementation "com.sun.activation:jakarta.activation:$mail_version"
//SM4 JAVA实现(BC实现)
def bouncycastle_version = '1.77'
@ -396,4 +397,4 @@ static def getGitCommitId() {
e.printStackTrace()
return ""
}
}
}

View File

@ -6,7 +6,12 @@
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="false" />
<uses-permission android:name="com.android.permission.GET_INSTALLED_APPS" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
@ -72,6 +77,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".App"
@ -156,10 +163,6 @@
<data android:scheme="https" />
<data android:scheme="about" />
<data android:scheme="javascript" />
<!-- 设置自己的deeplink -->
<!-- <data-->
<!-- android:host="xxx.com"-->
<!-- android:scheme="xui"/>-->
</intent-filter>
<!-- AppLink -->
<intent-filter
@ -377,4 +380,4 @@
</application>
</manifest>
</manifest>

View File

@ -1,13 +1,21 @@
{
"Code": 0,
"Data": [
{
"title": "短信转发器",
"content": "本软件用于监控Android手机短信、来电、APP通知并根据指定规则转发到其他设备<br />\n请确认您是否清楚该软件的用途<br />\n否则请立即卸载"
},
{
"title": "防诈提醒",
"content": "本软件不参与任何刷单返利担保!请您远离刷单返利陷阱,谨防网络诈骗!<br />\n请再次确认是否出于本人自用需要自愿安装<br />\n否则请立即卸载"
},
{
"title": "新用户必读",
"content": "开始设置之前,请您认真地看一遍 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages\"><font color=\"#800080\">Wiki</font></a> <br />\n遇到问题请按照 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4877445&doc_id=1821427\"><font color=\"#0000FF\">常见问题</font></a> 章节进行排查!<br />\n没找到答案的再加入互助交流群提问请清楚地描述问题并给出对应的配置截图与相关日志方便大家直观的判断问题 "
},
{
"title": "SmsF 互助交流群",
"content": "<a href=\"https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=1W5aewP&appChannel=share&businessType=9&from=246610&biz=ka\">QQ频道号: q7oofwp13s</a><br /><a href=\"https://t.me/+QBZgnL_fxYM0NjE9\">Telegram 群组</a><br />钉钉群号: 29760014208"
"content": "<a href=\"https://t.me/+QBZgnL_fxYM0NjE9\">Telegram 群组</a>"
},
{
"title": "打赏名单",

View File

@ -93,6 +93,15 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
@SuppressLint("StaticFieldLeak")
lateinit var context: Context
//自定义模板可用变量标签
var COMMON_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var SMS_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var CALL_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var APP_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var LOCATION_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var BATTERY_TAG_MAP: MutableMap<String, String> = mutableMapOf()
var NETWORK_TAG_MAP: MutableMap<String, String> = mutableMapOf()
//通话类型1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
var CALL_TYPE_MAP: MutableMap<String, String> = mutableMapOf()
var FILED_MAP: MutableMap<String, String> = mutableMapOf()
@ -290,7 +299,7 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P && SettingUtils.enableOnePixelActivity) {
setOnePixEnabled(true)
}
//溃是否可以重启用户界面
//溃是否可以重启用户界面
setCrashRestartUIEnabled(true)
addCallback({
Log.d(TAG, "Cactus保活onStop回调")
@ -390,6 +399,78 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
private fun switchLanguage(newLocale: Locale) {
isNeedSpaceBetweenWords = !newLocale.language.contains("zh")
//自定义模板可用变量标签
COMMON_TAG_MAP.clear()
COMMON_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_receive_time) to getString(R.string.insert_tag_receive_time),
getString(R.string.tag_current_time) to getString(R.string.insert_tag_current_time),
getString(R.string.tag_device_name) to getString(R.string.insert_tag_device_name),
getString(R.string.tag_app_version) to getString(R.string.insert_tag_app_version),
)
)
SMS_TAG_MAP.clear()
SMS_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_from) to getString(R.string.insert_tag_from),
getString(R.string.tag_sms) to getString(R.string.insert_tag_sms),
getString(R.string.tag_card_slot) to getString(R.string.insert_tag_card_slot),
getString(R.string.tag_card_subid) to getString(R.string.insert_tag_card_subid),
getString(R.string.tag_contact_name) to getString(R.string.insert_tag_contact_name),
getString(R.string.tag_phone_area) to getString(R.string.insert_tag_phone_area),
)
)
CALL_TAG_MAP.clear()
CALL_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_from) to getString(R.string.insert_tag_from),
getString(R.string.tag_sms) to getString(R.string.insert_tag_msg),
getString(R.string.tag_card_slot) to getString(R.string.insert_tag_card_slot),
getString(R.string.tag_card_subid) to getString(R.string.insert_tag_card_subid),
getString(R.string.tag_call_type) to getString(R.string.insert_tag_call_type),
getString(R.string.tag_contact_name) to getString(R.string.insert_tag_contact_name),
getString(R.string.tag_phone_area) to getString(R.string.insert_tag_phone_area),
)
)
APP_TAG_MAP.clear()
APP_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_uid) to getString(R.string.insert_tag_uid),
getString(R.string.tag_package_name) to getString(R.string.insert_tag_package_name),
getString(R.string.tag_app_name) to getString(R.string.insert_tag_app_name),
getString(R.string.tag_title) to getString(R.string.insert_tag_title),
getString(R.string.tag_msg) to getString(R.string.insert_tag_msg),
)
)
LOCATION_TAG_MAP.clear()
LOCATION_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_location) to getString(R.string.insert_tag_location),
getString(R.string.tag_location_longitude) to getString(R.string.insert_tag_location_longitude),
getString(R.string.tag_location_latitude) to getString(R.string.insert_tag_location_latitude),
getString(R.string.tag_location_address) to getString(R.string.insert_tag_location_address),
)
)
BATTERY_TAG_MAP.clear()
BATTERY_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_battery_pct) to getString(R.string.insert_tag_battery_pct),
getString(R.string.tag_battery_status) to getString(R.string.insert_tag_battery_status),
getString(R.string.tag_battery_plugged) to getString(R.string.insert_tag_battery_plugged),
getString(R.string.tag_battery_info) to getString(R.string.insert_tag_battery_info),
getString(R.string.tag_battery_info_simple) to getString(R.string.insert_tag_battery_info_simple),
)
)
NETWORK_TAG_MAP.clear()
NETWORK_TAG_MAP.putAll(
mapOf(
getString(R.string.tag_ipv4) to getString(R.string.insert_tag_ipv4),
getString(R.string.tag_ipv6) to getString(R.string.insert_tag_ipv6),
getString(R.string.tag_ip_list) to getString(R.string.insert_tag_ip_list),
getString(R.string.tag_net_type) to getString(R.string.insert_tag_net_type),
)
)
CALL_TYPE_MAP.clear()
CALL_TYPE_MAP.putAll(
mapOf(
@ -471,4 +552,4 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
)
}
}
}

View File

@ -51,6 +51,7 @@ import com.jeremyliao.liveeventbus.LiveEventBus
import com.xuexiang.xhttp2.XHttp
import com.xuexiang.xhttp2.callback.DownloadProgressCallBack
import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xui.XUI.getContext
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.utils.ViewUtils
@ -207,10 +208,6 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(), DrawerAdapter.OnItemS
private fun initSlidingMenu(savedInstanceState: Bundle?) {
mSlidingRootNav = SlidingRootNavBuilder(this).withGravity(if (ResUtils.isRtl(this)) SlideGravity.RIGHT else SlideGravity.LEFT).withMenuOpened(false).withContentClickableWhenMenuOpened(false).withSavedState(savedInstanceState).withMenuLayout(R.layout.menu_left_drawer).inject()
mLLMenu = mSlidingRootNav.layout.findViewById(R.id.ll_menu)
//val ivQrcode = mSlidingRootNav.layout.findViewById<AppCompatImageView>(R.id.iv_qrcode)
//ivQrcode.setOnClickListener { openNewPage(SettingsFragment::class.java) }
//val ivSetting = mSlidingRootNav.layout.findViewById<AppCompatImageView>(R.id.iv_setting)
//ivSetting.setOnClickListener { openNewPage(SettingsFragment::class.java) }
ViewUtils.setVisibility(mLLMenu, false)
mAdapter = DrawerAdapter(
mutableListOf(
@ -243,25 +240,6 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(), DrawerAdapter.OnItemS
override fun onDragEnd(isMenuOpened: Boolean) {
ViewUtils.setVisibility(mLLMenu, isMenuOpened)
/*if (isMenuOpened) {
if (!GuideCaseView.isShowOnce(this@MainActivity, getString(R.string.guide_key_sliding_root_navigation))) {
val guideStep1 = GuideCaseView.Builder(this@MainActivity)
.title("点击进入,可切换主题样式哦~~")
.titleSize(18, TypedValue.COMPLEX_UNIT_SP)
.focusOn(ivSetting)
.build()
val guideStep2 = GuideCaseView.Builder(this@MainActivity)
.title("点击进入,扫码关注哦~~")
.titleSize(18, TypedValue.COMPLEX_UNIT_SP)
.focusOn(ivQrcode)
.build()
GuideCaseQueue()
.add(guideStep1)
.add(guideStep2)
.show()
GuideCaseView.setShowOnce(this@MainActivity, getString(R.string.guide_key_sliding_root_navigation))
}
}*/
}
})
}
@ -302,14 +280,26 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(), DrawerAdapter.OnItemS
}
POS_APPS -> {
if (App.UserAppList.isEmpty() && App.SystemAppList.isEmpty()) {
XToastUtils.info(getString(R.string.loading_app_list))
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
WorkManager.getInstance(this).enqueue(request)
needToAppListFragment = true
return
}
openNewPage(AppListFragment::class.java)
//检查读取应用列表权限是否获取
XXPermissions.with(this).permission(Permission.GET_INSTALLED_APPS).request(object : OnPermissionCallback {
override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
if (App.UserAppList.isEmpty() && App.SystemAppList.isEmpty()) {
XToastUtils.info(getString(R.string.loading_app_list))
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
WorkManager.getInstance(getContext()).enqueue(request)
needToAppListFragment = true
return
}
openNewPage(AppListFragment::class.java)
}
override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
XToastUtils.error(R.string.tips_get_installed_apps)
if (doNotAskAgain) {
XXPermissions.startPermissionActivity(getContext(), permissions)
}
}
})
}
POS_HELP -> AgentWebActivity.goWeb(this, getString(R.string.url_help))
@ -389,4 +379,4 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(), DrawerAdapter.OnItemS
}
}
}

View File

@ -1,24 +0,0 @@
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?>?>>
}
}

View File

@ -1,35 +0,0 @@
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("Request Url: $mUrl", e)
} else {
Logger.e(e)
}
}
}

View File

@ -1,37 +0,0 @@
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("Request Url: $mUrl", e)
} else {
Logger.e(e)
}
}
}

View File

@ -1,45 +0,0 @@
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("Request Url: $mUrl", e)
} else {
Logger.e(e)
}
}
}

View File

@ -28,7 +28,7 @@ import com.idormy.sms.forwarder.utils.TAG_LIST
@Database(
entities = [Frpc::class, Msg::class, Logs::class, Rule::class, Sender::class, Task::class],
views = [LogsDetail::class],
version = 19,
version = 20,
exportSchema = false
)
@TypeConverters(ConvertersDate::class)
@ -110,6 +110,7 @@ custom_domains = smsf.demo.com
MIGRATION_16_17,
MIGRATION_17_18,
MIGRATION_18_19,
MIGRATION_19_20,
)
/*if (BuildConfig.DEBUG) {
@ -452,6 +453,13 @@ CREATE TABLE "Task" (
}
}
//免打扰星期段
private val MIGRATION_19_20 = object : Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("Alter table rule add column silent_day_of_week TEXT NOT NULL DEFAULT '' ")
}
}
}
}

View File

@ -54,6 +54,7 @@ data class Rule(
//免打扰(禁用转发)时间段
@ColumnInfo(name = "silent_period_start", defaultValue = "0") var silentPeriodStart: Int = 0,
@ColumnInfo(name = "silent_period_end", defaultValue = "0") var silentPeriodEnd: Int = 0,
@ColumnInfo(name = "silent_day_of_week", defaultValue = "") var silentDayOfWeek: String = "",
) : Parcelable {
companion object {

View File

@ -10,6 +10,7 @@ import com.idormy.sms.forwarder.utils.AppUtils
import com.idormy.sms.forwarder.utils.BatteryUtils
import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.idormy.sms.forwarder.utils.Log
import com.idormy.sms.forwarder.utils.PhoneUtils
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.enableSmsTemplate
import com.idormy.sms.forwarder.utils.SettingUtils.Companion.extraDeviceMark
@ -129,6 +130,8 @@ data class MsgInfo(
)
.replaceAppNameTag(from, encoderName)
.replaceLocationTag(encoderName)
.replaceContactNameTag(encoderName)
.replacePhoneAreaTag(encoderName)
.regexReplace(regexReplace)
.trim()
}
@ -185,6 +188,33 @@ data class MsgInfo(
return result
}
//替换{{CONTACT_NAME}}标签
private fun String.replaceContactNameTag(encoderName: String = ""): String {
if (TextUtils.isEmpty(this)) return this
if (this.indexOf(getString(R.string.tag_contact_name)) == -1) return this
val contacts = PhoneUtils.getContactByNumber(from)
var contactName = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
when (encoderName) {
"Gson" -> contactName = toJsonStr(contactName)
"URLEncoder" -> contactName = URLEncoder.encode(contactName, "UTF-8")
}
return this.replaceTag(getString(R.string.tag_contact_name), contactName)
}
//替换{{PHONE_AREA}}标签
private fun String.replacePhoneAreaTag(encoderName: String = ""): String {
if (TextUtils.isEmpty(this)) return this
if (this.indexOf(getString(R.string.tag_phone_area)) == -1) return this
var phoneArea = PhoneUtils.getPhoneArea(from)
when (encoderName) {
"Gson" -> phoneArea = toJsonStr(phoneArea)
"URLEncoder" -> phoneArea = URLEncoder.encode(phoneArea, "UTF-8")
}
return this.replaceTag(getString(R.string.tag_phone_area), phoneArea)
}
//替换{{APP名称}}标签
private fun String.replaceAppNameTag(packageName: String, encoderName: String = ""): String {
if (TextUtils.isEmpty(this)) return this
@ -256,4 +286,4 @@ data class MsgInfo(
", simInfo=" + simInfo +
'}'
}
}
}

View File

@ -5,7 +5,11 @@ import java.io.Serializable
data class AlarmSetting(
var description: String = "", //描述
var action: String = "stop", //动作: start=启动警报, stop=停止警报
var volume: Int = 80, //播放音量
var playTimes: Int = 1, //播放次数0=无限循环
var volume: Int = 80, //播放音量0-100
var playTimes: Int = 1, //播放次数0=无限循环-1=禁用
var music: String = "", //音乐文件
var repeatTimes: Int = 5, //振动重复次数0=无限循环,-1=禁用
var vibrate: String = "---___===___", //振动律动:=强振动, -弱震动, _不振动, 时长都是100ms
var flashTimes: Int = 5, //闪烁次数0=无限循环,-1=禁用
var flash: String = "XXOOXXOO", //闪烁律动X亮灯, O灭灯, 时长都是100ms
) : Serializable

View File

@ -25,4 +25,8 @@ data class BarkSetting(
val key: String = "",
//初始偏移向量
val iv: String = "",
) : Serializable
//持续提醒
val call: String = "",
//自动复制模板
val autoCopy: String = "",
) : Serializable

View File

@ -14,6 +14,7 @@ data class TelegramSetting(
val proxyAuthenticator: Boolean = false,
val proxyUsername: String = "",
val proxyPassword: String = "",
val parseMode: String = "HTML",
) : Serializable {
fun getMethodCheckId(): Int {
@ -27,4 +28,12 @@ data class TelegramSetting(
else -> R.id.rb_proxyNone
}
}
}
fun getParseModeCheckId(): Int {
return when (parseMode) {
"TEXT" -> R.id.rb_parse_mode_text
"MarkdownV2" -> R.id.rb_parse_mode_markdown
else -> R.id.rb_parse_mode_html
}
}
}

View File

@ -9,6 +9,9 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.AppListAdapter
@ -24,11 +27,11 @@ import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xui.XUI
import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.resource.ResUtils.getStringArray
@Suppress("PrivatePropertyName", "DEPRECATION")
@ -91,10 +94,10 @@ class AppListFragment : BaseFragment<FragmentAppListBinding?>() {
}
override fun onRefresh(refreshLayout: RefreshLayout) {
appListAdapter?.refresh(getAppsList(true))
refreshLayout.layout.postDelayed({
appListAdapter?.refresh(getAppsList(true))
refreshLayout.finishRefresh()
}, 3000)
}, 1000)
}
})
appListAdapter?.setOnItemClickListener { _, item, _ ->
@ -120,12 +123,24 @@ class AppListFragment : BaseFragment<FragmentAppListBinding?>() {
private fun getAppsList(refresh: Boolean): MutableList<AppInfo> {
if (refresh || (currentType == "user" && App.UserAppList.isEmpty()) || (currentType == "system" && App.SystemAppList.isEmpty())) {
XToastUtils.info(getString(R.string.loading_app_list))
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
WorkManager.getInstance(XUtil.getContext()).enqueue(request)
//检查读取应用列表权限是否获取
XXPermissions.with(this).permission(Permission.GET_INSTALLED_APPS).request(object : OnPermissionCallback {
override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
XToastUtils.info(getString(R.string.loading_app_list))
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
WorkManager.getInstance(XUI.getContext()).enqueue(request)
}
override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
XToastUtils.error(R.string.tips_get_installed_apps)
if (doNotAskAgain) {
XXPermissions.startPermissionActivity(XUI.getContext(), permissions)
}
}
})
}
return if (currentType == "system") App.SystemAppList else App.UserAppList
}
}
}

View File

@ -87,6 +87,7 @@ import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.Calendar
import java.util.Date
@Page(name = "转发规则·编辑器")
@ -165,9 +166,6 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.rbCallType.visibility = View.GONE
binding!!.rbContent.visibility = View.GONE
binding!!.tvMuRuleTips.setText(R.string.mu_rule_app_tips)
binding!!.btInsertExtra.visibility = View.GONE
binding!!.btInsertSender.visibility = View.GONE
binding!!.btInsertContent.visibility = View.GONE
//初始化APP下拉列表
initAppSpinner()
//监听已安装App信息列表加载完成事件
@ -182,11 +180,6 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.rbInformContent.visibility = View.GONE
//binding!!.rbMultiMatch.visibility = View.GONE
binding!!.tvMuRuleTips.setText(R.string.mu_rule_call_tips)
binding!!.btInsertContent.visibility = View.GONE
binding!!.btInsertSenderApp.visibility = View.GONE
binding!!.btInsertUid.visibility = View.GONE
binding!!.btInsertTitleApp.visibility = View.GONE
binding!!.btInsertContentApp.visibility = View.GONE
//通话类型1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
binding!!.spCallType.setItems(CALL_TYPE_MAP.values.toList())
@ -209,13 +202,12 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
binding!!.rbPackageName.visibility = View.GONE
binding!!.rbUid.visibility = View.GONE
binding!!.rbInformContent.visibility = View.GONE
binding!!.btInsertSenderApp.visibility = View.GONE
binding!!.btInsertUid.visibility = View.GONE
binding!!.btInsertTitleApp.visibility = View.GONE
binding!!.btInsertContentApp.visibility = View.GONE
}
}
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glSmsTemplate, binding!!.etSmsTemplate, ruleType)
if (ruleId <= 0) { //新增
titleBar?.setSubTitle(getString(R.string.add_rule))
binding!!.btnDel.setText(R.string.discard)
@ -229,15 +221,6 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
override fun initListeners() {
binding!!.btnSilentPeriod.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertSenderApp.setOnClickListener(this)
binding!!.btInsertUid.setOnClickListener(this)
binding!!.btInsertTitleApp.setOnClickListener(this)
binding!!.btInsertContentApp.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -333,7 +316,6 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
@SingleClick
override fun onClick(v: View) {
try {
val etSmsTemplate: EditText = binding!!.etSmsTemplate
when (v.id) {
R.id.btn_silent_period -> {
OptionsPickerBuilder(context, OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
@ -349,51 +331,6 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_sms))
return
}
R.id.bt_insert_sender_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_package_name))
return
}
R.id.bt_insert_uid -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_uid))
return
}
R.id.bt_insert_title_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_title))
return
}
R.id.bt_insert_content_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_msg))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
val ruleNew = checkForm()
testRule(ruleNew)
@ -639,6 +576,21 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
silentPeriodEnd = rule.silentPeriodEnd
//初始化发送通道下拉框
initSenderSpinner()
//绑定免打扰日期
val silentPeriodDays = rule.silentDayOfWeek.split(",").filter { it.isNotEmpty() }.map { it.toInt() }
if (silentPeriodDays.isNotEmpty()) {
val map = mapOf(
Calendar.SUNDAY to binding!!.sun,
Calendar.MONDAY to binding!!.mon,
Calendar.TUESDAY to binding!!.tue,
Calendar.WEDNESDAY to binding!!.wed,
Calendar.THURSDAY to binding!!.thu,
Calendar.FRIDAY to binding!!.fri,
Calendar.SATURDAY to binding!!.sat,
)
silentPeriodDays.forEach { map[it]?.isChecked = true }
}
}
})
}
@ -715,6 +667,19 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF
val map = mapOf(
Calendar.SUNDAY to binding!!.sun,
Calendar.MONDAY to binding!!.mon,
Calendar.TUESDAY to binding!!.tue,
Calendar.WEDNESDAY to binding!!.wed,
Calendar.THURSDAY to binding!!.thu,
Calendar.FRIDAY to binding!!.fri,
Calendar.SATURDAY to binding!!.sat,
)
val silentDayOfWeek = map.filter { it.value.isChecked }
.toList().map { it.first }.joinToString(",")
return Rule(
ruleId,
ruleType,
@ -730,7 +695,8 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
senderListSelected,
senderLogic,
silentPeriodStart,
silentPeriodEnd
silentPeriodEnd,
silentDayOfWeek,
)
}
@ -852,7 +818,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
val contactName = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
msg.append(getString(R.string.contact)).append(contactName).append("\n")
msg.append(getString(R.string.mandatory_type))
msg.append(CALL_TYPE_MAP[callType.toString()] ?: getString(R.string.unknown_call))
msg.append(CALL_TYPE_MAP[callTypeTest.toString()] ?: getString(R.string.unknown_call))
} else {
msg.append(etContent.text.toString())
}
@ -877,4 +843,4 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
}.show()
}
}
}

View File

@ -180,12 +180,18 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
editAddExtraDeviceMark(binding!!.etExtraDeviceMark)
//SIM1主键
editAddSubidSim1(binding!!.etSubidSim1)
//SIM2主键
editAddSubidSim2(binding!!.etSubidSim2)
//SIM1备注
editAddExtraSim1(binding!!.etExtraSim1)
//SIM2备注
editAddExtraSim2(binding!!.etExtraSim2)
// sim 槽只有一个的时候不显示 SIM2 设置
if (PhoneUtils.getSimSlotCount() != 1) {
//SIM2主键
editAddSubidSim2(binding!!.etSubidSim2)
//SIM2备注
editAddExtraSim2(binding!!.etExtraSim2)
} else {
binding!!.layoutSim2.visibility = View.GONE
}
//通知内容
editNotifyContent(binding!!.etNotifyContent)
//启用自定义模版
@ -215,11 +221,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
binding!!.btnExtraDeviceMark.setOnClickListener(this)
binding!!.btnExtraSim1.setOnClickListener(this)
binding!!.btnExtraSim2.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnExportLog.setOnClickListener(this)
//监听已安装App信息列表加载完成事件
@ -229,7 +230,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
@SuppressLint("SetTextI18n")
@SingleClick
override fun onClick(v: View) {
val etSmsTemplate: EditText = binding!!.etSmsTemplate
when (v.id) {
R.id.btn_silent_period -> {
OptionsPickerBuilder(context, OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
@ -298,37 +298,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
return
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_sms))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(
etSmsTemplate, getString(R.string.tag_card_slot)
)
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(
etSmsTemplate, getString(R.string.tag_receive_time)
)
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(
etSmsTemplate, getString(R.string.tag_device_name)
)
return
}
R.id.btn_export_log -> {
XXPermissions.with(this)
// 申请储存权限
@ -991,7 +960,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
//设置SIM1主键
private fun editAddSubidSim1(etSubidSim1: EditText) {
etSubidSim1.setText(SettingUtils.subidSim1.toString())
etSubidSim1.setText("${SettingUtils.subidSim1}")
etSubidSim1.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
@ -1008,7 +977,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
//设置SIM2主键
private fun editAddSubidSim2(etSubidSim2: EditText) {
etSubidSim2.setText(SettingUtils.subidSim2.toString())
etSubidSim2.setText("${SettingUtils.subidSim2}")
etSubidSim2.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
@ -1092,6 +1061,8 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
//设置转发信息模版
private fun editSmsTemplate(textSmsTemplate: EditText) {
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glSmsTemplate, textSmsTemplate, "all")
textSmsTemplate.setText(SettingUtils.smsTemplate)
textSmsTemplate.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
@ -1206,6 +1177,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
"huawei" -> getString(R.string.auto_start_huawei)
"honor" -> getString(R.string.auto_start_honor)
"xiaomi" -> getString(R.string.auto_start_xiaomi)
"redmi" -> getString(R.string.auto_start_redmi)
"oppo" -> getString(R.string.auto_start_oppo)
"vivo" -> getString(R.string.auto_start_vivo)
"meizu" -> getString(R.string.auto_start_meizu)

View File

@ -20,6 +20,7 @@ import com.idormy.sms.forwarder.databinding.FragmentTasksActionAlarmBinding
import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.entity.TaskSetting
import com.idormy.sms.forwarder.entity.action.AlarmSetting
import com.idormy.sms.forwarder.utils.CommonUtils
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
@ -37,6 +38,7 @@ import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import java.io.File
import java.util.Date
import java.util.Locale
@Page(name = "Alarm")
@Suppress("PrivatePropertyName", "DEPRECATION")
@ -84,24 +86,45 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
}
})
binding!!.sbEnableMusic.setOnCheckedChangeListener { _, isChecked ->
binding!!.layoutAlarmSettingsContent.visibility = if (isChecked) View.VISIBLE else View.GONE
checkSetting(true)
}
binding!!.sbEnableVibrate.setOnCheckedChangeListener { _, isChecked ->
binding!!.layoutVibrateSettingsContent.visibility = if (isChecked) View.VISIBLE else View.GONE
checkSetting(true)
}
var settingVo = AlarmSetting()
Log.d(TAG, "initViews eventData:$eventData")
if (eventData != null) {
val settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
Log.d(TAG, "initViews settingVo:$settingVo")
if (settingVo.action == "start") {
binding!!.rgAlarmState.check(R.id.rb_start_alarm)
binding!!.layoutAlarmSettings.visibility = View.VISIBLE
binding!!.layoutVibrateSettings.visibility = View.VISIBLE
binding!!.layoutFlashSettings.visibility = View.VISIBLE
} else {
binding!!.rgAlarmState.check(R.id.rb_stop_alarm)
binding!!.layoutAlarmSettings.visibility = View.GONE
binding!!.layoutVibrateSettings.visibility = View.GONE
binding!!.layoutFlashSettings.visibility = View.GONE
}
binding!!.xsbVolume.setDefaultValue(settingVo.volume)
binding!!.xsbLoopTimes.setDefaultValue(settingVo.playTimes)
binding!!.etMusicPath.setText(settingVo.music)
} else {
binding!!.xsbVolume.setDefaultValue(80)
binding!!.xsbLoopTimes.setDefaultValue(1)
}
binding!!.xsbVolume.setDefaultValue(settingVo.volume)
binding!!.xsbPlayTimes.setDefaultValue(if (settingVo.playTimes >= 0) settingVo.playTimes else 0)
binding!!.etMusicPath.setText(settingVo.music)
binding!!.xsbRepeatTimes.setDefaultValue(if (settingVo.repeatTimes >= 0) settingVo.repeatTimes else 0)
binding!!.etVibrationEffect.setText(settingVo.vibrate)
binding!!.xsbFlashTimes.setDefaultValue(if (settingVo.flashTimes >= 0) settingVo.flashTimes else 0)
binding!!.etFlashEffect.setText(settingVo.flash)
binding!!.sbEnableMusic.isChecked = settingVo.playTimes >= 0
binding!!.sbEnableVibrate.isChecked = settingVo.repeatTimes >= 0
binding!!.sbEnableFlash.isChecked = settingVo.flashTimes >= 0
binding!!.layoutAlarmSettingsContent.visibility = if (settingVo.playTimes >= 0) View.VISIBLE else View.GONE
binding!!.layoutVibrateSettingsContent.visibility = if (settingVo.repeatTimes >= 0) View.VISIBLE else View.GONE
binding!!.layoutFlashSettingsContent.visibility = if (settingVo.flashTimes >= 0) View.VISIBLE else View.GONE
}
override fun onDestroyView() {
@ -118,19 +141,53 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
binding!!.xsbVolume.setOnSeekBarListener { _, _ ->
checkSetting(true)
}
binding!!.xsbLoopTimes.setOnSeekBarListener { _, _ ->
binding!!.xsbPlayTimes.setOnSeekBarListener { _, _ ->
checkSetting(true)
}
binding!!.xsbRepeatTimes.setOnSeekBarListener { _, _ ->
checkSetting(true)
}
binding!!.rgAlarmState.setOnCheckedChangeListener { _, checkedId ->
binding!!.layoutAlarmSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
binding!!.layoutVibrateSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
binding!!.layoutFlashSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
checkSetting(true)
}
binding!!.btInsertVibrationEffect1.setOnClickListener(this)
binding!!.btInsertVibrationEffect2.setOnClickListener(this)
binding!!.btInsertVibrationEffect3.setOnClickListener(this)
binding!!.btInsertFlashEffect1.setOnClickListener(this)
binding!!.btInsertFlashEffect2.setOnClickListener(this)
}
@SingleClick
override fun onClick(v: View) {
try {
when (v.id) {
R.id.bt_insert_vibration_effect_1 -> {
CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "=")
return
}
R.id.bt_insert_vibration_effect_2 -> {
CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "-")
return
}
R.id.bt_insert_vibration_effect_3 -> {
CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "_")
return
}
R.id.bt_insert_flash_effect_1 -> {
CommonUtils.insertOrReplaceText2Cursor(binding!!.etFlashEffect, "X")
return
}
R.id.bt_insert_flash_effect_2 -> {
CommonUtils.insertOrReplaceText2Cursor(binding!!.etFlashEffect, "O")
return
}
R.id.btn_file_picker -> {
// 申请储存权限
@ -165,14 +222,23 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
}
R.id.btn_test -> {
val permissions = arrayListOf<String>()
permissions.add(Permission.WRITE_SETTINGS)
if (binding!!.sbEnableFlash.isChecked) {
permissions.add(Permission.CAMERA)
}
// 申请修改系统设置权限
XXPermissions.with(this).permission(Permission.WRITE_SETTINGS).request(object : OnPermissionCallback {
XXPermissions.with(this).permission(permissions).request(object : OnPermissionCallback {
@SuppressLint("SetTextI18n")
override fun onGranted(permissions: List<String>, all: Boolean) {
mCountDownHelper?.start()
try {
val settingVo = checkSetting()
Log.d(TAG, settingVo.toString())
if (settingVo.playTimes < 0 && settingVo.repeatTimes < 0 && settingVo.flashTimes < 0) {
XToastUtils.error(getString(R.string.alarm_settings_error))
return
}
val taskAction = TaskSetting(TASK_ACTION_ALARM, getString(R.string.task_alarm), settingVo.description, Gson().toJson(settingVo), requestCode)
val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
val msgInfo = MsgInfo("task", getString(R.string.task_alarm), settingVo.description, Date(), getString(R.string.task_alarm))
@ -207,11 +273,20 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
}
R.id.btn_save -> {
val permissions = arrayListOf<String>()
permissions.add(Permission.WRITE_SETTINGS)
if (binding!!.sbEnableFlash.isChecked) {
permissions.add(Permission.CAMERA)
}
// 申请修改系统设置权限
XXPermissions.with(this).permission(Permission.WRITE_SETTINGS).request(object : OnPermissionCallback {
XXPermissions.with(this).permission(permissions).request(object : OnPermissionCallback {
@SuppressLint("SetTextI18n")
override fun onGranted(permissions: List<String>, all: Boolean) {
val settingVo = checkSetting()
if (settingVo.playTimes < 0 && settingVo.repeatTimes < 0 && settingVo.flashTimes < 0) {
XToastUtils.error(getString(R.string.alarm_settings_error))
return
}
val intent = Intent()
intent.putExtra(KEY_BACK_DESCRIPTION_ACTION, settingVo.description)
intent.putExtra(KEY_BACK_DATA_ACTION, Gson().toJson(settingVo))
@ -244,16 +319,42 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
@Suppress("SameParameterValue")
@SuppressLint("SetTextI18n")
private fun checkSetting(updateView: Boolean = false): AlarmSetting {
val enableMusic = binding!!.sbEnableMusic.isChecked
val enableVibrate = binding!!.sbEnableVibrate.isChecked
val enableFlash = binding!!.sbEnableFlash.isChecked
val volume = binding!!.xsbVolume.selectedNumber
val loopTimes = binding!!.xsbLoopTimes.selectedNumber
var playTimes = binding!!.xsbPlayTimes.selectedNumber
val music = binding!!.etMusicPath.text.toString().trim()
var repeatTimes = binding!!.xsbRepeatTimes.selectedNumber
val vibrationEffect = binding!!.etVibrationEffect.text.toString().trim()
var flashTimes = binding!!.xsbFlashTimes.selectedNumber
var flashEffect = binding!!.etFlashEffect.text.toString().trim()
val description = StringBuilder()
val action = if (binding!!.rgAlarmState.checkedRadioButtonId == R.id.rb_start_alarm) {
description.append(getString(R.string.start_alarm))
description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(loopTimes)
if (music.isNotEmpty()) {
description.append(", ").append(getString(R.string.alarm_music)).append(":").append(music)
if (enableMusic) {
description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(playTimes)
if (music.isNotEmpty()) {
description.append(", ").append(getString(R.string.alarm_music)).append(":").append(music)
}
} else {
playTimes = -1
}
if (enableVibrate) {
vibrationEffect.ifEmpty { "---___===___".also { binding!!.etVibrationEffect.setText(it) } }
description.append(", ").append(getString(R.string.alarm_vibration_effect)).append(":").append(vibrationEffect)
description.append(", ").append(getString(R.string.alarm_repeat_times)).append(":").append(repeatTimes)
} else {
repeatTimes = -1
}
if (enableFlash) {
flashEffect.ifEmpty { "XXOOXXOO".also { binding!!.etFlashEffect.setText(it) } }
flashEffect = flashEffect.toUpperCase(Locale.ROOT).replace("1", "X").replace("0", "O")
description.append(", ").append(getString(R.string.alarm_flash_effect)).append(":").append(flashEffect)
description.append(", ").append(getString(R.string.alarm_repeat_times)).append(":").append(flashTimes)
} else {
flashTimes = -1
}
"start"
} else {
@ -265,7 +366,7 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
binding!!.tvDescription.text = description.toString()
}
return AlarmSetting(description.toString(), action, volume, loopTimes, music)
return AlarmSetting(description.toString(), action, volume, playTimes, music, repeatTimes, vibrationEffect, flashTimes, flashEffect)
}
private fun findAudioFiles(directoryPath: String): List<String> {
@ -286,4 +387,4 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
val supportedExtensions = listOf("mp3", "ogg", "wav")
return supportedExtensions.any { it.equals(file.extension, ignoreCase = true) }
}
}
}

View File

@ -7,7 +7,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.CompoundButton
import android.widget.EditText
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -156,6 +155,9 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
//初始化发送通道下拉框
initSenderSpinner()
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glSmsTemplate, binding!!.etSmsTemplate, ruleType)
}
override fun onDestroyView() {
@ -165,15 +167,6 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
override fun initListeners() {
binding!!.btnSilentPeriod.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertSenderApp.setOnClickListener(this)
binding!!.btInsertUid.setOnClickListener(this)
binding!!.btInsertTitleApp.setOnClickListener(this)
binding!!.btInsertContentApp.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -213,7 +206,6 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
@SingleClick
override fun onClick(v: View) {
try {
val etSmsTemplate: EditText = binding!!.etSmsTemplate
when (v.id) {
R.id.btn_silent_period -> {
OptionsPickerBuilder(context, OnOptionsSelectListener { _: View?, options1: Int, options2: Int, _: Int ->
@ -229,51 +221,6 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
}
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_sms))
return
}
R.id.bt_insert_sender_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_package_name))
return
}
R.id.bt_insert_uid -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_uid))
return
}
R.id.bt_insert_title_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_title))
return
}
R.id.bt_insert_content_app -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_msg))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etSmsTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
try {
@ -506,4 +453,4 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
return 0
}
}
}

View File

@ -4,7 +4,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.idormy.sms.forwarder.App.Companion.BARK_ENCRYPTION_ALGORITHM_MAP
@ -124,6 +123,10 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
}
binding!!.spEncryptionAlgorithm.selectedIndex = 0
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
CommonUtils.createTagButtons(requireContext(), binding!!.glAutoCopyTemplate, binding!!.etAutoCopyTemplate)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -156,6 +159,7 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
binding!!.etServer.setText(settingVo.server)
binding!!.etGroup.setText(settingVo.group)
binding!!.etIcon.setText(settingVo.icon)
binding!!.sbCall.isChecked = settingVo.call == "1"
binding!!.etSound.setText(settingVo.sound)
binding!!.etBadge.setText(settingVo.badge)
binding!!.etUrl.setText(settingVo.url)
@ -178,10 +182,6 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
}
override fun initListeners() {
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -191,27 +191,7 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -281,6 +261,7 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
if (!CommonUtils.checkUrl(icon, true)) {
throw Exception(getString(R.string.invalid_bark_icon))
}
val call = if (binding!!.sbCall.isChecked) "1" else "0"
val sound = binding!!.etSound.text.toString().trim()
val badge = binding!!.etBadge.text.toString().trim()
val url = binding!!.etUrl.text.toString().trim()
@ -288,6 +269,7 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
throw Exception(getString(R.string.invalid_bark_url))
}
val title = binding!!.etTitleTemplate.text.toString().trim()
val autoCopy = binding!!.etAutoCopyTemplate.text.toString().trim()
val key = binding!!.etEncryptionKey.text.toString().trim()
val iv = binding!!.etEncryptionIv.text.toString().trim()
if (transformation.startsWith("AES128") && key.length != 16) {
@ -301,7 +283,7 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
throw Exception(getString(R.string.bark_encryption_key_error4))
}
return BarkSetting(server, group, icon, sound, badge, url, barkLevel, title, transformation, key, iv)
return BarkSetting(server, group, icon, sound, badge, url, barkLevel, title, transformation, key, iv, call, autoCopy)
}
override fun onDestroyView() {
@ -309,4 +291,4 @@ class BarkFragment : BaseFragment<FragmentSendersBarkBinding?>(), View.OnClickLi
super.onDestroyView()
}
}
}

View File

@ -5,7 +5,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.RadioGroup
import androidx.fragment.app.viewModels
import com.google.gson.Gson
@ -96,6 +95,9 @@ class DingtalkGroupRobotFragment : BaseFragment<FragmentSendersDingtalkGroupRobo
}
})
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -138,10 +140,6 @@ class DingtalkGroupRobotFragment : BaseFragment<FragmentSendersDingtalkGroupRobo
}
override fun initListeners() {
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -168,27 +166,7 @@ class DingtalkGroupRobotFragment : BaseFragment<FragmentSendersDingtalkGroupRobo
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -269,4 +247,4 @@ class DingtalkGroupRobotFragment : BaseFragment<FragmentSendersDingtalkGroupRobo
super.onDestroyView()
}
}
}

View File

@ -6,7 +6,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.RadioGroup
import androidx.fragment.app.viewModels
import com.google.gson.Gson
@ -19,7 +18,15 @@ import com.idormy.sms.forwarder.database.viewmodel.SenderViewModel
import com.idormy.sms.forwarder.databinding.FragmentSendersDingtalkInnerRobotBinding
import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.entity.setting.DingtalkInnerRobotSetting
import com.idormy.sms.forwarder.utils.*
import com.idormy.sms.forwarder.utils.CommonUtils
import com.idormy.sms.forwarder.utils.EVENT_TOAST_ERROR
import com.idormy.sms.forwarder.utils.KEY_SENDER_CLONE
import com.idormy.sms.forwarder.utils.KEY_SENDER_ID
import com.idormy.sms.forwarder.utils.KEY_SENDER_TEST
import com.idormy.sms.forwarder.utils.KEY_SENDER_TYPE
import com.idormy.sms.forwarder.utils.Log
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.XToastUtils
import com.idormy.sms.forwarder.utils.sender.DingtalkInnerRobotUtils
import com.jeremyliao.liveeventbus.LiveEventBus
import com.xuexiang.xaop.annotation.SingleClick
@ -35,7 +42,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.net.Proxy
import java.util.*
import java.util.Date
@Page(name = "钉钉企业机器人")
@Suppress("PrivatePropertyName")
@ -90,6 +97,9 @@ class DingtalkInnerRobotFragment : BaseFragment<FragmentSendersDingtalkInnerRobo
}
})
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -137,10 +147,6 @@ class DingtalkInnerRobotFragment : BaseFragment<FragmentSendersDingtalkInnerRobo
}
override fun initListeners() {
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -176,27 +182,7 @@ class DingtalkInnerRobotFragment : BaseFragment<FragmentSendersDingtalkInnerRobo
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -295,4 +281,4 @@ class DingtalkInnerRobotFragment : BaseFragment<FragmentSendersDingtalkInnerRobo
super.onDestroyView()
}
}
}

View File

@ -166,6 +166,10 @@ class EmailFragment : BaseFragment<FragmentSendersEmailBinding?>(), View.OnClick
}
}
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
CommonUtils.createTagButtons(requireContext(), binding!!.glNickname, binding!!.etNickname)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -237,14 +241,6 @@ class EmailFragment : BaseFragment<FragmentSendersEmailBinding?>(), View.OnClick
}
override fun initListeners() {
binding!!.btInsertSenderToNickname.setOnClickListener(this)
binding!!.btInsertExtraToNickname.setOnClickListener(this)
binding!!.btInsertTimeToNickname.setOnClickListener(this)
binding!!.btInsertDeviceNameToNickname.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -260,48 +256,7 @@ class EmailFragment : BaseFragment<FragmentSendersEmailBinding?>(), View.OnClick
@SingleClick
override fun onClick(v: View) {
try {
val etNickname: EditText = binding!!.etNickname
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender_to_nickname -> {
CommonUtils.insertOrReplaceText2Cursor(etNickname, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra_to_nickname -> {
CommonUtils.insertOrReplaceText2Cursor(etNickname, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time_to_nickname -> {
CommonUtils.insertOrReplaceText2Cursor(etNickname, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name_to_nickname -> {
CommonUtils.insertOrReplaceText2Cursor(etNickname, getString(R.string.tag_device_name))
return
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -653,4 +608,4 @@ class EmailFragment : BaseFragment<FragmentSendersEmailBinding?>(), View.OnClick
super.onDestroyView()
}
}
}

View File

@ -4,7 +4,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.google.gson.JsonParser
@ -105,6 +104,10 @@ class FeishuAppFragment : BaseFragment<FragmentSendersFeishuAppBinding?>(), View
}
}
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
CommonUtils.createTagButtons(requireContext(), binding!!.glMessageCard, binding!!.etMessageCard)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -150,15 +153,6 @@ class FeishuAppFragment : BaseFragment<FragmentSendersFeishuAppBinding?>(), View
}
override fun initListeners() {
binding!!.btInsertSenderToTitle.setOnClickListener(this)
binding!!.btInsertExtraToTitle.setOnClickListener(this)
binding!!.btInsertTimeToTitle.setOnClickListener(this)
binding!!.btInsertDeviceNameToTitle.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -168,53 +162,7 @@ class FeishuAppFragment : BaseFragment<FragmentSendersFeishuAppBinding?>(), View
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
val etMessageCard: EditText = binding!!.etMessageCard
when (v.id) {
R.id.bt_insert_sender_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_sms))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -315,4 +263,4 @@ class FeishuAppFragment : BaseFragment<FragmentSendersFeishuAppBinding?>(), View
super.onDestroyView()
}
}
}

View File

@ -4,7 +4,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.google.gson.JsonParser
@ -105,6 +104,10 @@ class FeishuFragment : BaseFragment<FragmentSendersFeishuBinding?>(), View.OnCli
}
}
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
CommonUtils.createTagButtons(requireContext(), binding!!.glMessageCard, binding!!.etMessageCard)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -145,15 +148,6 @@ class FeishuFragment : BaseFragment<FragmentSendersFeishuBinding?>(), View.OnCli
}
override fun initListeners() {
binding!!.btInsertSenderToTitle.setOnClickListener(this)
binding!!.btInsertExtraToTitle.setOnClickListener(this)
binding!!.btInsertTimeToTitle.setOnClickListener(this)
binding!!.btInsertDeviceNameToTitle.setOnClickListener(this)
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertContent.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -163,53 +157,7 @@ class FeishuFragment : BaseFragment<FragmentSendersFeishuBinding?>(), View.OnCli
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
val etMessageCard: EditText = binding!!.etMessageCard
when (v.id) {
R.id.bt_insert_sender_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name_to_title -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_from))
return
}
R.id.bt_insert_content -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_sms))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etMessageCard, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -295,4 +243,4 @@ class FeishuFragment : BaseFragment<FragmentSendersFeishuBinding?>(), View.OnCli
super.onDestroyView()
}
}
}

View File

@ -4,7 +4,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
@ -94,6 +93,9 @@ class GotifyFragment : BaseFragment<FragmentSendersGotifyBinding?>(), View.OnCli
}
})
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -132,10 +134,6 @@ class GotifyFragment : BaseFragment<FragmentSendersGotifyBinding?>(), View.OnCli
}
override fun initListeners() {
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -145,27 +143,7 @@ class GotifyFragment : BaseFragment<FragmentSendersGotifyBinding?>(), View.OnCli
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -242,4 +220,4 @@ class GotifyFragment : BaseFragment<FragmentSendersGotifyBinding?>(), View.OnCli
super.onDestroyView()
}
}
}

View File

@ -4,7 +4,6 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.RadioGroup
import androidx.fragment.app.viewModels
import com.google.gson.Gson
@ -95,6 +94,9 @@ class PushplusFragment : BaseFragment<FragmentSendersPushplusBinding?>(), View.O
}
})
//创建标签按钮
CommonUtils.createTagButtons(requireContext(), binding!!.glTitleTemplate, binding!!.etTitleTemplate)
//新增
if (senderId <= 0) {
titleBar?.setSubTitle(getString(R.string.add_sender))
@ -143,10 +145,6 @@ class PushplusFragment : BaseFragment<FragmentSendersPushplusBinding?>(), View.O
}
override fun initListeners() {
binding!!.btInsertSender.setOnClickListener(this)
binding!!.btInsertExtra.setOnClickListener(this)
binding!!.btInsertTime.setOnClickListener(this)
binding!!.btInsertDeviceName.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
@ -165,27 +163,7 @@ class PushplusFragment : BaseFragment<FragmentSendersPushplusBinding?>(), View.O
@SingleClick
override fun onClick(v: View) {
try {
val etTitleTemplate: EditText = binding!!.etTitleTemplate
when (v.id) {
R.id.bt_insert_sender -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_from))
return
}
R.id.bt_insert_extra -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_card_slot))
return
}
R.id.bt_insert_time -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_receive_time))
return
}
R.id.bt_insert_device_name -> {
CommonUtils.insertOrReplaceText2Cursor(etTitleTemplate, getString(R.string.tag_device_name))
return
}
R.id.btn_test -> {
mCountDownHelper?.start()
@ -272,4 +250,4 @@ class PushplusFragment : BaseFragment<FragmentSendersPushplusBinding?>(), View.O
super.onDestroyView()
}
}
}

View File

@ -226,4 +226,4 @@ class ServerchanFragment : BaseFragment<FragmentSendersServerchanBinding?>(), Vi
super.onDestroyView()
}
}
}

View File

@ -251,4 +251,4 @@ class SmsFragment : BaseFragment<FragmentSendersSmsBinding?>(), View.OnClickList
super.onDestroyView()
}
}
}

View File

@ -262,4 +262,4 @@ class SocketFragment : BaseFragment<FragmentSendersSocketBinding?>(), View.OnCli
super.onDestroyView()
}
}
}

View File

@ -133,6 +133,7 @@ class TelegramFragment : BaseFragment<FragmentSendersTelegramBinding?>(), View.O
binding!!.sbProxyAuthenticator.isChecked = settingVo.proxyAuthenticator == true
binding!!.etProxyUsername.setText(settingVo.proxyUsername)
binding!!.etProxyPassword.setText(settingVo.proxyPassword)
binding!!.rgParseMode.check(settingVo.getParseModeCheckId())
}
}
})
@ -251,8 +252,13 @@ class TelegramFragment : BaseFragment<FragmentSendersTelegramBinding?>(), View.O
}
val method = if (binding!!.rgMethod.checkedRadioButtonId == R.id.rb_method_get) "GET" else "POST"
val parseMode = when (binding!!.rgParseMode.checkedRadioButtonId) {
R.id.rb_parse_mode_text -> "TEXT"
R.id.rb_parse_mode_markdown -> "MarkdownV2"
else -> "HTML"
}
return TelegramSetting(method, apiToken, chatId, proxyType, proxyHost, proxyPort, proxyAuthenticator, proxyUsername, proxyPassword)
return TelegramSetting(method, apiToken, chatId, proxyType, proxyHost, proxyPort, proxyAuthenticator, proxyUsername, proxyPassword, parseMode)
}
override fun onDestroyView() {
@ -260,4 +266,4 @@ class TelegramFragment : BaseFragment<FragmentSendersTelegramBinding?>(), View.O
super.onDestroyView()
}
}
}

View File

@ -223,4 +223,4 @@ class UrlSchemeFragment : BaseFragment<FragmentSendersUrlSchemeBinding?>(), View
super.onDestroyView()
}
}
}

View File

@ -336,4 +336,4 @@ class WebhookFragment : BaseFragment<FragmentSendersWebhookBinding?>(), View.OnC
super.onDestroyView()
}
}
}

View File

@ -307,4 +307,4 @@ class WeworkAgentFragment : BaseFragment<FragmentSendersWeworkAgentBinding?>(),
super.onDestroyView()
}
}
}

View File

@ -238,4 +238,4 @@ class WeworkRobotFragment : BaseFragment<FragmentSendersWeworkRobotBinding?>(),
super.onDestroyView()
}
}
}

View File

@ -36,10 +36,12 @@ import com.idormy.sms.forwarder.utils.EXTRA_UPDATE_NOTIFICATION
import com.idormy.sms.forwarder.utils.FRONT_CHANNEL_ID
import com.idormy.sms.forwarder.utils.FRONT_CHANNEL_NAME
import com.idormy.sms.forwarder.utils.FRONT_NOTIFY_ID
import com.idormy.sms.forwarder.utils.FlashUtils
import com.idormy.sms.forwarder.utils.INTENT_FRPC_APPLY_FILE
import com.idormy.sms.forwarder.utils.Log
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
import com.idormy.sms.forwarder.utils.VibrationUtils
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
import com.idormy.sms.forwarder.workers.LoadAppListWorker
import com.jeremyliao.liveeventbus.LiveEventBus
@ -92,77 +94,108 @@ class ForegroundService : Service() {
})
}
// 振动控制
private lateinit var vibrationUtils: VibrationUtils
private var isVibrating = false
// 闪光灯控制
private lateinit var flashUtils: FlashUtils
private var isFlash = false
// 音乐播放器
private var alarmPlayer: MediaPlayer? = null
private var alarmPlayTimes = 0
private val alarmObserver = Observer<AlarmSetting> { alarm ->
Log.d(TAG, "Received alarm: $alarm")
//停止振动
if (vibrationUtils.isVibrating) {
vibrationUtils.stopVibration()
}
//停止闪光灯
if (flashUtils.isFlashing) {
flashUtils.stopFlashing()
}
//停止播放音乐
alarmPlayer?.release()
alarmPlayer = null
if (alarm.action == "start") {
//获取音量
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
Log.d(TAG, "maxVolume=$maxVolume, currentVolume=$currentVolume")
//设置音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (maxVolume * alarm.volume / 100), 0)
//播放音乐
alarmPlayer = MediaPlayer().apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
setAudioAttributes(audioAttributes)
} else {
// 对于 Android 5.0 之前的版本,使用 setAudioStreamType
val audioStreamType = AudioManager.STREAM_ALARM
setAudioStreamType(audioStreamType)
}
try {
if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
val fd = resources.openRawResourceFd(R.raw.alarm)
setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
if (alarm.playTimes >= 0) {
//获取音量
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
Log.d(TAG, "maxVolume=$maxVolume, currentVolume=$currentVolume")
//设置音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (maxVolume * alarm.volume / 100), 0)
//播放音乐
alarmPlayer = MediaPlayer().apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
setAudioAttributes(audioAttributes)
} else {
setDataSource(alarm.music)
// 对于 Android 5.0 之前的版本,使用 setAudioStreamType
val audioStreamType = AudioManager.STREAM_ALARM
setAudioStreamType(audioStreamType)
}
setOnPreparedListener {
Log.d(TAG, "MediaPlayer prepared")
start()
alarmPlayTimes++
//更新通知栏
updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
}
try {
if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
val fd = resources.openRawResourceFd(R.raw.alarm)
setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
} else {
setDataSource(alarm.music)
}
setOnCompletionListener {
Log.d(TAG, "MediaPlayer completed")
if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
setOnPreparedListener {
Log.d(TAG, "MediaPlayer prepared")
start()
alarmPlayTimes++
} else {
stop()
reset()
release()
alarmPlayer = null
alarmPlayTimes = 0
//恢复音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
//恢复通知栏
updateNotification(SettingUtils.notifyContent)
//更新通知栏
updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
}
}
setOnErrorListener { _, what, extra ->
Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
release()
return@setOnErrorListener true
}
setOnCompletionListener {
Log.d(TAG, "MediaPlayer completed")
if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
start()
alarmPlayTimes++
} else {
stop()
reset()
release()
alarmPlayer = null
alarmPlayTimes = 0
//恢复音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
//恢复通知栏
updateNotification(SettingUtils.notifyContent)
}
}
setVolume(alarm.volume / 100F, alarm.volume / 100F)
prepareAsync()
} catch (e: Exception) {
Log.e(TAG, "MediaPlayer Exception: ${e.message}")
setOnErrorListener { _, what, extra ->
Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
release()
return@setOnErrorListener true
}
setVolume(alarm.volume / 100F, alarm.volume / 100F)
prepareAsync()
} catch (e: Exception) {
Log.e(TAG, "MediaPlayer Exception: ${e.message}")
}
}
}
//振动提醒
if (alarm.repeatTimes >= 0) {
isVibrating = true
vibrationUtils.startVibration(alarm.vibrate, alarm.repeatTimes)
}
//闪光灯提醒
if (alarm.flashTimes >= 0) {
isFlash = true
flashUtils.startFlashing(alarm.flash, alarm.flashTimes)
}
}
}
@ -176,7 +209,14 @@ class ForegroundService : Service() {
//纯客户端模式
if (SettingUtils.enablePureClientMode) return
//创建通知渠道
createNotificationChannel()
//初始化振动
vibrationUtils = VibrationUtils(this)
//初始化闪光灯
flashUtils = FlashUtils(this)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -212,7 +252,7 @@ class ForegroundService : Service() {
override fun onDestroy() {
//非纯客户端模式
if (!SettingUtils.enablePureClientMode) stopForegroundService()
flashUtils.release()
super.onDestroy()
}
@ -292,6 +332,14 @@ class ForegroundService : Service() {
isRunning = false
alarmPlayer?.release()
alarmPlayer = null
//停止振动
if (vibrationUtils.isVibrating) {
vibrationUtils.stopVibration()
}
//停止闪光灯
if (flashUtils.isFlashing) {
flashUtils.stopFlashing()
}
} catch (e: Exception) {
handleException(e, "stopForegroundService")
}
@ -354,4 +402,4 @@ class ForegroundService : Service() {
isRunning = false
}
}
}

View File

@ -6,18 +6,29 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Rect
import android.os.Build
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.util.TypedValue
import android.view.View
import android.widget.EditText
import android.widget.GridLayout
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.Fragment
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.App.Companion.APP_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.BATTERY_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.CALL_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.COMMON_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.LOCATION_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.NETWORK_TAG_MAP
import com.idormy.sms.forwarder.App.Companion.SMS_TAG_MAP
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.webview.AgentWebActivity
import com.idormy.sms.forwarder.core.webview.AgentWebFragment
@ -35,6 +46,8 @@ import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog.SingleButton
import com.xuexiang.xui.widget.imageview.preview.PreviewBuilder
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.common.StringUtils
import com.xuexiang.xutil.resource.ResUtils.getColor
import com.xuexiang.xutil.resource.ResUtils.getDrawable
import com.xuexiang.xutil.resource.ResUtils.getString
import java.net.Inet4Address
import java.net.Inet6Address
@ -338,41 +351,76 @@ class CommonUtils private constructor() {
XUtil.exitApp()
}
/*fun switchLanguage(oldLocale: Locale, newLocale: Locale) {
val oldLang = if (TAG_LANG.contains(oldLocale.toString())) oldLocale.toString() else "en"
val newLang = if (TAG_LANG.contains(newLocale.toString())) newLocale.toString() else "en"
Log.i(App.TAG, "switchLanguage: oldLang=$oldLang, newLang=$newLang")
// 动态创建标签按钮并设置点击事件(将标签插入指定输入框)
fun createTagButtons(context: Context, gridLayout: GridLayout, editText: EditText, scene: String = "basic", excludeButtons: Array<String> = emptyArray()) {
// 将排除的按钮转换成一个集合,方便查找
val excludeSet = excludeButtons.toSet()
//替换自定义模板标签
var smsTemplate = SettingUtils.smsTemplate
//替换Rule.sms_template中的标签
var ruleColumn = "sms_template"
//替换Sender.json_setting中的标签
var senderColumn = "json_setting"
// 清空GridLayout中的所有视图
gridLayout.removeAllViews()
for (i in TAG_LIST.indices) {
val oldTag = TAG_LIST[i][oldLang].toString()
val newTag = TAG_LIST[i][newLang].toString()
if (oldTag == newTag) continue
// 根据场景动态拼接所有按钮数据
val allButtons = when (scene) {
"sms" -> SMS_TAG_MAP
"call" -> CALL_TAG_MAP
"app" -> APP_TAG_MAP
else -> CALL_TAG_MAP + SMS_TAG_MAP + APP_TAG_MAP
}.toMutableMap()
smsTemplate = smsTemplate.replace(oldTag, newTag)
ruleColumn = "REPLACE($ruleColumn, '$oldTag', '$newTag')"
senderColumn = "REPLACE($senderColumn, '$oldTag', '$newTag')"
if (SettingUtils.enableLocation) {
allButtons += LOCATION_TAG_MAP
}
if (scene == "all") {
allButtons += BATTERY_TAG_MAP
allButtons += NETWORK_TAG_MAP
}
allButtons += COMMON_TAG_MAP
val btnBackground = getDrawable(R.drawable.rounded_button)
val btnTextColor = getColor(android.R.color.white)
// 遍历所有按钮数据,过滤掉需要排除的按钮
allButtons.forEach { (tag, lable) ->
if (excludeSet.isNotEmpty() && excludeSet.contains(tag)) {
return@forEach
}
val button = TextView(context).apply {
text = lable
setOnClickListener {
insertOrReplaceText2Cursor(editText, tag)
}
// 设置紧凑样式
setTextSize(TypedValue.COMPLEX_UNIT_SP, 9f)
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
setPadding(5, 5, 5, 5)
gravity = android.view.Gravity.CENTER
background = btnBackground
setTextColor(btnTextColor)
// 布局参数
layoutParams = GridLayout.LayoutParams().apply {
height = GridLayout.LayoutParams.WRAP_CONTENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
width = 0
columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
} else {
width = GridLayout.LayoutParams.WRAP_CONTENT
}
setMargins(8, 8, 8, 8)
}
}
gridLayout.addView(button)
}
SettingUtils.smsTemplate = smsTemplate
}
val updateRuleSql = "UPDATE Rule SET sms_template = $ruleColumn WHERE sms_template != ''"
Log.d(App.TAG, "updateRuleSql: $updateRuleSql")
Core.rule.replaceTags(updateRuleSql)
val updateSenderSql = "UPDATE Sender SET json_setting = $senderColumn WHERE type NOT IN (4, 5, 6, 7, 8, 14)"
Log.d(App.TAG, "updateSenderSql: $updateSenderSql")
Core.sender.replaceTags(updateSenderSql)
}*/
}
init {
throw UnsupportedOperationException("u can't instantiate me...")
}
}
}

View File

@ -0,0 +1,116 @@
@file:Suppress("DEPRECATION")
package com.idormy.sms.forwarder.utils
import android.content.Context
import android.hardware.Camera
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraManager
import android.os.Build
import android.os.Handler
import android.os.Looper
class FlashUtils(context: Context) {
private var cameraManager: CameraManager? = null
private var cameraId: String? = null
private var legacyCamera: Camera? = null
private var legacyParams: Camera.Parameters? = null
private var handler: Handler? = null
private val duration = 100L // 闪烁持续时间
var isFlashing = false
private set
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
cameraId = cameraManager?.cameraIdList?.get(0) // 获取后置摄像头 ID
} catch (e: CameraAccessException) {
e.printStackTrace()
}
} else {
try {
legacyCamera = Camera.open()
legacyParams = legacyCamera?.parameters
} catch (e: Exception) {
e.printStackTrace()
}
}
}
/**
* 按照模式控制闪光灯
* @param pattern 例如 "XXOOXXOO" X-O-
* @param repeatTimes 闪烁的重复次数0 表示无限循环
*/
fun startFlashing(pattern: String, repeatTimes: Int) {
if (isFlashing) return
isFlashing = true
handler = Handler(Looper.getMainLooper())
val sequence = pattern.toCharArray()
var index = 0
var repeatCount = 0
val runnable = object : Runnable {
override fun run() {
if (!isFlashing) return
val shouldFlash = sequence[index] == 'X' || sequence[index] == '1'
setFlashlight(shouldFlash)
index++
if (index >= sequence.size) {
index = 0
repeatCount++
if (repeatTimes != 0 && repeatCount >= repeatTimes) {
stopFlashing()
return
}
}
handler?.postDelayed(this, duration)
}
}
handler?.post(runnable)
}
/**
* 关闭闪光灯并停止模式
*/
fun stopFlashing() {
isFlashing = false
handler?.removeCallbacksAndMessages(null)
setFlashlight(false) // 确保停止后灯是关闭的
}
/**
* 设置闪光灯状态兼容 Android 4.4+
*/
private fun setFlashlight(enable: Boolean) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraManager?.setTorchMode(cameraId!!, enable)
} else {
legacyParams?.flashMode = if (enable) Camera.Parameters.FLASH_MODE_TORCH else Camera.Parameters.FLASH_MODE_OFF
legacyCamera?.parameters = legacyParams
if (enable) legacyCamera?.startPreview() else legacyCamera?.stopPreview()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 释放旧 API 资源
*/
fun release() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
legacyCamera?.stopPreview()
legacyCamera?.release()
legacyCamera = null
}
}
}

View File

@ -14,6 +14,7 @@ import android.provider.Settings
import android.telephony.SmsManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.text.TextUtils
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
@ -28,8 +29,16 @@ import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.app.IntentUtils
import com.xuexiang.xutil.data.DateUtils
import com.xuexiang.xutil.resource.ResUtils.getString
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.min
@Suppress("DEPRECATION")
class PhoneUtils private constructor() {
@ -37,6 +46,14 @@ class PhoneUtils private constructor() {
companion object {
const val TAG = "PhoneUtils"
/** 获取 sim 卡槽数量,注意不是 sim 卡的数量。*/
fun getSimSlotCount() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
(App.context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).activeModemCount
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
(App.context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).phoneCount
else
-1
//获取多卡信息
@SuppressLint("Range")
fun getSimMultiInfo(): MutableMap<Int, SimInfo> {
@ -330,7 +347,7 @@ class PhoneUtils private constructor() {
//获取联系人列表
fun getContactInfoList(
limit: Int, offset: Int, phoneNumber: String?, name: String?
limit: Int, offset: Int, phoneNumber: String?, name: String?, isFuzzy: Boolean = true
): MutableList<ContactInfo> {
val contactInfoList: MutableList<ContactInfo> = mutableListOf()
@ -339,7 +356,11 @@ class PhoneUtils private constructor() {
val selectionArgs = ArrayList<String>()
if (!TextUtils.isEmpty(phoneNumber)) {
selection += " and replace(replace(" + ContactsContract.CommonDataKinds.Phone.NUMBER + ",' ',''),'-','') like ?"
selectionArgs.add("%$phoneNumber%")
if (isFuzzy) {
selectionArgs.add("%$phoneNumber%")
} else {
selectionArgs.add("%$phoneNumber")
}
}
if (!TextUtils.isEmpty(name)) {
selection += " and " + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " like ?"
@ -383,11 +404,100 @@ class PhoneUtils private constructor() {
return contactInfoList
}
// 获取号码归属地
fun getPhoneArea(phoneNumber: String): String {
val client = OkHttpClient()
val url = "https://cx.shouji.360.cn/phonearea.php?number=$phoneNumber"
val request = Request.Builder().url(url).build()
var result = getString(R.string.unknown_area) // 默认值
// 使用协程来执行网络请求
runBlocking {
val job = CoroutineScope(Dispatchers.IO).launch {
try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseData = response.body()?.string()
Log.i(TAG, "getPhoneArea: $responseData")
if (responseData != null) {
val jsonObject = JSONObject(responseData)
val data = jsonObject.getJSONObject("data")
val province = data.getString("province")
val city = data.getString("city")
val sp = data.getString("sp")
result = "$province $city $sp"
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
job.join() // 等待协程执行完毕
}
return result
}
//获取联系人姓名
fun getContactByNumber(phoneNumber: String?): MutableList<ContactInfo> {
val contactInfoList = mutableListOf<ContactInfo>()
if (TextUtils.isEmpty(phoneNumber)) return contactInfoList
return getContactInfoList(1, 0, phoneNumber, null)
// 去除国际区号、空格、括号、横线等字符
val normalizedInputNumber = if (phoneNumber!!.startsWith("+") && phoneNumber.length > 4) {
phoneNumber.substring(4).replace("[^0-9]".toRegex(), "")
} else {
phoneNumber.replace("[^0-9]".toRegex(), "")
}
contactInfoList.addAll(getContactInfoList(99, 0, normalizedInputNumber, null, false))
if (contactInfoList.isEmpty() || contactInfoList.size == 1) {
return contactInfoList
}
// 计算每个联系人的匹配长度和优先级
val scoredContacts = contactInfoList.map { contact ->
//去除空格、括号、横线等字符
val normalizedContactNumber = contact.phoneNumber.replace("[^0-9]".toRegex(), "")
val matchLength = calculateMatchLength(normalizedInputNumber, normalizedContactNumber)
// 优先级规则:
// 1. 完全匹配(输入手机号与联系人手机号完全一致):优先级 2
// 2. 匹配长度等于输入手机号长度:优先级 1
// 3. 其他情况:优先级 0
val priority = when {
normalizedInputNumber == normalizedContactNumber -> 2
matchLength == normalizedInputNumber.length -> 1
else -> 0
}
contact to Pair(matchLength, priority)
}.sortedWith(compareByDescending<Pair<ContactInfo, Pair<Int, Int>>> { it.second.first } // 按匹配长度降序
.thenByDescending { it.second.second }) // 按优先级降序
// 返回匹配长度最长且优先级最高的联系人列表
val maxMatchLength = scoredContacts.first().second.first
val maxPriority = scoredContacts.first().second.second
return scoredContacts
.filter { it.second.first == maxMatchLength && it.second.second == maxPriority }
.map { it.first }
.toMutableList()
}
// 计算从右向左的匹配长度
private fun calculateMatchLength(number1: String, number2: String): Int {
var matchLength = 0
val minLength = min(number1.length, number2.length)
// 从右向左逐位比较
for (i in 1..minLength) {
if (number1[number1.length - i] == number2[number2.length - i]) {
matchLength++
} else {
break // 遇到不匹配的字符,停止比较
}
}
return matchLength
}
//获取通话记录转发内容
@ -581,4 +691,4 @@ class PhoneUtils private constructor() {
init {
throw UnsupportedOperationException("u can't instantiate me...")
}
}
}

View File

@ -49,6 +49,7 @@ import com.idormy.sms.forwarder.workers.UpdateLogsWorker
import com.jeremyliao.liveeventbus.LiveEventBus
import com.xuexiang.xutil.XUtil
import com.xuexiang.xutil.resource.ResUtils.getString
import java.util.Calendar
object SendUtils {
private const val TAG = "SendUtils"
@ -97,6 +98,19 @@ object SendUtils {
senderLogic(0, msgInfo, rule, senderIndex, msgId)
return
}
//免打扰(禁用转发)日期段
Log.d(TAG, "silentDayOfWeek = ${rule.silentDayOfWeek}")
val silentDayOfWeek = rule.silentDayOfWeek.split(",").filter { it.isNotEmpty() }.map { it.toInt() }
if (silentDayOfWeek.isNotEmpty()) {
val dayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
if (silentDayOfWeek.contains(dayOfWeek)) {
Log.d(TAG, "免打扰(禁用转发)日期段")
updateLogs(logId, 0, getString(R.string.silent_time_period))
senderLogic(0, msgInfo, rule, senderIndex, msgId)
return
}
}
//免打扰(禁用转发)时间段
Log.d(TAG, "silentPeriodStart = ${rule.silentPeriodStart}, silentPeriodEnd = ${rule.silentPeriodEnd}")
if (rule.silentPeriodStart != rule.silentPeriodEnd) {

View File

@ -80,7 +80,7 @@ class SettingUtils private constructor() {
//是否不在最近任务列表中显示
var enableExcludeFromRecents: Boolean by SharedPreference(SP_ENABLE_EXCLUDE_FROM_RECENTS, false)
//是否转发应用通知
//是否启用Cactus增强保活措施
var enableCactus: Boolean by SharedPreference(SP_ENABLE_CACTUS, false)
//是否播放静音音乐

View File

@ -0,0 +1,88 @@
package com.idormy.sms.forwarder.utils
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.VibrationEffect
import android.os.Vibrator
@Suppress("DEPRECATION")
class VibrationUtils(context: Context) {
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private val handler = Handler(Looper.getMainLooper())
private var currentRepeat = 0
var isVibrating = false
private set
fun startVibration(patternString: String, repeatTimes: Int) {
isVibrating = true
currentRepeat = 0
val parsedPattern = parsePattern(patternString)
vibratePattern(parsedPattern, 0, repeatTimes)
}
fun stopVibration() {
isVibrating = false
vibrator.cancel()
}
private fun parsePattern(pattern: String): List<Triple<Long, Boolean, Int>> {
val parsedPattern = mutableListOf<Triple<Long, Boolean, Int>>()
var currentChar = pattern[0]
var currentLength = 1L
for (i in 1 until pattern.length) {
if (pattern[i] == currentChar) {
currentLength++
} else {
parsedPattern.add(createTriple(currentChar, currentLength))
currentChar = pattern[i]
currentLength = 1L
}
}
parsedPattern.add(createTriple(currentChar, currentLength))
return parsedPattern
}
private fun createTriple(char: Char, length: Long): Triple<Long, Boolean, Int> {
val duration = 100L * length
val intensity = when (char) {
'=' -> 255
'-' -> 128
'_' -> 0
else -> 0
}
return Triple(duration, intensity > 0, intensity)
}
private fun vibratePattern(parsedPattern: List<Triple<Long, Boolean, Int>>, index: Int, repeatTimes: Int) {
if (isVibrating && index < parsedPattern.size) {
val (duration, shouldVibrate, intensity) = parsedPattern[index]
if (shouldVibrate) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val effect = VibrationEffect.createOneShot(duration, intensity)
vibrator.vibrate(effect)
} else {
vibrator.vibrate(duration)
}
}
handler.postDelayed({
if (isVibrating) {
vibrator.cancel()
if (index + 1 < parsedPattern.size) {
vibratePattern(parsedPattern, index + 1, repeatTimes)
} else {
currentRepeat++
if (repeatTimes == 0 || currentRepeat < repeatTimes) {
vibratePattern(parsedPattern, 0, repeatTimes) // Restart pattern
} else {
stopVibration()
}
}
}
}, duration)
}
}
}

View File

@ -3,14 +3,16 @@ package com.idormy.sms.forwarder.utils.mail
import android.text.Html
import android.text.Spanned
import com.idormy.sms.forwarder.utils.Log
import com.sun.mail.util.MailSSLSocketFactory
import jakarta.mail.Authenticator
import jakarta.mail.PasswordAuthentication
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing
import java.io.File
import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.util.Properties
import javax.mail.Authenticator
import javax.mail.PasswordAuthentication
@Suppress("PrivatePropertyName", "DEPRECATION")
class EmailSender(
@ -32,7 +34,6 @@ class EmailSender(
private val listener: EmailTaskListener? = null,
// 安全选项
private val openSSL: Boolean = false, //是否开启ssl验证 默认关闭
private val sslFactory: String = "javax.net.ssl.SSLSocketFactory", //SSL构建类名
private val startTls: Boolean = false, //是否开启starttls加密方式 默认关闭
// 邮件加密方式: S/MIME、OpenPGP、Plain不传证书
private val encryptionProtocol: String = "S/MIME",
@ -58,7 +59,11 @@ class EmailSender(
// 设置是否启用 SSL 连接
if (openSSL) {
put("mail.smtp.ssl.enable", "true")
put("mail.smtp.socketFactory.class", sslFactory)
// 使用 TLSv1.2 协议 & 信任所有主机
val sf = MailSSLSocketFactory("TLSv1.2")
sf.setTrustedHosts("*")
put("mail.smtp.ssl.socketFactory", sf)
put("mail.smtp.ssl.protocols", "TLSv1.2")
}
// 设置是否启用 TLS 连接
if (startTls) {
@ -171,4 +176,4 @@ class EmailSender(
return PasswordAuthentication(userName, password)
}
}
}
}

View File

@ -1,6 +1,18 @@
package com.idormy.sms.forwarder.utils.mail
import com.idormy.sms.forwarder.utils.Log
import jakarta.activation.DataHandler
import jakarta.activation.FileDataSource
import jakarta.mail.Authenticator
import jakarta.mail.Message
import jakarta.mail.Session
import jakarta.mail.Transport
import jakarta.mail.internet.InternetAddress
import jakarta.mail.internet.MimeBodyPart
import jakarta.mail.internet.MimeMessage
import jakarta.mail.internet.MimeMultipart
import jakarta.mail.internet.MimeUtility
import jakarta.mail.util.ByteArrayDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.bouncycastle.jce.provider.BouncyCastleProvider
@ -22,18 +34,6 @@ import java.io.InputStream
import java.security.Security
import java.util.Date
import java.util.Properties
import javax.activation.DataHandler
import javax.activation.FileDataSource
import javax.mail.Authenticator
import javax.mail.Message
import javax.mail.Session
import javax.mail.Transport
import javax.mail.internet.InternetAddress
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMessage
import javax.mail.internet.MimeMultipart
import javax.mail.internet.MimeUtility
import javax.mail.util.ByteArrayDataSource
@Suppress("PrivatePropertyName")
@ -244,4 +244,3 @@ class PgpUtils(
}
}

View File

@ -1,6 +1,17 @@
package com.idormy.sms.forwarder.utils.mail
import com.idormy.sms.forwarder.utils.Log
import jakarta.activation.DataHandler
import jakarta.activation.FileDataSource
import jakarta.mail.Authenticator
import jakarta.mail.Message
import jakarta.mail.Session
import jakarta.mail.Transport
import jakarta.mail.internet.InternetAddress
import jakarta.mail.internet.MimeBodyPart
import jakarta.mail.internet.MimeMessage
import jakarta.mail.internet.MimeMultipart
import jakarta.mail.internet.MimeUtility
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.bouncycastle.cert.jcajce.JcaCertStore
@ -24,17 +35,7 @@ import java.security.Security
import java.security.cert.X509Certificate
import java.util.Date
import java.util.Properties
import javax.activation.DataHandler
import javax.activation.FileDataSource
import javax.mail.Authenticator
import javax.mail.Message
import javax.mail.Session
import javax.mail.Transport
import javax.mail.internet.InternetAddress
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMessage
import javax.mail.internet.MimeMultipart
import javax.mail.internet.MimeUtility
@Suppress("PrivatePropertyName")
class SmimeUtils(
@ -249,4 +250,4 @@ class SmimeUtils(
return encryptedMessage
}
}
}

View File

@ -67,17 +67,27 @@ class BarkUtils {
if (!TextUtils.isEmpty(setting.level)) msgMap["level"] = setting.level
if (!TextUtils.isEmpty(setting.sound)) msgMap["sound"] = setting.sound
if (!TextUtils.isEmpty(setting.badge)) msgMap["badge"] = setting.badge
if (!TextUtils.isEmpty(setting.url)) msgMap["url"] = setting.url
if (!TextUtils.isEmpty(setting.url)) {
val replacedUrl = msgInfo.getContentForSend(setting.url)
msgMap["url"] = replacedUrl
}
//自动复制验证码
val pattern = Regex("(?<!回复)(验证码|授权码|校验码|检验码|确认码|激活码|动态码|安全码|(验证)?代码|校验代码|检验代码|激活代码|确认代码|动态代码|安全代码|登入码|认证码|识别码|短信口令|动态密码|交易码|上网密码|动态口令|随机码|驗證碼|授權碼|校驗碼|檢驗碼|確認碼|激活碼|動態碼|(驗證)?代碼|校驗代碼|檢驗代碼|確認代碼|激活代碼|動態代碼|登入碼|認證碼|識別碼|一次性密码|[Cc][Oo][Dd][Ee]|[Vv]erification)")
if (pattern.containsMatchIn(content)) {
var code = content.replace("(.*)((代|授权|验证|动态|校验)码|[【\\[].*[】\\]]|[Cc][Oo][Dd][Ee]|[Vv]erification\\s?([Cc]ode)?)\\s?(G-|<#>)?([:\\s是为]|[Ii][Ss]){0,3}[\\(\\[【{「]?(([0-9\\s]{4,7})|([\\dA-Za-z]{5,6})(?!([Vv]erification)?([Cc][Oo][Dd][Ee])|:))[」}】\\]\\)]?(?=([^0-9a-zA-Z]|\$))(.*)".toRegex(), "$7").trim()
code = code.replace("\\D*[\\(\\[【{「]?([0-9]{3}\\s?[0-9]{1,3})[」}】\\]\\)]?(?=.*((代|授权|验证|动态|校验)码|[【\\[].*[】\\]]|[Cc][Oo][Dd][Ee]|[Vv]erification\\s?([Cc]ode)?))(.*)".toRegex(), "$1").trim()
if (code.isNotEmpty()) {
msgMap["copy"] = code
msgMap["automaticallyCopy"] = 1
if (!TextUtils.isEmpty(setting.call)) msgMap["call"] = setting.call
//自动复制
if (TextUtils.isEmpty(setting.autoCopy)) {
val pattern = Regex("(?<!回复)(验证码|授权码|校验码|检验码|确认码|激活码|动态码|安全码|(验证)?代码|校验代码|检验代码|激活代码|确认代码|动态代码|安全代码|登入码|认证码|识别码|短信口令|动态密码|交易码|上网密码|动态口令|随机码|驗證碼|授權碼|校驗碼|檢驗碼|確認碼|激活碼|動態碼|(驗證)?代碼|校驗代碼|檢驗代碼|確認代碼|激活代碼|動態代碼|登入碼|認證碼|識別碼|一次性密码|[Cc][Oo][Dd][Ee]|[Vv]erification)")
if (pattern.containsMatchIn(content)) {
var code = content.replace("(.*)((代|授权|验证|动态|校验)码|[【\\[].*[】\\]]|[Cc][Oo][Dd][Ee]|[Vv]erification\\s?([Cc]ode)?)\\s?(G-|<#>)?([:\\s是为]|[Ii][Ss]){0,3}[\\(\\[【{「]?(([0-9\\s]{4,7})|([\\dA-Za-z]{5,6})(?!([Vv]erification)?([Cc][Oo][Dd][Ee])|:))[」}】\\]\\)]?(?=([^0-9a-zA-Z]|\$))(.*)".toRegex(), "$7").trim()
code = code.replace("\\D*[\\(\\[【{「]?([0-9]{3}\\s?[0-9]{1,3})[」}】\\]\\)]?(?=.*((代|授权|验证|动态|校验)码|[【\\[].*[】\\]]|[Cc][Oo][Dd][Ee]|[Vv]erification\\s?([Cc]ode)?))(.*)".toRegex(), "$1").trim()
if (code.isNotEmpty()) {
msgMap["copy"] = code
msgMap["autoCopy"] = 1
}
}
} else {
msgMap["copy"] = msgInfo.getContentForSend(setting.autoCopy)
msgMap["autoCopy"] = 1
}
val requestMsg: String = Gson().toJson(msgMap)
@ -161,4 +171,4 @@ class BarkUtils {
}
}
}
}

View File

@ -137,6 +137,14 @@ class EmailUtils {
setting.fromEmail += setting.mailType
}
"@icloud.com" -> {
setting.host = "smtp.mail.me.com"
setting.port = "587"
setting.ssl = false
setting.startTls = true
setting.fromEmail += setting.mailType
}
else -> {}
}
@ -346,4 +354,4 @@ class EmailUtils {
}
}
}
}

View File

@ -38,7 +38,14 @@ class ServerchanUtils {
msgInfo.getContentForSend(SettingUtils.smsTemplate)
}
val requestUrl: String = String.format("https://sctapi.ftqq.com/%s.send", setting.sendKey) //推送地址
// 兼容Server酱³Sendkey使用正则表达式提取数字部分
val matchResult = Regex("^sctp(\\d+)t", RegexOption.IGNORE_CASE).find(setting.sendKey)
val requestUrl = if (matchResult != null && matchResult.groups[1] != null) {
"https://${matchResult.groups[1]?.value}.push.ft07.com/send/${setting.sendKey}.send"
} else {
String.format("https://sctapi.ftqq.com/%s.send", setting.sendKey) // 默认推送地址
}
Log.i(TAG, "requestUrl:$requestUrl")
val request = XHttp.post(requestUrl)

View File

@ -58,13 +58,29 @@ class TelegramUtils private constructor() {
val request = if (setting.method == "GET") {
requestUrl += "?chat_id=" + setting.chatId + "&text=" + URLEncoder.encode(content, "UTF-8")
if (setting.parseMode.isNotEmpty() && setting.parseMode != "TEXT") {
requestUrl += "&parse_mode=" + setting.parseMode
}
Log.i(TAG, "requestUrl:$requestUrl")
XHttp.get(requestUrl)
} else {
val bodyMap: MutableMap<String, Any> = mutableMapOf()
bodyMap["chat_id"] = setting.chatId
bodyMap["text"] = content
bodyMap["parse_mode"] = "HTML"
when (setting.parseMode) {
"MarkdownV2" -> {
bodyMap["parse_mode"] = "MarkdownV2"
bodyMap["text"] = escapeMarkdownV2(content)
}
"HTML" -> {
bodyMap["parse_mode"] = "HTML"
bodyMap["text"] = content
}
else -> {
bodyMap["text"] = content
}
}
bodyMap["disable_web_page_preview"] = "true"
val requestMsg: String = Gson().toJson(bodyMap)
Log.i(TAG, "requestMsg:$requestMsg")
@ -155,5 +171,18 @@ class TelegramUtils private constructor() {
}
return buffer.toString()
}
// 用于转义 MarkdownV2 特殊字符的方法
private fun escapeMarkdownV2(text: String): String {
// TODO: MarkdownV2 要求转义以下字符,实测不能全部转义(丢失格式)
//val specialChars = listOf('_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!')
val specialChars = listOf('-')
var escapedText = text
for (char in specialChars) {
// 将每个字符替换为带反斜杠的形式
escapedText = escapedText.replace(char.toString(), "\\$char")
}
return escapedText
}
}
}
}

View File

@ -93,10 +93,19 @@ class WebhookUtils {
//通过`Content-Type=applicaton/json`指定请求体为`json`格式
var isJson = false
//通过`Content-Type=text/plain、text/html、text/css、text/javascript、text/xml`指定请求体为`文本`格式
var isText = false
var mediaType = "text/plain"
for ((key, value) in setting.headers.entries) {
if (key.equals("Content-Type", ignoreCase = true) && value.contains("application/json")) {
isJson = true
break
if (key.equals("Content-Type", ignoreCase = true)) {
if (value.contains("application/json")) {
isJson = true
break
} else if (value.startsWith("text/")) {
isText = true
mediaType = value
break
}
}
}
@ -138,7 +147,7 @@ class WebhookUtils {
}
Log.d(TAG, "method = GET, Url = $requestUrl")
XHttp.get(requestUrl).keepJson(true)
} else if (webParams.isNotEmpty() && (isJson || webParams.startsWith("{"))) {
} else if (webParams.isNotEmpty() && (isJson || isText || webParams.startsWith("{"))) {
webParams = msgInfo.replaceTemplate(webParams, "", "Gson")
val bodyMsg = webParams.replace("[from]", from)
.replace("[content]", escapeJson(content))
@ -155,10 +164,18 @@ class WebhookUtils {
.replace("[timestamp]", timestamp.toString())
.replace("[sign]", sign)
Log.d(TAG, "method = ${setting.method}, Url = $requestUrl, bodyMsg = $bodyMsg")
when (setting.method) {
"PUT" -> XHttp.put(requestUrl).keepJson(true).upJson(bodyMsg)
"PATCH" -> XHttp.patch(requestUrl).keepJson(true).upJson(bodyMsg)
else -> XHttp.post(requestUrl).keepJson(true).upJson(bodyMsg)
if (isText) {
when (setting.method) {
"PUT" -> XHttp.put(requestUrl).keepJson(true).upString(bodyMsg, mediaType)
"PATCH" -> XHttp.patch(requestUrl).keepJson(true).upString(bodyMsg, mediaType)
else -> XHttp.post(requestUrl).keepJson(true).upString(bodyMsg, mediaType)
}
} else {
when (setting.method) {
"PUT" -> XHttp.put(requestUrl).keepJson(true).upJson(bodyMsg)
"PATCH" -> XHttp.patch(requestUrl).keepJson(true).upJson(bodyMsg)
else -> XHttp.post(requestUrl).keepJson(true).upJson(bodyMsg)
}
}
} else {
if (webParams.isEmpty()) {

View File

@ -6,17 +6,19 @@ import android.widget.CompoundButton
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatCheckBox
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.reflect.TypeToken
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.http.api.ApiService.IGetService
import com.idormy.sms.forwarder.core.http.callback.NoTipCallBack
import com.idormy.sms.forwarder.core.http.entity.TipInfo
import com.idormy.sms.forwarder.utils.AppUtils
import com.idormy.sms.forwarder.utils.SharedPreference
import com.xuexiang.constant.TimeConstants
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xhttp2.XHttp
import com.xuexiang.xhttp2.cache.model.CacheMode
import com.xuexiang.xhttp2.callback.SimpleCallBack
import com.xuexiang.xhttp2.exception.ApiException
import com.xuexiang.xui.widget.dialog.BaseDialog
import com.xuexiang.xutil.resource.ResUtils.getString
import com.zzhoujay.richtext.RichText
/**
@ -163,18 +165,33 @@ class GuideTipsDialog(context: Context?, tips: List<TipInfo>) :
*/
@JvmStatic
fun showTipsForce(context: Context?) {
val request = XHttp.custom().cacheMode(CacheMode.FIRST_CACHE)
.cacheTime(TimeConstants.DAY.toLong()).cacheKey("getTips")
request.apiCall(request.create(
IGetService::class.java
).tips, object : NoTipCallBack<List<TipInfo>>() {
@Throws(Throwable::class)
override fun onSuccess(response: List<TipInfo>?) {
if (!response.isNullOrEmpty()) {
GuideTipsDialog(context, response).show()
XHttp.get(getString(R.string.url_tips))
.keepJson(true)
.ignoreHttpsCert()
.timeStamp(true) //url自动追加时间戳避免缓存
.execute(object : SimpleCallBack<String>() {
override fun onError(e: ApiException) {
e.printStackTrace()
}
}
})
override fun onSuccess(json: String) {
try {
val gson = Gson()
val jsonObject = gson.fromJson(json, JsonObject::class.java)
if (jsonObject.isJsonObject
&& jsonObject.has("Code") && jsonObject["Code"].asInt == 0
&& jsonObject.has("Data") && jsonObject["Data"].isJsonArray
) {
val dataJsonArray = jsonObject["Data"].asJsonArray
val listType = object : TypeToken<List<TipInfo>>() {}.type
val tips = gson.fromJson<List<TipInfo>>(dataJsonArray, listType)
GuideTipsDialog(context, tips).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
})
}
fun setIsIgnoreTips(isIgnore: Boolean): Boolean {
@ -190,4 +207,4 @@ class GuideTipsDialog(context: Context?, tips: List<TipInfo>) :
initViews()
updateTips(tips)
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下时的背景 -->
<item android:state_pressed="true">
<shape>
<solid android:color="#1E88E5" /> <!-- 按下时的深蓝背景色 -->
<corners android:radius="3dp" /> <!-- 圆角半径 -->
<stroke android:width="1dp" android:color="#1565C0" /> <!-- 边框深蓝色 -->
</shape>
</item>
<!-- 默认背景 -->
<item>
<shape>
<solid android:color="#2196F3" /> <!-- 默认浅蓝背景色 -->
<corners android:radius="3dp" /> <!-- 圆角半径 -->
<stroke android:width="1dp" android:color="#1E88E5" /> <!-- 边框蓝色 -->
</shape>
</item>
</selector>

View File

@ -376,65 +376,13 @@
android:inputType="textMultiLine"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_sms_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_content" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_sender_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_uid"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_uid" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_title_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_title_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_content_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -509,52 +457,184 @@
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="RtlSymmetry">
<LinearLayout
android:layout_width="180dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
tools:ignore="RtlSymmetry">
<LinearLayout
android:layout_width="180dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/silent_time_period"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/silent_time_period_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp,TooManyViews" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:id="@+id/tv_silent_period"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/silent_time_period"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="center" />
<TextView
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_silent_period"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/silent_time_period_tips"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select"
android:textColor="@color/white"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp,TooManyViews" />
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
</LinearLayout>
<TextView
android:id="@+id/tv_silent_period"
android:layout_width="0dp"
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="center" />
android:layout_marginTop="3dp">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_silent_period"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select"
android:textColor="@color/white"
android:textSize="@dimen/text_size_mini"
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/mon"
android:layout_width="15dp"
android:layout_height="15dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/mon"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/tue"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/tue"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/wed"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/wed"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/thu"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/thu"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/fri"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/fri"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/sat"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/sat"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.SmoothCheckBox
android:id="@+id/sun"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="5dp"
app:scb_color_checked="@color/colorPrimary"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/sun"
android:textSize="@dimen/text_size_small"
tools:ignore="SmallSp" />
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
@ -592,4 +672,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -140,6 +140,28 @@
</LinearLayout>
<LinearLayout
style="@style/BarStyle.Switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="@string/bark_call"
android:textStyle="bold" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_call"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true" />
</LinearLayout>
<LinearLayout
style="@style/BarStyle"
android:layout_width="match_parent"
@ -271,36 +293,60 @@
android:singleLine="true"
app:met_clearButton="true" />
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_auto_copy_template"
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/auto_copy"
android:textStyle="bold" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
android:text="@string/custom_template_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
</LinearLayout>
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
android:id="@+id/et_auto_copy_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
app:met_clearButton="true" />
<GridLayout
android:id="@+id/gl_auto_copy_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
<LinearLayout
@ -405,4 +451,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -253,35 +253,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -319,4 +297,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -230,35 +230,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -443,4 +421,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -149,35 +149,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender_to_nickname"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra_to_nickname"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time_to_nickname"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name_to_nickname"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -520,35 +498,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -586,4 +542,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -158,35 +158,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender_to_title"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -230,41 +208,13 @@
android:text=""
tools:ignore="RtlHardcoded,SpeakableTextPresentCheck" />
<LinearLayout
<GridLayout
android:id="@+id/gl_message_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_content" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -302,4 +252,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -235,35 +235,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender_to_title"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name_to_title"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -307,41 +285,13 @@
android:text=""
tools:ignore="RtlHardcoded,SpeakableTextPresentCheck" />
<LinearLayout
<GridLayout
android:id="@+id/gl_message_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_content" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -379,4 +329,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -112,35 +112,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -205,4 +183,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -286,35 +286,13 @@
android:singleLine="true"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -352,4 +330,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -102,6 +102,44 @@
</LinearLayout>
<LinearLayout
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parse_mode"
android:textStyle="bold" />
<RadioGroup
android:id="@+id/rg_parse_mode"
style="@style/rg_style"
android:layout_marginStart="5dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_parse_mode_text"
style="@style/rg_rb_style"
android:text="@string/parse_mode_text" />
<RadioButton
android:id="@+id/rb_parse_mode_html"
style="@style/rg_rb_style"
android:checked="true"
android:text="@string/parse_mode_html" />
<RadioButton
android:id="@+id/rb_parse_mode_markdown"
style="@style/rg_rb_style"
android:text="@string/parse_mode_markdown" />
</RadioGroup>
</LinearLayout>
<LinearLayout
style="@style/BarStyle"
android:layout_width="match_parent"
@ -306,4 +344,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1226,7 +1226,7 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/enabe_cactus_tips"
android:text="@string/enable_cactus_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
@ -1567,6 +1567,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/layout_sim2"
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -1741,41 +1742,13 @@
android:text=""
tools:ignore="RtlHardcoded,SpeakableTextPresentCheck" />
<LinearLayout
<GridLayout
android:id="@+id/gl_sms_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_content" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="3dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -1975,4 +1948,4 @@
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</LinearLayout>

View File

@ -86,101 +86,350 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/alarm_play_settings"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_volume"
android:textStyle="bold" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_volume"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="100"
app:xsb_min="1" />
android:text="@string/alarm_play_settings"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_music"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_alarm_settings_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_play_times"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:background="?attr/xui_config_color_separator_light" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_loop_times"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="30"
app:xsb_min="0" />
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_volume"
android:textStyle="bold" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_volume"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="100"
app:xsb_min="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_play_times"
android:textStyle="bold" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_play_times"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="30"
app:xsb_min="0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_music"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_music_path"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:hint="@string/alarm_music_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_file_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select_file"
android:textColor="@color/white"
android:textSize="@dimen/text_size_mini"
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_vibrate_settings"
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_music"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_music_path"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:hint="@string/alarm_music_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
android:text="@string/alarm_vibrate_settings"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/btn_file_picker"
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_vibrate"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_vibrate_settings_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:gravity="center"
android:padding="5dp"
android:text="@string/select_file"
android:textColor="@color/white"
android:textSize="@dimen/text_size_mini"
app:sb_color_unpressed="@color/colorPrimary"
app:sb_ripple_color="@color/white"
app:sb_ripple_duration="500"
app:sb_shape_type="rectangle"
tools:ignore="SmallSp" />
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_vibration_effect"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_vibration_effect"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:hint="@string/alarm_vibration_effect_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_vibration_effect_1"
style="@style/insertButtonStyle"
android:text="@string/alarm_vibration_effect_1" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_vibration_effect_2"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/alarm_vibration_effect_2" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_vibration_effect_3"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/alarm_vibration_effect_3" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_repeat_times"
android:textStyle="bold" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_repeat_times"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="30"
app:xsb_min="0" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_flash_settings"
style="@style/BarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/alarm_flash_settings"
android:textStyle="bold"
tools:ignore="RelativeOverlap" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_flash"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_flash_settings_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_flash_effect"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.ClearEditText
android:id="@+id/et_flash_effect"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:hint="@string/alarm_flash_effect_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_flash_effect_1"
style="@style/insertButtonStyle"
android:text="@string/alarm_flash_effect_1" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_flash_effect_2"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/alarm_flash_effect_2" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_repeat_times"
android:textStyle="bold" />
<com.xuexiang.xui.widget.picker.XSeekBar
android:id="@+id/xsb_flash_times"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:padding="0dp"
app:xsb_max="30"
app:xsb_min="0" />
</LinearLayout>
</LinearLayout>
@ -220,4 +469,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -161,65 +161,13 @@
android:inputType="textMultiLine"
app:met_clearButton="true" />
<LinearLayout
<GridLayout
android:id="@+id/gl_sms_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender"
style="@style/insertButtonStyle"
android:text="@string/insert_sender" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_content" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_extra"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_extra" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_sender_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_sender_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_uid"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_uid" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_title_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_title_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_content_app"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_content_app" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_time"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_time" />
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
android:id="@+id/bt_insert_device_name"
style="@style/insertButtonStyle"
android:layout_marginStart="5dp"
android:text="@string/insert_device_name" />
</LinearLayout>
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" />
</LinearLayout>
@ -357,4 +305,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,5 +1,35 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="insert_tag_from">Phone Number</string>
<string name="insert_tag_sms">SMS Content</string>
<string name="insert_tag_package_name">Package Name</string>
<string name="insert_tag_app_name">App Name</string>
<string name="insert_tag_title">Inform Title</string>
<string name="insert_tag_msg">Inform Content</string>
<string name="insert_tag_card_slot">SIM Label</string>
<string name="insert_tag_card_subid">SIM SubId</string>
<string name="insert_tag_receive_time">Receive Time</string>
<string name="insert_tag_current_time">Current Time</string>
<string name="insert_tag_device_name">Device Name</string>
<string name="insert_tag_app_version">SmsF Version</string>
<string name="insert_tag_call_type">Call Type</string>
<string name="insert_tag_location">Location</string>
<string name="insert_tag_location_longitude">Longitude</string>
<string name="insert_tag_location_latitude">Latitude</string>
<string name="insert_tag_location_address">Address</string>
<string name="insert_tag_battery_pct">Battery Percentage</string>
<string name="insert_tag_battery_status">Battery Status</string>
<string name="insert_tag_battery_plugged">Charging Method</string>
<string name="insert_tag_battery_info">Complete Battery Info</string>
<string name="insert_tag_battery_info_simple">Simplified Battery Info</string>
<string name="insert_tag_uid">UID</string>
<string name="insert_tag_ipv4">Public IPv4</string>
<string name="insert_tag_ipv6">Public IPv6</string>
<string name="insert_tag_ip_list">IP List</string>
<string name="insert_tag_net_type">Network Status</string>
<string name="insert_tag_contact_name">Contact Name</string>
<string name="insert_tag_phone_area">Phone Area</string>
<string name="type_param_sms">Sms</string>
<string name="type_param_call">Call</string>
<string name="type_param_app">App</string>
@ -58,6 +88,7 @@
<string name="url_help">https://gitee.com/pp/SmsForwarder/wikis/pages</string>
<string name="url_donation_link">https://gitee.com/pp/SmsForwarder.wiki/raw/master/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95.md</string>
<string name="url_wechat_miniprogram">https://gitee.com/pp/SmsForwarder/raw/main/pic/wechat_miniprogram.jpg</string>
<string name="url_tips">https://gitee.com/pp/SmsForwarder.wiki/raw/master/tips_en.json</string>
<string name="lab_yes">Yes</string>
<string name="lab_no">No</string>
@ -186,6 +217,13 @@
<string name="switch_rule_status">Enable This Forwarding Rule</string>
<string name="invalid_match_value">The matched value cannot be null</string>
<string name="invalid_call_type">The call type is incorrect, you can only enter any number from 1 to 6.</string>
<string name="mon">MON</string>
<string name="tue">TUE</string>
<string name="wed">WED</string>
<string name="thu">THU</string>
<string name="fri">FRI</string>
<string name="sat">SAT</string>
<string name="sun">SUN</string>
<!--SenderActivity-->
<string name="delete_sender_title">Delete confirmation</string>
<string name="delete_sender_tips">Are you sure to delete this sender?</string>
@ -215,8 +253,8 @@
<string name="invalid_wework_agent">CoreID, AgentID, and Secret cannot be empty</string>
<string name="invalid_dingtalk_inner_robot">AgentId, AppKey, AppSecret, and UserIds cannot be empty</string>
<string name="invalid_phone_num">The receiving phone number cannot be empty</string>
<string name="invalid_multi_match">Malformed multiple match rule line %s</string>
<string name="invalid_regex_replace">Incorrect format on line %s of regex replacement</string>
<string name="invalid_multi_match">Malformed multiple match rule line %d</string>
<string name="invalid_regex_replace">Incorrect format on line %d of regex replacement</string>
<string name="invalid_message_card">The Message Card Json is invalid.</string>
<string name="email_host">Host</string>
<string name="smtp_port">Port</string>
@ -271,6 +309,10 @@
<string name="TelegramApiToken">ApiToken or Custom Proxy Address (startwith http)</string>
<string name="TelegramChatId">ChatId</string>
<string name="Method" formatted="false">Method</string>
<string name="parse_mode">Parse Mode</string>
<string name="parse_mode_text">Text</string>
<string name="parse_mode_html">Html</string>
<string name="parse_mode_markdown">MarkdownV2</string>
<string name="SmsSimSlot">SIM Slot</string>
<string name="same_source">Same source</string>
@ -310,17 +352,10 @@
<string name="custom_template">Custom templates</string>
<string name="custom_template_tips">Tip: Insert labels as needed; Leave blank to apply default template</string>
<string name="insert_sender">Phone</string>
<string name="insert_sender_app">PackageName</string>
<string name="insert_uid">UID</string>
<string name="insert_content">SMS</string>
<string name="insert_title_app">InformTitle</string>
<string name="insert_content_app">InformContent</string>
<string name="insert_extra">SIM</string>
<string name="insert_time">Time</string>
<string name="insert_device_name">Device</string>
<string name="battery_setting">Battery Optimization</string>
<string name="battery_setting_tips">Set it to manual management, including automatic startup, associated startup, and background running</string>
<string name="unknown_number">Unknown Number</string>
<string name="unknown_area">Unknown Area</string>
<string name="unsupport">Your phone does not support this setting</string>
<string name="isIgnored">Set successfully!</string>
<string name="isIgnored2">Can not directly operate the system power saving optimization Settings</string>
@ -357,6 +392,7 @@
<string name="appicon">App Icon</string>
<string name="user_app">User App</string>
<string name="system_app">System App</string>
<string name="tips_get_installed_apps">Please grant GET_INSTALLED_APPS permission</string>
<string name="tips_notification">Please grant Notification permission, in order to keep the App alive!</string>
<string name="tips_notification_listener">Please grant Notification reading permission to SmsForwarder, before other Apps\'s notification could be forwarded. Forwarding automatically canceled!</string>
<string name="pushplus_website">Official website</string>
@ -403,6 +439,7 @@
<string name="GotifyWebServer">WebServer</string>
<string name="GotifyWebServerTips"><![CDATA[eg.: https://push.ppps.cn/message?token=<apptoken>]]></string>
<string name="title_template">Title Template</string>
<string name="auto_copy">Auto Copy</string>
<string name="priority">Priority1 9</string>
<string name="dingtalk_robot">Dingtalk Group Bot</string>
<string name="dingtalk_inner_robot">Dingtalk Inner Bot</string>
@ -446,7 +483,7 @@
<string name="interval_label">Increasing Interval</string>
<string name="timeout_label">Single Timeout</string>
<string name="seconds">secs</string>
<string name="seconds_n">%s sec</string>
<string name="seconds_n">%d sec</string>
<string name="retry_label">Max Retries</string>
<string name="test_sender_sms">[%s] Congratulations, the sender test is successful, please continue to add forwarding rules!</string>
<string name="test_sender_name">Test Channel</string>
@ -512,7 +549,7 @@
<string name="no">No</string>
<string name="refresh">Refresh</string>
<string name="tip_can_not_get_sim_infos">Please confirm that the app permission [Get mobile phone information] is [Always allow]</string>
<string name="tip_can_not_get_sim_info">The SIM card information in the card slot %s has not been obtained</string>
<string name="tip_can_not_get_sim_info">The SIM card information in the card slot %d has not been obtained</string>
<string name="auto_check">Auto check</string>
<string name="check_update">Check</string>
<string name="join_preview_program">Join Preview Program</string>
@ -540,6 +577,7 @@
<string name="bark_group_tips">Opt., e.g. SmsForwarder</string>
<string name="bark_icon">Message Icon</string>
<string name="bark_icon_tips">Opt., fill in Url, the picture should not be too big</string>
<string name="bark_call">Keep Reminding</string>
<string name="bark_sound">Message Sound</string>
<string name="bark_sound_tips">Opt., e.g. minuet.caf</string>
<string name="bark_badge">Message Badge</string>
@ -803,7 +841,7 @@
<string name="debug_mode_tips">Save Log.* to file for troubleshooting; export to download directory.</string>
<string name="optional_components">Opt.:</string>
<string name="enable_cactus">Enable Cactus Keep Alive</string>
<string name="enabe_cactus_tips">Dual foreground service/JobScheduler/WorkManager/1px/silent music</string>
<string name="enable_cactus_tips">Dual foreground service/JobScheduler/WorkManager/1px/silent music</string>
<string name="load_app_list">Get installed app info async at startup</string>
<string name="load_app_list_tips">Used to speed up entering the application list/editing forwarding rules drop-down selection/replacement {{APP_NAME}}</string>
<string name="load_app_list_toast">A type must be selected when enabling asynchronous loading of the list of installed apps</string>
@ -964,8 +1002,8 @@
<string name="task_rule_tips">Control the enable/disable of "Rules"</string>
<string name="task_sender">Channels On/Off</string>
<string name="task_sender_tips">Control the enable/disable of "Senders"</string>
<string name="task_alarm">Alarm</string>
<string name="task_alarm_tips">Alarm</string>
<string name="task_alarm">Alarm Reminder</string>
<string name="task_alarm_tips">Play music/vibrate phone to remind</string>
<string name="task_resend">Resend Message</string>
<string name="task_resend_tips">Resend forwarded records since N hours ago, 0=ALL</string>
<string name="task_resend_desc" formatted="false">Resend forwarding records since %s hours ago for %s</string>
@ -1115,11 +1153,24 @@
<string name="start_alarm">Start Alarm</string>
<string name="stop_alarm">Stop Alarm</string>
<string name="alarm_play_settings">Playback Settings</string>
<string name="alarm_play_settings">Play Music</string>
<string name="alarm_music">Specify Music</string>
<string name="alarm_music_tips">Opt., download mp3/ogg/wav to the Download directory.</string>
<string name="alarm_volume">Alarm Volume</string>
<string name="alarm_play_times">Play Times(0=Infinite)</string>
<string name="alarm_vibrate_settings">Vibrate Phone</string>
<string name="alarm_repeat_times">Repeat Times(0=Infinite)</string>
<string name="alarm_vibration_effect">Vibration Effect</string>
<string name="alarm_vibration_effect_tips">Syntax: =[strong], -[weak], _[no], 100ms each</string>
<string name="alarm_vibration_effect_1">Strong vibration</string>
<string name="alarm_vibration_effect_2">Weak vibration</string>
<string name="alarm_vibration_effect_3">No vibration</string>
<string name="alarm_flash_settings">Flash Phone</string>
<string name="alarm_flash_effect">Flash Effect</string>
<string name="alarm_flash_effect_tips">Syntax: 1 or X [turn on flash], 0 or O [turn off flash], each time 100ms</string>
<string name="alarm_flash_effect_1">Turn on flash 100ms</string>
<string name="alarm_flash_effect_2">Turn off flash 100ms</string>
<string name="alarm_settings_error">At least one of Play Music/Vibrate Phone/Flash Phone must be enabled</string>
<string name="invalid_tag" formatted="false">%s tag is invalid: %s</string>
<string name="invalid_task_name">Please input task name.</string>
@ -1142,4 +1193,5 @@
<string name="bluetooth_not_supported">Bluetooth not supported.</string>
<string name="start_discovery">Discovery</string>
<string name="invalid_bluetooth_mac_address">Bluetooth Mac Address is invalid, eg. AA:BB:CC:DD:EE:FF</string>
<string name="auto_start_redmi"><![CDATA[RedMi: Authorization Management -> Self-Start Management -> Allow Apps to Self-Start]]></string>
</resources>

View File

@ -1,5 +1,35 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="insert_tag_from">来源号码</string>
<string name="insert_tag_sms">短信内容</string>
<string name="insert_tag_package_name">APP包名</string>
<string name="insert_tag_app_name">APP应用名</string>
<string name="insert_tag_title">通知标题</string>
<string name="insert_tag_msg">通知内容</string>
<string name="insert_tag_card_slot">卡槽备注</string>
<string name="insert_tag_card_subid">卡槽主键</string>
<string name="insert_tag_receive_time">接收时间</string>
<string name="insert_tag_current_time">当前时间</string>
<string name="insert_tag_device_name">设备名称</string>
<string name="insert_tag_app_version">SmsF版本</string>
<string name="insert_tag_call_type">通话类型</string>
<string name="insert_tag_location">定位信息</string>
<string name="insert_tag_location_longitude">定位经度</string>
<string name="insert_tag_location_latitude">定位纬度</string>
<string name="insert_tag_location_address">定位地址</string>
<string name="insert_tag_battery_pct">电池电量</string>
<string name="insert_tag_battery_status">电池状态</string>
<string name="insert_tag_battery_plugged">充电方式</string>
<string name="insert_tag_battery_info">电池完整信息</string>
<string name="insert_tag_battery_info_simple">电池简单信息</string>
<string name="insert_tag_uid">用户UID</string>
<string name="insert_tag_ipv4">公网IPv4</string>
<string name="insert_tag_ipv6">公网IPv6</string>
<string name="insert_tag_ip_list">IP地址列表</string>
<string name="insert_tag_net_type">网络状态</string>
<string name="insert_tag_contact_name">来源姓名</string>
<string name="insert_tag_phone_area">来源归属</string>
<string name="type_param_sms">短信</string>
<string name="type_param_call">通话</string>
<string name="type_param_app">应用</string>
@ -58,6 +88,7 @@
<string name="url_help">https://gitee.com/pp/SmsForwarder/wikis/pages</string>
<string name="url_donation_link">https://gitee.com/pp/SmsForwarder.wiki/raw/master/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95.md</string>
<string name="url_wechat_miniprogram">https://gitee.com/pp/SmsForwarder/raw/main/pic/wechat_miniprogram.jpg</string>
<string name="url_tips">https://gitee.com/pp/SmsForwarder.wiki/raw/master/tips.json</string>
<string name="lab_yes"></string>
<string name="lab_no"></string>
@ -187,6 +218,13 @@
<string name="switch_rule_status">启用该条转发规则</string>
<string name="invalid_match_value">匹配的值不可为空</string>
<string name="invalid_call_type">通话类型不正确只能填写1到6的任意一个数字</string>
<string name="mon">周一</string>
<string name="tue">周二</string>
<string name="wed">周三</string>
<string name="thu">周四</string>
<string name="fri">周五</string>
<string name="sat">周六</string>
<string name="sun">周日</string>
<!--SenderActivity-->
<string name="delete_sender_title">发送通道操作确认</string>
<string name="delete_sender_tips">删除发送通道后会级联删除其相关的转发规则、转发日志的所有记录!\n\n确定删除该条发送通道?</string>
@ -216,8 +254,8 @@
<string name="invalid_wework_agent">企业ID、AgentID、Secret都不能为空</string>
<string name="invalid_dingtalk_inner_robot">AgentId、AppKey、AppSecret、UserIds都不能为空</string>
<string name="invalid_phone_num">接收手机号不能为空</string>
<string name="invalid_multi_match">多重匹配规则的第 %s 行格式有误</string>
<string name="invalid_regex_replace">正则替换内容的第 %s 行格式有误</string>
<string name="invalid_multi_match">多重匹配规则的第 %d 行格式有误</string>
<string name="invalid_regex_replace">正则替换内容的第 %d 行格式有误</string>
<string name="invalid_message_card">自定义消息卡片Json不合法</string>
<string name="email_host">主机</string>
<string name="smtp_port">端口</string>
@ -272,6 +310,10 @@
<string name="TelegramApiToken">ApiToken 或 自定义代理地址(http开头)</string>
<string name="TelegramChatId">被通知人(或群组)的ChatId</string>
<string name="Method" formatted="false">请求方式</string>
<string name="parse_mode">解析模式</string>
<string name="parse_mode_text">Text</string>
<string name="parse_mode_html">Html</string>
<string name="parse_mode_markdown">MarkdownV2</string>
<string name="SmsSimSlot">发送卡槽</string>
<string name="same_source">原进原出</string>
@ -311,17 +353,10 @@
<string name="custom_template">转发信息模版</string>
<string name="custom_template_tips">Tip按需插入内容标签留空使用默认模版</string>
<string name="insert_sender">来源号码</string>
<string name="insert_sender_app">APP包名</string>
<string name="insert_uid">UID</string>
<string name="insert_content">短信内容</string>
<string name="insert_title_app">通知标题</string>
<string name="insert_content_app">通知内容</string>
<string name="insert_extra">卡槽信息</string>
<string name="insert_time">接收时间</string>
<string name="insert_device_name">设备名称</string>
<string name="battery_setting">忽略电池优化设置</string>
<string name="battery_setting_tips">请设置为手动管理:允许自启动、允许关联启动、允许后台运行</string>
<string name="unknown_number">未知号码</string>
<string name="unknown_area">未知归属地</string>
<string name="unsupport">您的手机不支持此设置</string>
<string name="isIgnored">已将省电优化设置为无限制(不优化)</string>
<string name="isIgnored2">本界面无法直接操作系统的省电优化设置</string>
@ -358,6 +393,7 @@
<string name="appicon">应用图标</string>
<string name="user_app">用户应用</string>
<string name="system_app">系统应用</string>
<string name="tips_get_installed_apps">请先授予获取应用列表权限</string>
<string name="tips_notification">请先授予发送通知权限,有利于《短信转发器》保活!</string>
<string name="tips_notification_listener">请先授予《短信转发器》通知使用权否则无法转发APP通知已经自动关闭转发!</string>
<string name="pushplus_website">官网地址</string>
@ -404,6 +440,7 @@
<string name="GotifyWebServer">WebServer</string>
<string name="GotifyWebServerTips"><![CDATA[例https://push.ppps.cn/message?token=<apptoken>]]></string>
<string name="title_template">标题模板</string>
<string name="auto_copy">自动复制</string>
<string name="priority">优先级1 9</string>
<string name="dingtalk_robot">钉钉群机器人</string>
<string name="dingtalk_inner_robot">钉钉企业机器人</string>
@ -447,7 +484,7 @@
<string name="interval_label">递增间隔</string>
<string name="timeout_label">单次超时</string>
<string name="seconds"></string>
<string name="seconds_n">%s</string>
<string name="seconds_n">%d</string>
<string name="retry_label">最多重试</string>
<string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string>
<string name="test_sender_name">测试通道</string>
@ -513,7 +550,7 @@
<string name="no">No</string>
<string name="refresh">刷新</string>
<string name="tip_can_not_get_sim_infos">无法获取卡槽信息,请确认应用权限【获取手机信息】为【始终允许】</string>
<string name="tip_can_not_get_sim_info">未获取到卡槽%s中的SIM卡信息</string>
<string name="tip_can_not_get_sim_info">未获取到卡槽%d中的SIM卡信息</string>
<string name="auto_check">启动时检查</string>
<string name="check_update">检查更新</string>
<string name="join_preview_program">加入SmsF预览体验计划</string>
@ -541,6 +578,7 @@
<string name="bark_group_tips">可选,例:短信转发器</string>
<string name="bark_icon">消息图标</string>
<string name="bark_icon_tips">可选填写Url图片不要太大</string>
<string name="bark_call">持续提醒</string>
<string name="bark_sound">消息声音</string>
<string name="bark_sound_tips">可选minuet.caf</string>
<string name="bark_badge">消息角标</string>
@ -695,7 +733,7 @@
<string name="api_location_tips">远程查询手机定位,方便找回手机/防止老少走丢</string>
<string name="api_location_permission_tips">请先在【通用设置】中【启用GPS定位功能】</string>
<string name="location_longitude">经度:%s</string>
<string name="location_latitude">度:%s</string>
<string name="location_latitude">度:%s</string>
<string name="location_address">地址:%s</string>
<string name="location_time">时间:%s</string>
<string name="location_provider">供应商:%s</string>
@ -804,7 +842,7 @@
<string name="debug_mode_tips">将Log.*写入文件,以便排查问题;可导出到下载目录</string>
<string name="optional_components">可选组件:</string>
<string name="enable_cactus">启用 Cactus 增强保活措施(会增加耗电)</string>
<string name="enabe_cactus_tips">双进程前台服务/JobScheduler/WorkManager/1像素/无声音乐</string>
<string name="enable_cactus_tips">双进程前台服务/JobScheduler/WorkManager/1像素/无声音乐</string>
<string name="load_app_list">启动时异步获取已安装App列表</string>
<string name="load_app_list_tips">用于加速进入应用列表/编辑转发规则下拉选择/替换{{APP_NAME}}</string>
<string name="load_app_list_toast">开启异步获取已安装App列表时必选一个类型</string>
@ -965,8 +1003,8 @@
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
<string name="task_sender">启停通道</string>
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
<string name="task_alarm">声音警报</string>
<string name="task_alarm_tips">声音警报</string>
<string name="task_alarm">警报提醒</string>
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
<string name="task_resend">重发消息</string>
<string name="task_resend_tips">自动重发N小时以来的转发记录0=全部</string>
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
@ -1060,7 +1098,7 @@
<string name="calc_type_distance">根据GPS坐标计算距离</string>
<string name="calc_type_address">根据地址关键字判断</string>
<string name="longitude">经度</string>
<string name="latitude"></string>
<string name="latitude"></string>
<string name="distance1">以经纬度为中心,</string>
<string name="distance2">米半径建立电子围栏</string>
<string name="current_coordinates">当前坐标</string>
@ -1116,11 +1154,24 @@
<string name="start_alarm">启动警报</string>
<string name="stop_alarm">停止警报</string>
<string name="alarm_play_settings">播放设置</string>
<string name="alarm_play_settings">播放音乐</string>
<string name="alarm_music">指定音乐</string>
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
<string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次数(0=无限)</string>
<string name="alarm_vibrate_settings">振动手机</string>
<string name="alarm_repeat_times">重复次数(0=无限)</string>
<string name="alarm_vibration_effect">振动效果</string>
<string name="alarm_vibration_effect_tips">语法:=[强振动], -[弱震动], _[不振动], 每次100ms</string>
<string name="alarm_vibration_effect_1">强振动 100ms</string>
<string name="alarm_vibration_effect_2">弱振动 100ms</string>
<string name="alarm_vibration_effect_3">不振动 100ms</string>
<string name="alarm_flash_settings">闪烁手机</string>
<string name="alarm_flash_effect">闪光效果</string>
<string name="alarm_flash_effect_tips">语法1或X[开启闪光灯], 0或O[关闭闪光灯], 每次100ms</string>
<string name="alarm_flash_effect_1">开启闪光灯 100ms</string>
<string name="alarm_flash_effect_2">关闭闪光灯 100ms</string>
<string name="alarm_settings_error">播放音乐/振动手机/闪烁手机必须至少开启一个</string>
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
<string name="invalid_task_name">请输入任务名称</string>
@ -1143,4 +1194,5 @@
<string name="bluetooth_not_supported">不支持蓝牙设备</string>
<string name="start_discovery">搜索设备</string>
<string name="invalid_bluetooth_mac_address">蓝牙设备MAC地址无效例如AA:BB:CC:DD:EE:FF</string>
<string name="auto_start_redmi"><![CDATA[红米手机:授权管理 -> 自启动管理 -> 允许应用自启动]]></string>
</resources>

View File

@ -1,5 +1,35 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="insert_tag_from">來源號碼</string>
<string name="insert_tag_sms">簡訊內容</string>
<string name="insert_tag_package_name">APP包名</string>
<string name="insert_tag_app_name">APP應用名</string>
<string name="insert_tag_title">通知標題</string>
<string name="insert_tag_msg">通知內容</string>
<string name="insert_tag_card_slot">卡槽備註</string>
<string name="insert_tag_card_subid">卡槽主鍵</string>
<string name="insert_tag_receive_time">接收時間</string>
<string name="insert_tag_current_time">當前時間</string>
<string name="insert_tag_device_name">設備名稱</string>
<string name="insert_tag_app_version">SmsF版本</string>
<string name="insert_tag_call_type">通話類型</string>
<string name="insert_tag_location">定位資訊</string>
<string name="insert_tag_location_longitude">定位經度</string>
<string name="insert_tag_location_latitude">定位緯度</string>
<string name="insert_tag_location_address">定位地址</string>
<string name="insert_tag_battery_pct">電池電量</string>
<string name="insert_tag_battery_status">電池狀態</string>
<string name="insert_tag_battery_plugged">充電方式</string>
<string name="insert_tag_battery_info">電池完整資訊</string>
<string name="insert_tag_battery_info_simple">電池簡單資訊</string>
<string name="insert_tag_uid">用戶UID</string>
<string name="insert_tag_ipv4">公網IPv4</string>
<string name="insert_tag_ipv6">公網IPv6</string>
<string name="insert_tag_ip_list">IP地址列表</string>
<string name="insert_tag_net_type">網路狀態</string>
<string name="insert_tag_contact_name">來源姓名</string>
<string name="insert_tag_phone_area">来源歸屬</string>
<string name="type_param_sms">簡訊</string>
<string name="type_param_call">通話</string>
<string name="type_param_app">應用</string>
@ -58,6 +88,7 @@
<string name="url_help">https://gitee.com/pp/SmsForwarder/wikis/pages</string>
<string name="url_donation_link">https://gitee.com/pp/SmsForwarder.wiki/raw/master/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95.md</string>
<string name="url_wechat_miniprogram">https://gitee.com/pp/SmsForwarder/raw/main/pic/wechat_miniprogram.jpg</string>
<string name="url_tips">https://gitee.com/pp/SmsForwarder.wiki/raw/master/tips_tc.json</string>
<string name="lab_yes"></string>
<string name="lab_no"></string>
@ -187,6 +218,13 @@
<string name="switch_rule_status">啟用該條轉發規則</string>
<string name="invalid_match_value">匹配的值不可為空</string>
<string name="invalid_call_type">通話類型不正確只能填寫1到6的任意一個數字</string>
<string name="mon">星期一</string>
<string name="tue">星期二</string>
<string name="wed">星期三</string>
<string name="thu">星期四</string>
<string name="fri">星期五</string>
<string name="sat">星期六</string>
<string name="sun">星期日</string>
<!--SenderActivity-->
<string name="delete_sender_title">發送通道操作確認</string>
<string name="delete_sender_tips">刪除發送通道後會級聯刪除其相關的轉發規則、轉發日誌的所有記錄!\n\n確定刪除該條發送通道</string>
@ -216,8 +254,8 @@
<string name="invalid_wework_agent">企業ID、AgentID、Secret都不能為空</string>
<string name="invalid_dingtalk_inner_robot">AgentId、AppKey、AppSecret、UserIds都不能為空</string>
<string name="invalid_phone_num">接收手機號不能為空</string>
<string name="invalid_multi_match">多重匹配規則的第 %s 行格式有誤</string>
<string name="invalid_regex_replace">正則替換內容的第 %s 行格式有誤</string>
<string name="invalid_multi_match">多重匹配規則的第 %d 行格式有誤</string>
<string name="invalid_regex_replace">正則替換內容的第 %d 行格式有誤</string>
<string name="invalid_message_card">自定義消息卡片Json不合法</string>
<string name="email_host">主機</string>
<string name="smtp_port">端口</string>
@ -272,6 +310,10 @@
<string name="TelegramApiToken">ApiToken 或 自定義代理地址(http開頭)</string>
<string name="TelegramChatId">被通知人(或群組)的ChatId</string>
<string name="Method" formatted="false">請求方式</string>
<string name="parse_mode">解析模式</string>
<string name="parse_mode_text">Text</string>
<string name="parse_mode_html">Html</string>
<string name="parse_mode_markdown">MarkdownV2</string>
<string name="SmsSimSlot">發送卡槽</string>
<string name="same_source">原進原出</string>
@ -311,17 +353,10 @@
<string name="custom_template">轉發信息模版</string>
<string name="custom_template_tips">Tip按需插入內容標籤留空使用默認模版</string>
<string name="insert_sender">來源號碼</string>
<string name="insert_sender_app">APP包名</string>
<string name="insert_uid">UID</string>
<string name="insert_content">簡訊內容</string>
<string name="insert_title_app">通知標題</string>
<string name="insert_content_app">通知內容</string>
<string name="insert_extra">卡槽信息</string>
<string name="insert_time">接收時間</string>
<string name="insert_device_name">裝置名稱</string>
<string name="battery_setting">忽略電池優化設置</string>
<string name="battery_setting_tips">請設置為手動管理:允許自啟動、允許關聯啟動、允許後台運行</string>
<string name="unknown_number">未知號碼</string>
<string name="unknown_area">未知歸屬地</string>
<string name="unsupport">您的手機不支持此設置</string>
<string name="isIgnored">已將省電優化設置為無限制(不優化)</string>
<string name="isIgnored2">本界面無法直接操作系統的省電優化設置</string>
@ -358,6 +393,7 @@
<string name="appicon">應用圖標</string>
<string name="user_app">用戶應用</string>
<string name="system_app">系統應用</string>
<string name="tips_get_installed_apps">請先授予獲取應用列表權限</string>
<string name="tips_notification">請先授予發送通知權限,有利於《簡訊轉發器》保活!</string>
<string name="tips_notification_listener">請先授予《簡訊轉發器》通知使用權,否則無法轉發應用程式通知,已經自動關閉轉發!</string>
<string name="pushplus_website">官網地址</string>
@ -404,6 +440,7 @@
<string name="GotifyWebServer">WebServer</string>
<string name="GotifyWebServerTips"><![CDATA[例https://push.ppps.cn/message?token=<apptoken>]]></string>
<string name="title_template">標題模板</string>
<string name="auto_copy">自動複製</string>
<string name="priority">優先級1 9</string>
<string name="dingtalk_robot">釘釘群機器人</string>
<string name="dingtalk_inner_robot">釘釘企業機器人</string>
@ -447,7 +484,7 @@
<string name="interval_label">遞增間隔</string>
<string name="timeout_label">單次超時</string>
<string name="seconds"></string>
<string name="seconds_n">%s</string>
<string name="seconds_n">%d</string>
<string name="retry_label">最多重試</string>
<string name="test_sender_sms">【%s】恭喜您該發送通道測試成功請繼續添加轉發規則</string>
<string name="test_sender_name">測試通道</string>
@ -513,7 +550,7 @@
<string name="no">No</string>
<string name="refresh">刷新</string>
<string name="tip_can_not_get_sim_infos">無法獲取卡槽信息,請確認應用權限【獲取手機信息】為【始終允許】</string>
<string name="tip_can_not_get_sim_info">未獲取到卡槽%s中的SIM卡信息</string>
<string name="tip_can_not_get_sim_info">未獲取到卡槽%d中的SIM卡信息</string>
<string name="auto_check">啟動時檢查</string>
<string name="check_update">檢查更新</string>
<string name="join_preview_program">加入SmsF預覽體驗計劃</string>
@ -541,6 +578,7 @@
<string name="bark_group_tips">可選,例:簡訊轉發器</string>
<string name="bark_icon">消息圖標</string>
<string name="bark_icon_tips">可選填寫Url圖片不要太大</string>
<string name="bark_call">持續提醒</string>
<string name="bark_sound">消息聲音</string>
<string name="bark_sound_tips">可選minuet.caf</string>
<string name="bark_badge">消息角標</string>
@ -804,7 +842,7 @@
<string name="debug_mode_tips">將 Log.* 寫入檔案,以便排查問題;可匯出至下載目錄</string>
<string name="optional_components">可選組件:</string>
<string name="enable_cactus">啟用 Cactus 增強保活措施(會增加耗電)</string>
<string name="enabe_cactus_tips">雙進程前台服務/JobScheduler/WorkManager/1像素/無聲音樂</string>
<string name="enable_cactus_tips">雙進程前台服務/JobScheduler/WorkManager/1像素/無聲音樂</string>
<string name="load_app_list">啟動時異步獲取已安裝App列表</string>
<string name="load_app_list_tips">用於加速進入應用列表/編輯轉發規則下拉選擇/替換{{APP_NAME}}</string>
<string name="load_app_list_toast">開啟異步獲取已安裝App列表時必選一個類型</string>
@ -965,8 +1003,8 @@
<string name="task_rule_tips">控制【轉發規則】的啟用/禁用</string>
<string name="task_sender">啟停通道</string>
<string name="task_sender_tips">控制【發送通道】的啟用/禁用</string>
<string name="task_alarm">聲音警報</string>
<string name="task_alarm_tips">聲音警報</string>
<string name="task_alarm">警報提醒</string>
<string name="task_alarm_tips">播放音樂/震動手機發出提醒</string>
<string name="task_resend">重發消息</string>
<string name="task_resend_tips">自動重發N小時以來的轉發記錄0=全部</string>
<string name="task_resend_desc" formatted="false">自動重發%s小時以來%s的轉發記錄</string>
@ -1116,11 +1154,25 @@
<string name="start_alarm">啟動警報</string>
<string name="stop_alarm">停止警報</string>
<string name="alarm_play_settings">播放設置</string>
<string name="alarm_play_settings">播放音樂</string>
<string name="alarm_music">指定音樂</string>
<string name="alarm_music_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string>
<string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次數(0=無限)</string>
<string name="alarm_vibrate_settings">振動手機</string>
<string name="alarm_repeat_times">重複次數(0=無限)</string>
<string name="alarm_vibration_effect">振動效果</string>
<string name="alarm_vibration_effect_tips">語法:=[強振動], -[弱振動], _[不振動], 每次100ms</string>
<string name="alarm_vibration_effect_1">強振動 100ms</string>
<string name="alarm_vibration_effect_2">弱振動 100ms</string>
<string name="alarm_vibration_effect_3">不振動 100ms</string>
<string name="alarm_flash_settings">閃爍手機</string>
<string name="alarm_flash_effect">閃光效果</string>
<string name="alarm_flash_effect_tips">語法1或X[開啟閃光燈]0或O[關閉閃光燈]每次300毫秒</string>
<string name="alarm_flash_effect_1">開啟閃光燈 300毫秒</string>
<string name="alarm_flash_effect_2">關閉閃光燈 300毫秒</string>
<string name="alarm_settings_error">播放音樂/振動手機/閃爍手機必須至少開啟一個</string>
<string name="invalid_tag" formatted="false">%s 標籤無效:%s</string>
<string name="invalid_task_name">請輸入任務名稱</string>
@ -1143,4 +1195,5 @@
<string name="bluetooth_not_supported">不支援藍牙裝置</string>
<string name="start_discovery">搜索裝置</string>
<string name="invalid_bluetooth_mac_address">藍牙裝置MAC地址無效例如AA:BB:CC:DD:EE:FF</string>
<string name="auto_start_redmi"><![CDATA[紅米手機:授權管理 -> 自啟動管理 -> 允許應用自啟動]]></string>
</resources>

View File

@ -127,6 +127,7 @@
<item>\@sina.cn</item>
<item>\@139.com</item>
<item>\@189.cn</item>
<item>\@icloud.com</item>
<item>@string/other_mail_type</item><!--注意这里不能修改-->
</string-array>
@ -142,4 +143,4 @@
<item>UTF-32BE</item>
</string-array>
</resources>
</resources>

View File

@ -26,6 +26,38 @@
<string name="tag_ipv6" translatable="false">{{IPV6}}</string>
<string name="tag_ip_list" translatable="false">{{IP_LIST}}</string>
<string name="tag_net_type" translatable="false">{{NET_TYPE}}</string>
<string name="tag_contact_name" translatable="false">{{CONTACT_NAME}}</string>
<string name="tag_phone_area" translatable="false">{{PHONE_AREA}}</string>
<string name="insert_tag_from">来源号码</string>
<string name="insert_tag_sms">短信内容</string>
<string name="insert_tag_package_name">APP包名</string>
<string name="insert_tag_app_name">APP应用名</string>
<string name="insert_tag_title">通知标题</string>
<string name="insert_tag_msg">通知内容</string>
<string name="insert_tag_card_slot">卡槽备注</string>
<string name="insert_tag_card_subid">卡槽主键</string>
<string name="insert_tag_receive_time">接收时间</string>
<string name="insert_tag_current_time">当前时间</string>
<string name="insert_tag_device_name">设备名称</string>
<string name="insert_tag_app_version">SmsF版本</string>
<string name="insert_tag_call_type">通话类型</string>
<string name="insert_tag_location">定位信息</string>
<string name="insert_tag_location_longitude">定位经度</string>
<string name="insert_tag_location_latitude">定位纬度</string>
<string name="insert_tag_location_address">定位地址</string>
<string name="insert_tag_battery_pct">电池电量</string>
<string name="insert_tag_battery_status">电池状态</string>
<string name="insert_tag_battery_plugged">充电方式</string>
<string name="insert_tag_battery_info">电池完整信息</string>
<string name="insert_tag_battery_info_simple">电池简单信息</string>
<string name="insert_tag_uid">用户UID</string>
<string name="insert_tag_ipv4">公网IPv4</string>
<string name="insert_tag_ipv6">公网IPv6</string>
<string name="insert_tag_ip_list">IP地址列表</string>
<string name="insert_tag_net_type">网络状态</string>
<string name="insert_tag_contact_name">来源姓名</string>
<string name="insert_tag_phone_area">来源归属</string>
<string name="type_param_sms">短信</string>
<string name="type_param_call">通话</string>
@ -85,6 +117,7 @@
<string name="url_help">https://gitee.com/pp/SmsForwarder/wikis/pages</string>
<string name="url_donation_link">https://gitee.com/pp/SmsForwarder.wiki/raw/master/%E6%89%93%E8%B5%8F%E5%90%8D%E5%8D%95.md</string>
<string name="url_wechat_miniprogram">https://gitee.com/pp/SmsForwarder/raw/main/pic/wechat_miniprogram.jpg</string>
<string name="url_tips">https://gitee.com/pp/SmsForwarder.wiki/raw/master/tips.json</string>
<string name="lab_yes"></string>
<string name="lab_no"></string>
@ -214,6 +247,13 @@
<string name="switch_rule_status">启用该条转发规则</string>
<string name="invalid_match_value">匹配的值不可为空</string>
<string name="invalid_call_type">通话类型不正确只能填写1到6的任意一个数字</string>
<string name="mon">周一</string>
<string name="tue">周二</string>
<string name="wed">周三</string>
<string name="thu">周四</string>
<string name="fri">周五</string>
<string name="sat">周六</string>
<string name="sun">周日</string>
<!--SenderActivity-->
<string name="delete_sender_title">发送通道操作确认</string>
<string name="delete_sender_tips">删除发送通道后会级联删除其相关的转发规则、转发日志的所有记录!\n\n确定删除该条发送通道?</string>
@ -243,8 +283,8 @@
<string name="invalid_wework_agent">企业ID、AgentID、Secret都不能为空</string>
<string name="invalid_dingtalk_inner_robot">AgentId、AppKey、AppSecret、UserIds都不能为空</string>
<string name="invalid_phone_num">接收手机号不能为空</string>
<string name="invalid_multi_match">多重匹配规则的第 %s 行格式有误</string>
<string name="invalid_regex_replace">正则替换内容的第 %s 行格式有误</string>
<string name="invalid_multi_match">多重匹配规则的第 %d 行格式有误</string>
<string name="invalid_regex_replace">正则替换内容的第 %d 行格式有误</string>
<string name="invalid_message_card">自定义消息卡片Json不合法</string>
<string name="email_host">主机</string>
<string name="smtp_port">端口</string>
@ -299,6 +339,10 @@
<string name="TelegramApiToken">ApiToken 或 自定义代理地址(http开头)</string>
<string name="TelegramChatId">被通知人(或群组)的ChatId</string>
<string name="Method" formatted="false">请求方式</string>
<string name="parse_mode">解析模式</string>
<string name="parse_mode_text">Text</string>
<string name="parse_mode_html">Html</string>
<string name="parse_mode_markdown">MarkdownV2</string>
<string name="SmsSimSlot">发送卡槽</string>
<string name="same_source">原进原出</string>
@ -338,17 +382,10 @@
<string name="custom_template">转发信息模版</string>
<string name="custom_template_tips">Tip按需插入内容标签留空使用默认模版</string>
<string name="insert_sender">来源号码</string>
<string name="insert_sender_app">APP包名</string>
<string name="insert_uid">UID</string>
<string name="insert_content">短信内容</string>
<string name="insert_title_app">通知标题</string>
<string name="insert_content_app">通知内容</string>
<string name="insert_extra">卡槽信息</string>
<string name="insert_time">接收时间</string>
<string name="insert_device_name">设备名称</string>
<string name="battery_setting">忽略电池优化设置</string>
<string name="battery_setting_tips">请设置为手动管理:允许自启动、允许关联启动、允许后台运行</string>
<string name="unknown_number">未知号码</string>
<string name="unknown_area">未知归属地</string>
<string name="unsupport">您的手机不支持此设置</string>
<string name="isIgnored">已将省电优化设置为无限制(不优化)</string>
<string name="isIgnored2">本界面无法直接操作系统的省电优化设置</string>
@ -385,6 +422,7 @@
<string name="appicon">应用图标</string>
<string name="user_app">用户应用</string>
<string name="system_app">系统应用</string>
<string name="tips_get_installed_apps">请先授予获取应用列表权限</string>
<string name="tips_notification">请先授予发送通知权限,有利于《短信转发器》保活!</string>
<string name="tips_notification_listener">请先授予《短信转发器》通知使用权否则无法转发APP通知已经自动关闭转发!</string>
<string name="pushplus_website">官网地址</string>
@ -431,6 +469,7 @@
<string name="GotifyWebServer">WebServer</string>
<string name="GotifyWebServerTips"><![CDATA[例https://push.ppps.cn/message?token=<apptoken>]]></string>
<string name="title_template">标题模板</string>
<string name="auto_copy">自动复制</string>
<string name="priority">优先级1 9</string>
<string name="dingtalk_robot">钉钉群机器人</string>
<string name="dingtalk_inner_robot">钉钉企业机器人</string>
@ -474,7 +513,7 @@
<string name="interval_label">递增间隔</string>
<string name="timeout_label">单次超时</string>
<string name="seconds"></string>
<string name="seconds_n">%s</string>
<string name="seconds_n">%d</string>
<string name="retry_label">最多重试</string>
<string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string>
<string name="test_sender_name">测试通道</string>
@ -540,7 +579,7 @@
<string name="no">No</string>
<string name="refresh">刷新</string>
<string name="tip_can_not_get_sim_infos">无法获取卡槽信息,请确认应用权限【获取手机信息】为【始终允许】</string>
<string name="tip_can_not_get_sim_info">未获取到卡槽%s中的SIM卡信息</string>
<string name="tip_can_not_get_sim_info">未获取到卡槽%d中的SIM卡信息</string>
<string name="auto_check">启动时检查</string>
<string name="check_update">检查更新</string>
<string name="join_preview_program">加入SmsF预览体验计划</string>
@ -568,6 +607,7 @@
<string name="bark_group_tips">可选,例:短信转发器</string>
<string name="bark_icon">消息图标</string>
<string name="bark_icon_tips">可选填写Url图片不要太大</string>
<string name="bark_call">持续提醒</string>
<string name="bark_sound">消息声音</string>
<string name="bark_sound_tips">可选minuet.caf</string>
<string name="bark_badge">消息角标</string>
@ -722,7 +762,7 @@
<string name="api_location_tips">远程查询手机定位,方便找回手机/防止老少走丢</string>
<string name="api_location_permission_tips">请先在【通用设置】中【启用GPS定位功能】</string>
<string name="location_longitude">经度:%s</string>
<string name="location_latitude">度:%s</string>
<string name="location_latitude">度:%s</string>
<string name="location_address">地址:%s</string>
<string name="location_time">时间:%s</string>
<string name="location_provider">供应商:%s</string>
@ -831,7 +871,7 @@
<string name="debug_mode_tips">将Log.*写入文件,以便排查问题;可导出到下载目录</string>
<string name="optional_components">可选组件:</string>
<string name="enable_cactus">启用 Cactus 增强保活措施(会增加耗电)</string>
<string name="enabe_cactus_tips">双进程前台服务/JobScheduler/WorkManager/1像素/无声音乐</string>
<string name="enable_cactus_tips">双进程前台服务/JobScheduler/WorkManager/1像素/无声音乐</string>
<string name="load_app_list">启动时异步获取已安装App列表</string>
<string name="load_app_list_tips">用于加速进入应用列表/编辑转发规则下拉选择/替换{{APP_NAME}}</string>
<string name="load_app_list_toast">开启异步获取已安装App列表时必选一个类型</string>
@ -992,8 +1032,8 @@
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
<string name="task_sender">启停通道</string>
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
<string name="task_alarm">声音警报</string>
<string name="task_alarm_tips">播放音乐提醒</string>
<string name="task_alarm">警报提醒</string>
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
<string name="task_resend">重发消息</string>
<string name="task_resend_tips">自动重发N小时以来的转发记录0=全部</string>
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
@ -1087,7 +1127,7 @@
<string name="calc_type_distance">根据GPS坐标计算距离</string>
<string name="calc_type_address">根据地址关键字判断</string>
<string name="longitude">经度</string>
<string name="latitude"></string>
<string name="latitude"></string>
<string name="distance1">以经纬度为中心,</string>
<string name="distance2">米半径建立电子围栏</string>
<string name="current_coordinates">当前坐标</string>
@ -1143,11 +1183,24 @@
<string name="start_alarm">启动警报</string>
<string name="stop_alarm">停止警报</string>
<string name="alarm_play_settings">播放设置</string>
<string name="alarm_play_settings">播放音乐</string>
<string name="alarm_music">指定音乐</string>
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
<string name="alarm_volume">播放音量</string>
<string name="alarm_play_times">播放次数(0=无限)</string>
<string name="alarm_vibrate_settings">振动手机</string>
<string name="alarm_repeat_times">重复次数(0=无限)</string>
<string name="alarm_vibration_effect">振动效果</string>
<string name="alarm_vibration_effect_tips">语法:=[强振动], -[弱震动], _[不振动], 每次100ms</string>
<string name="alarm_vibration_effect_1">强振动 100ms</string>
<string name="alarm_vibration_effect_2">弱振动 100ms</string>
<string name="alarm_vibration_effect_3">不振动 100ms</string>
<string name="alarm_flash_settings">闪烁手机</string>
<string name="alarm_flash_effect">闪光效果</string>
<string name="alarm_flash_effect_tips">语法1或X[开启闪光灯], 0或O[关闭闪光灯], 每次100ms</string>
<string name="alarm_flash_effect_1">开启闪光灯 100ms</string>
<string name="alarm_flash_effect_2">关闭闪光灯 100ms</string>
<string name="alarm_settings_error">播放音乐/振动手机/闪烁手机必须至少开启一个</string>
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
<string name="invalid_task_name">请输入任务名称</string>
@ -1170,4 +1223,5 @@
<string name="bluetooth_not_supported">不支持蓝牙设备</string>
<string name="start_discovery">搜索设备</string>
<string name="invalid_bluetooth_mac_address">蓝牙设备MAC地址无效例如AA:BB:CC:DD:EE:FF</string>
<string name="auto_start_redmi"><![CDATA[红米手机:授权管理 -> 自启动管理 -> 允许应用自启动]]></string>
</resources>

View File

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

BIN
pic/sponsor.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -2,8 +2,8 @@ import java.util.regex.Matcher
import java.util.regex.Pattern
def build_versions = [:]
build_versions.version_code = 53
build_versions.version_name = "3.3.2"
build_versions.version_code = 54
build_versions.version_name = "3.3.3"
build_versions.min_sdk = 19
build_versions.target_sdk = 33
build_versions.build_tools = "33.0.1"
@ -38,13 +38,13 @@ versions.kotlin = '1.7.21'
//========xlibrary start========//
versions.xui = "dev~1.2.2-SNAPSHOT"
versions.xui = "1.2.2_250201"
versions.xupdate = "2.1.5"
versions.xaop = "dab6b77b2f" //1.1.0
versions.xaop = "1.1.0"
versions.xutil = "2.0.0"
versions.xhttp2 = "9115cfcd5a" //2.0.9
versions.xhttp2 = "2.0.10"
versions.xpage = "3.3.0"
versions.xrouter = "1.0.1"
versions.xrouter = "1.1.0"
//========xlibrary end========//
@ -132,18 +132,18 @@ deps.leakcanary = "com.squareup.leakcanary:leakcanary-android:$versions.leakcana
def xlibrary = [:]
xlibrary.xui = "com.github.pppscn:XUI:$versions.xui" //com.github.xuexiangjys:XUI
xlibrary.xupdate = "com.github.xuexiangjys:XUpdate:$versions.xupdate"
xlibrary.xaop_runtime = "com.github.pppscn.XAOP:xaop-runtime:$versions.xaop" //com.github.xuexiangjys.XAOP
xlibrary.xaop_plugin = "com.github.pppscn.XAOP:xaop-plugin:$versions.xaop" //com.github.xuexiangjys.XAOP
xlibrary.xutil_core = "com.github.xuexiangjys.XUtil:xutil-core:$versions.xutil"
xlibrary.xutil_sub = "com.github.xuexiangjys.XUtil:xutil-sub:$versions.xutil"
xlibrary.xhttp2 = "com.github.pppscn:XHttp2:$versions.xhttp2" //com.github.xuexiangjys:XHttp2
xlibrary.xpage_lib = "com.github.xuexiangjys.XPage:xpage-lib:$versions.xpage"
xlibrary.xpage_compiler = "com.github.xuexiangjys.XPage:xpage-compiler:$versions.xpage"
xlibrary.xrouter_runtime = "com.github.xuexiangjys.XRouter:xrouter-runtime:$versions.xrouter"
xlibrary.xrouter_compiler = "com.github.xuexiangjys.XRouter:xrouter-compiler:$versions.xrouter"
xlibrary.xrouter_plugin = "com.github.xuexiangjys.XRouter:xrouter-plugin:$versions.xrouter"
xlibrary.xui = "com.github.pppscn:XUI:$versions.xui"
xlibrary.xupdate = "com.github.pppscn:XUpdate:$versions.xupdate"
xlibrary.xaop_runtime = "com.github.pppscn.XAOP:xaop-runtime:$versions.xaop"
xlibrary.xaop_plugin = "com.github.pppscn.XAOP:xaop-plugin:$versions.xaop"
xlibrary.xutil_core = "com.github.pppscn.XUtil:xutil-core:$versions.xutil"
xlibrary.xutil_sub = "com.github.pppscn.XUtil:xutil-sub:$versions.xutil"
xlibrary.xhttp2 = "com.github.pppscn:XHttp2:$versions.xhttp2"
xlibrary.xpage_lib = "com.github.pppscn.XPage:xpage-lib:$versions.xpage"
xlibrary.xpage_compiler = "com.github.pppscn.XPage:xpage-compiler:$versions.xpage"
xlibrary.xrouter_runtime = "com.github.pppscn.XRouter:xrouter-runtime:$versions.xrouter"
xlibrary.xrouter_compiler = "com.github.pppscn.XRouter:xrouter-compiler:$versions.xrouter"
xlibrary.xrouter_plugin = "com.github.pppscn.XRouter:xrouter-plugin:$versions.xrouter"
deps.xlibrary = xlibrary
@ -219,4 +219,4 @@ project.buildscript.configurations.each { configuration ->
//kotlin插件
configuration.dependencies.add(dependencies.create("org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"))
}
}
}