add kyuubiran hooks

Signed-off-by: ACh Sulfate <xenonhydride@gmail.com>
This commit is contained in:
KyuubiRan
2022-02-07 16:13:00 +08:00
committed by ACh Sulfate
parent 0277b6f101
commit 1678c40f32
5 changed files with 989 additions and 0 deletions

View File

@@ -0,0 +1,264 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2022 qwq233@qwq2333.top
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/
package me.kyuubiran.dialog
import android.app.AlertDialog
import android.content.Context
import android.graphics.Color
import android.text.Editable
import android.text.TextWatcher
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.view.isVisible
import cc.ioctl.util.HostStyledViewBuilder
import cc.ioctl.util.LayoutHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.github.qauxv.R
import io.github.qauxv.ui.CustomDialog
import io.github.qauxv.util.LicenseStatus
import io.github.qauxv.util.Toasts
import me.kyuubiran.util.AutoRenewFireMgr
import me.kyuubiran.util.getDefaultCfg
import me.kyuubiran.util.getExFriendCfg
import me.kyuubiran.util.showToastByTencent
import java.util.regex.Pattern
object AutoRenewFireDialog {
private var currentEnable: Boolean? = null
private var currentEnableAuto = getDefaultCfg().getBooleanOrFalse(AutoRenewFireMgr.AUTO)
private val allowMsg = arrayListOf("早安", "", "晚安", "", "续火", "🔥")
var replyMsg: String = getExFriendCfg()!!.getStringOrDefault(AutoRenewFireMgr.MESSAGE, "续火")
var replyTime: String = getExFriendCfg()!!.getStringOrDefault(AutoRenewFireMgr.TIMEPRESET, "")
fun showMainDialog(context: Context) {
val dialog = CustomDialog.createFailsafe(context)
val ctx = dialog.context
val enable = getExFriendCfg()!!.getBooleanOrFalse(AutoRenewFireMgr.ENABLE)
currentEnable = enable
val msgEditText = EditText(ctx)
msgEditText.textSize = 16f
msgEditText.hint = "消息内容"
val _5 = LayoutHelper.dip2px(context, 5f)
msgEditText.setPadding(_5, _5, _5, _5 * 2)
msgEditText.setText(replyMsg)
msgEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) =
Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
replyMsg = s.toString()
}
override fun afterTextChanged(s: Editable?) =
Unit
})
msgEditText.isVisible = !currentEnableAuto
val timeEditText = EditText(ctx)
timeEditText.textSize = 16f
timeEditText.hint = "续火时间,默认 00:00:05格式 HH:MM:SS"
timeEditText.setPadding(_5, _5, _5, _5 * 2)
timeEditText.setText(replyTime)
timeEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
if (replyTime.isNotEmpty() && !stringTimeValidator(replyTime)) {
timeEditText.error = "时间格式错误"
}
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
replyTime = s.toString().replace("", ":")
if (replyTime.isNotEmpty() && !stringTimeValidator(replyTime)) {
timeEditText.error = "时间格式错误"
}
}
override fun afterTextChanged(s: Editable?) =
Unit
})
val checkBox = CheckBox(ctx)
checkBox.text = "开启自动续火"
checkBox.isChecked = enable
checkBox.setOnCheckedChangeListener { _, isChecked ->
currentEnable = isChecked
when (isChecked) {
true -> Toasts.showToast(ctx, Toasts.TYPE_INFO, "已开启自动续火", Toasts.LENGTH_SHORT)
false -> Toasts.showToast(ctx, Toasts.TYPE_INFO, "已关闭自动续火", Toasts.LENGTH_SHORT)
}
}
val checkBoxAuto = CheckBox(ctx)
checkBoxAuto.text = "每日一言"
checkBoxAuto.isChecked = currentEnableAuto
checkBoxAuto.setOnCheckedChangeListener { _, isChecked ->
currentEnableAuto = isChecked
msgEditText.isVisible = !currentEnableAuto
getDefaultCfg().putBoolean(AutoRenewFireMgr.AUTO, currentEnableAuto)
}
val params = LayoutHelper.newLinearLayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
_5 * 2
)
val linearLayout = LinearLayout(ctx)
linearLayout.orientation = LinearLayout.VERTICAL
linearLayout.addView(
HostStyledViewBuilder.subtitle(
context,
"说明:启用后将会在每天预设时间之后给对方发一条消息。\n此处开关为总开关,请单独在好友的设置页面打开自动续火开关。\n无论你是否给TA发过消息本功能都会发送续火消息。\n如果你在续火消息发送前添加了好友,那么之后将会发送给这个好友。\n如果今天已经发送过续火消息了,则再添加好友并不会发送续火消息。"
)
)
linearLayout.addView(
HostStyledViewBuilder.subtitle(
context,
"允许的续火消息:${allowMsg.joinToString(",")}",
Color.RED
)
)
linearLayout.addView(checkBox, params)
linearLayout.addView(checkBoxAuto, params)
linearLayout.addView(msgEditText, params)
linearLayout.addView(timeEditText, params)
val alertDialog = dialog.setTitle("自动续火设置")
.setView(linearLayout)
.setCancelable(true)
.setPositiveButton("确认") { _, _ ->
}.setNeutralButton("使用默认值") { _, _ ->
replyMsg = "[续火]"
replyTime = ""
save()
}
.setNegativeButton("取消", null)
.create() as AlertDialog
alertDialog.show()
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (replyMsg == "") {
Toasts.showToast(context, Toasts.TYPE_ERROR, "请输入自动续火内容", Toast.LENGTH_SHORT)
} else {
if (stringTimeValidator(replyTime) && msgValidator(replyMsg)) {
save()
Toasts.showToast(context, Toasts.TYPE_INFO, "设置已保存", Toast.LENGTH_SHORT)
alertDialog.cancel()
} else if (!stringTimeValidator(replyTime)) {
replyTime = ""
Toasts.showToast(context, Toasts.TYPE_ERROR, "时间格式错误", Toast.LENGTH_SHORT)
} else {
replyMsg = "续火"
Toasts.showToast(
context,
Toasts.TYPE_ERROR,
"非允许的续火消息",
Toast.LENGTH_SHORT
)
}
}
}
}
fun showSettingsDialog(ctx: Context) {
MaterialAlertDialogBuilder(ctx, R.style.MaterialDialog)
.setTitle("自动续火设置")
.setItems(arrayOf("重置列表", "重置时间")) { _, i ->
when (i) {
0 -> {
AutoRenewFireMgr.resetList()
ctx.showToastByTencent("已清空自动续火列表")
}
1 -> {
AutoRenewFireMgr.resetTime()
ctx.showToastByTencent("已重置下次续火时间")
}
}
}
.create()
.show()
}
fun showSetMsgDialog(context: Context, uin: String?) {
if (uin == null || uin.isEmpty() || !AutoRenewFireMgr.hasEnabled(uin)) return
val dialog = CustomDialog.createFailsafe(context)
val ctx = dialog.context
val editText = EditText(ctx)
editText.textSize = 16f
editText.hint = "续火消息内容"
val _5 = LayoutHelper.dip2px(context, 5f)
editText.setPadding(_5, _5, _5, _5 * 2)
editText.setText(AutoRenewFireMgr.getMsg(uin))
val linearLayout = LinearLayout(ctx)
linearLayout.orientation = LinearLayout.VERTICAL
linearLayout.addView(
editText,
LayoutHelper.newLinearLayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
_5 * 2
)
)
val alertDialog = dialog.setTitle("设置单独续火消息")
.setView(linearLayout)
.setCancelable(true)
.setPositiveButton("确认") { _, _ ->
Toasts.showToast(context, Toasts.TYPE_INFO, "设置自动续火消息成功", Toast.LENGTH_SHORT)
dialog.dismiss()
AutoRenewFireMgr.setMsg(uin, editText.text.toString())
}.setNeutralButton("使用默认值") { _, _ ->
AutoRenewFireMgr.setMsg(uin, "")
Toasts.showToast(context, Toasts.TYPE_ERROR, "已使用默认值", Toast.LENGTH_SHORT)
}
.setNegativeButton("取消", null)
.create() as AlertDialog
alertDialog.show()
}
fun save() {
val cfg = getExFriendCfg()!!
currentEnable?.let { cfg.putBoolean(AutoRenewFireMgr.ENABLE, it) }
cfg.putString(AutoRenewFireMgr.MESSAGE, replyMsg)
cfg.putString(
AutoRenewFireMgr.TIMEPRESET,
if (replyTime.isNotEmpty()) replyTime else "00:00:05"
)
cfg.save()
}
fun stringTimeValidator(time: String): Boolean {
val format = "([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]"
val pattern = Pattern.compile(format)
val matcher = pattern.matcher(time)
if (matcher.matches()) {
return true
}
return false
}
private fun msgValidator(msg: String): Boolean {
if (!LicenseStatus.isInsider()) {
if (msg !in allowMsg) {
return false
}
}
return true
}
}

View File

@@ -0,0 +1,145 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2022 qwq233@qwq2333.top
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/
package me.kyuubiran.hook
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.TextView
import cc.ioctl.util.Reflex
import io.github.qauxv.base.IUiItemAgent
import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.hook.CommonConfigFunctionHook
import io.github.qauxv.util.LicenseStatus
import kotlinx.coroutines.flow.MutableStateFlow
import me.kyuubiran.dialog.AutoRenewFireDialog
import me.kyuubiran.util.AutoRenewFireMgr
import me.kyuubiran.util.getExFriendCfg
import me.kyuubiran.util.showToastByTencent
import xyz.nextalone.util.*
//自动续火
@UiItemAgentEntry
@FunctionHookEntry
object AutoRenewFire : CommonConfigFunctionHook("kr_auto_renew_fire") {
var autoRenewFireStarted = false
override val name = "自动续火"
override val valueState: MutableStateFlow<String?>? = null
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.EXPERIMENTAL_CATEGORY
override val onUiItemClickListener: (IUiItemAgent, Activity, View) -> Unit = { _, activity, _ ->
AutoRenewFireDialog.showMainDialog(activity)
}
override fun initOnce(): Boolean = throwOrTrue {
if (!autoRenewFireStarted) {
AutoRenewFireMgr.doAutoSend()
autoRenewFireStarted = true
}
val FormSimpleItem = "com.tencent.mobileqq.widget.FormSwitchItem".clazz
"com.tencent.mobileqq.activity.ChatSettingActivity".clazz?.method("doOnCreate")?.hookAfter(this) {
//如果未启用 不显示按钮
if (!getExFriendCfg()!!.getBooleanOrFalse("kr_auto_renew_fire")) return@hookAfter
//获取 设为置顶 SwitchItem
val setToTopItem = it.thisObject.getAll(FormSimpleItem)?.first { item ->
item.get(TextView::class.java)?.text?.contains("置顶") ?: false
}
//如果SwitchItem不为空 说明为好友
if (setToTopItem != null) {
//创建SwitchItem对象
val autoRenewFireItem =
Reflex.newInstance(
FormSimpleItem,
it.thisObject,
Context::class.java
)
//拿到ViewGroup
val listView = (setToTopItem as View).parent as ViewGroup
//设置开关文本
Reflex.invokeVirtual(
autoRenewFireItem,
"setText",
"自动续火",
CharSequence::class.java
)
//添加View
listView.addView(autoRenewFireItem as View, 7)
//拿到好友相关信息
val intent = it.thisObject.get(Intent::class.java)
//QQ
val uin = intent?.getStringExtra("uin")
//昵称
val uinName = intent?.getStringExtra("uinname")
//设置按钮是否启用
Reflex.invokeVirtual(
autoRenewFireItem,
"setChecked",
AutoRenewFireMgr.hasEnabled(uin),
Boolean::class.java
)
//设置监听事件
Reflex.invokeVirtual(
autoRenewFireItem,
"setOnCheckedChangeListener",
object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(
p0: CompoundButton?,
p1: Boolean
) {
if (p1) {
AutoRenewFireMgr.add(uin)
(it.thisObject as Context).showToastByTencent("已开启与${uinName}的自动续火")
} else {
AutoRenewFireMgr.remove(uin)
(it.thisObject as Context).showToastByTencent("已关闭与${uinName}的自动续火")
}
}
},
CompoundButton.OnCheckedChangeListener::class.java
)
if (LicenseStatus.isInsider()) {
autoRenewFireItem.setOnLongClickListener { _ ->
AutoRenewFireDialog.showSetMsgDialog(
it.thisObject as Context,
uin
)
true
}
}
}
}
}
override var isEnabled: Boolean
get() = getExFriendCfg()?.getBooleanOrDefault("kr_auto_renew_fire", false) ?: false
set(value) {
getExFriendCfg()?.putBoolean("kr_auto_renew_fire", value)
}
}

View File

@@ -0,0 +1,218 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2022 qwq233@qwq2333.top
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/
package me.kyuubiran.hook
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.forEach
import androidx.core.view.forEachIndexed
import androidx.core.view.get
import androidx.core.view.size
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XC_MethodReplacement
import de.robv.android.xposed.XposedBridge
import io.github.qauxv.base.annotation.FunctionHookEntry
import io.github.qauxv.base.annotation.UiItemAgentEntry
import io.github.qauxv.config.ConfigManager
import io.github.qauxv.dsl.FunctionEntryRouter
import io.github.qauxv.util.Initiator
import io.github.qauxv.util.LicenseStatus
import io.github.qauxv.util.Log
import io.github.qauxv.util.QQVersion.*
import io.github.qauxv.util.requireMinQQVersion
import me.kyuubiran.util.setViewZeroSize
import me.singleneuron.tlb.ConfigTable
import xyz.nextalone.base.MultiItemDelayableHook
import xyz.nextalone.util.get
import xyz.nextalone.util.hide
import xyz.nextalone.util.throwOrTrue
import java.util.*
//侧滑栏精简
@FunctionHookEntry
@UiItemAgentEntry
object SimplifyQQSettingMe : MultiItemDelayableHook("SimplifyQQSettingMe") {
const val MidContentName = "SimplifyQQSettingMe::MidContentName"
override val preferenceTitle: String = "侧滑栏精简"
override val allItems = setOf<String>()
override val uiItemLocation = FunctionEntryRouter.Locations.Simplify.SLIDING_UI
override val isAvailable = requireMinQQVersion(QQ_8_4_1)
override val enableCustom = false
//Form 8.4.1
//Body = [0,1,0,0,0,1,4] || [0,1,0,0,0,1,4,0]
override var items: MutableList<String> = mutableListOf(
"夜间模式", //夜间模式 [0,1,0,0,0,1,6,1]
"登录达人", //登录达人 [0,1,0,0,0,1,6,2]
"当前温度", //当前温度 [0,1,0,0,0,1,6,3]
"开播啦鹅", //开播啦鹅 [0,1,0,0,0,1,4,0,1] || [0,1,0,0,0,1,4,0,1,1,1]
"我的小世界", //我的小世界 [0,1,0,0,0,1,4,0,2] || [0,1,0,0,0,1,4,0,1,2,1]
"开通会员", //开通会员 [0,1,0,0,0,1,4,0,3] || [0,1,0,0,0,1,4,0,1,3,1]
"我的钱包", //我的钱包 [0,1,0,0,0,1,4,0,4] || [0,1,0,0,0,1,4,0,1,4,1]
"个性装扮", //个性装扮 [0,1,0,0,0,1,4,0,5] || [0,1,0,0,0,1,4,0,1,5,1]
"情侣空间", //情侣空间 [0,1,0,0,0,1,4,0,6] || [0,1,0,0,0,1,4,0,1,6,1]
"我的收藏", //我的收藏 [0,1,0,0,0,1,4,0,7] || [0,1,0,0,0,1,4,0,1,7,1]
"我的相册", //我的相册 [0,1,0,0,0,1,4,0,8] || [0,1,0,0,0,1,4,0,1,8,1]
"我的文件", //我的文件 [0,1,0,0,0,1,4,0,9] || [0,1,0,0,0,1,4,0,1,9,1]
"我的日程", //我的日程 [0,1,0,0,0,1,4,0,10] || [0,1,0,0,0,1,4,0,1,10,1]
"我的视频", //我的视频 [0,1,0,0,0,1,4,0,11] || [0,1,0,0,0,1,4,0,1,11,1]
"小游戏", //小游戏 [0,1,0,0,0,1,4,0,12] || [0,1,0,0,0,1,4,0,1,12,1]
"腾讯文档", //腾讯文档 [0,1,0,0,0,1,4,0,13] || [0,1,0,0,0,1,4,0,1,13,1]
"每日打卡", //每日打卡 [0,1,0,0,0,1,4,0,14] || [0,1,0,0,0,1,4,0,1,14,1]
"王卡免流量特权", //开通王卡 [0,1,0,0,0,1,4,0,15] || [0,1,0,0,0,1,4,0,1,15,1]
"厘米秀",
)
private val keyWords: SortedMap<String, String> = sortedMapOf(
"" to "夜间模式",
"" to "登录达人",
"" to "登录达人",
"" to "开播啦鹅",
"世界" to "我的小世界",
"会员" to "开通会员",
"vip" to "开通会员",
"VIP" to "开通会员",
"钱包" to "我的钱包",
"装扮" to "个性装扮",
"情侣" to "情侣空间",
"相册" to "我的相册",
"收藏" to "我的收藏",
"文件" to "我的文件",
"日程" to "我的日程",
"视频" to "我的视频",
"游戏" to "小游戏",
"文档" to "腾讯文档",
"打卡" to "每日打卡",
"王卡" to "王卡免流量特权",
"流量" to "王卡免流量特权",
"送12个月" to "王卡免流量特权",
"厘米" to "厘米秀",
)
@Throws(Exception::class)
override fun initOnce() = throwOrTrue {
val clz = Initiator.load("com.tencent.mobileqq.activity.QQSettingMe")
XposedBridge.hookAllConstructors(clz, object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
if (LicenseStatus.sDisableCommonHooks) return
if (!isEnabled) return
try {
//中间部分(QQ会员 我的钱包等)
val midContentName = ConfigTable.getConfig<String>(MidContentName)
val midContentListLayout = if (requireMinQQVersion(QQ_8_6_5)) {
param.thisObject.get(midContentName, LinearLayout::class.java)
} else {
param.thisObject.get(midContentName, View::class.java) as LinearLayout
}
//底端部分 设置 夜间模式 达人 等
val underSettingsName = if (requireMinQQVersion(QQ_8_6_0)) "l" else "h"
val underSettingsLayout = if (requireMinQQVersion(QQ_8_6_5)) {
val parent = midContentListLayout?.parent?.parent as ViewGroup
var ret: LinearLayout? = null
parent.forEach {
if (it is LinearLayout && it[0] is LinearLayout) {
ret = it
}
}
ret
} else {
param.thisObject.get(underSettingsName, View::class.java) as LinearLayout
}
underSettingsLayout?.forEachIndexed { i, v ->
val tv = (v as LinearLayout)[1] as TextView
val text = tv.text
if (stringHit(text.toString()) || i == 3 && activeItems.contains("当前温度")) {
v.setViewZeroSize()
}
}
val midRemovedList: MutableList<Int> = mutableListOf()
midContentListLayout?.forEach {
val child = it as LinearLayout
val tv = if (child.size == 1) {
(child[0] as LinearLayout)[1]
} else {
child[1]
} as TextView
val text = tv.text.toString()
if (stringHit(text)) {
midRemovedList.add(midContentListLayout.indexOfChild(child))
}
}
midRemovedList.sorted().forEachIndexed { index, i ->
if (requireMinQQVersion(QQ_8_8_11)) {
midContentListLayout?.removeViewAt(i - index)
} else {
midContentListLayout?.getChildAt(i)?.hide()
}
}
} catch (t: Throwable) {
traceError(t)
}
}
})
XposedBridge.hookAllMethods(ViewTreeObserver::class.java, "dispatchOnGlobalLayout", object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) {
try {
XposedBridge.invokeOriginalMethod(param.method, param.thisObject, param.args)
} catch (e: Exception) {
if (e.stackTraceToString().contains("QQSettingMe")) {
Log.d("SimplifyQQSettingMe: have prevented crash")
} else {
throw e
}
}
}
})
}
private fun stringHit(string: String): Boolean {
val mActiveItems = activeItems
for (pair in keyWords) {
if (string.contains(pair.key) && mActiveItems.contains(pair.value)) {
return true
}
}
return false
}
//以下三个方法是曾经辉煌的MultiConfigItem和BaseMultiConfigDelayableHook最后的墓碑
fun hasConfig(name: String): Boolean {
val cfg = ConfigManager.getDefaultConfig()
return cfg.contains(this::class.java.simpleName + '$' + name)
}
fun setBooleanConfig(name: String, value: Boolean) {
val cfg = ConfigManager.getDefaultConfig()
cfg.putBoolean(this::class.java.simpleName + '$' + name, value)
}
fun getBooleanConfig(name: String): Boolean {
val cfg = ConfigManager.getDefaultConfig()
return cfg.getBooleanOrDefault(this::class.java.simpleName + '$' + name, false)
}
}

View File

@@ -0,0 +1,262 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2022 qwq233@qwq2333.top
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/
package me.kyuubiran.util
import android.os.Handler
import android.os.Looper
import com.google.gson.JsonParser
import io.github.qauxv.bridge.AppRuntimeHelper
import io.github.qauxv.bridge.ChatActivityFacade
import io.github.qauxv.bridge.SessionInfoImpl
import io.github.qauxv.util.hostInfo
import java.net.URL
import java.util.*
import kotlin.concurrent.thread
object AutoRenewFireMgr {
const val ENABLE = "kr_auto_renew_fire"
const val LIST = "kr_auto_renew_fire_list"
const val MESSAGE = "kr_auto_renew_fire_message"
const val AUTO = "kr_auto_renew_fire_auto"
const val TIME = "kr_auto_renew_fire_time"
const val TIMEPRESET = "kr_auto_renew_fire_time_preset"
private val mHandler = Handler(Looper.getMainLooper())
private val mRunnable = object : Runnable {
override fun run() {
if (autoRenewList.isEmpty()) return
if (needSend()) {
thread {
hostInfo.application.showToastBySystem("好耶 开始自动续火了 请不要关闭QQ哦")
for (u in autoRenewList) {
if (u.isGlobalMode) sendTextMessage(u.uin, autoRenewMsg, 0)
else sendTextMessage(u.uin, u.msg, 0)
Thread.sleep(5000)
}
hostInfo.application.showToastBySystem("好耶 续火完毕了")
}
}
mHandler.postDelayed(this, 600000L)
}
}
/**
* 发送一条文字消息
*
* @param uin 要发送的 群/好友
* @param content 要发送的内容
* @param type 类型当发送给好友为0.否则为1
*/
@JvmStatic
fun sendTextMessage(uin: String?, content: String?, type: Int) {
ChatActivityFacade.sendMessage(
AppRuntimeHelper.getQQAppInterface(), hostInfo.application,
SessionInfoImpl.createSessionInfo(uin, type), content
)
}
private val str: String
get() {
return getExFriendCfg()!!.getStringOrDefault(LIST, "")
}
private val tempList: ArrayList<AutoRenewFireItem> = arrayListOf()
private val autoRenewList: ArrayList<AutoRenewFireItem>
get() {
return strToArr()
}
private var autoRenewMsg: String = ""
set(value) {
field = value
val cfg = getExFriendCfg()!!
cfg.putString(MESSAGE, value)
cfg.save()
}
get() {
return if (getDefaultCfg().getBooleanOrFalse(AUTO)) {
return JsonParser.parseString(URL("https://v1.hitokoto.cn/?c=a").readText()).asJsonObject.get("hitokoto")
.toString().replace("\"", "")
} else getExFriendCfg()!!.getStringOrDefault(MESSAGE, "").split("|").random()
}
private fun strToArr(): ArrayList<AutoRenewFireItem> {
if (str.isEmpty()) return arrayListOf()
val strList = ArrayList(str.split("[||]"))
val arfItemList = ArrayList<AutoRenewFireItem>()
for (item in strList) {
arfItemList.add(AutoRenewFireItem.parse(item))
}
return arfItemList
}
@Suppress("SameParameterValue")
private fun save(list: ArrayList<AutoRenewFireItem>) {
val cfg = getExFriendCfg()!!
if (list.isEmpty()) {
cfg.putString(LIST, "")
cfg.save()
return
}
val sb = StringBuilder()
for (s in list.withIndex()) {
if (s.index != list.size - 1) {
sb.append(s.value).append("[||]")
} else {
sb.append(s.value)
}
}
cfg.putString(LIST, sb.toString())
cfg.save()
}
fun add(uin: String?, msg: String = "") {
if (uin == null) return
tempList.clear()
tempList.addAll(autoRenewList)
tempList.add(AutoRenewFireItem(uin, msg))
save(tempList)
}
fun add(uin: Long) {
add(uin.toString())
}
fun setMsg(uin: String, msg: String) {
tempList.clear()
tempList.addAll(autoRenewList)
for (u in tempList) {
if (u.uin == uin) {
u.msg = msg
}
}
save(tempList)
}
fun setMsg(uin: Long, msg: String) {
setMsg(uin.toString(), msg)
}
private fun getUser(uin: String?): AutoRenewFireItem? {
if (uin == null || uin.isEmpty()) return null
for (u in autoRenewList) {
if (uin == u.uin) return u
}
return null
}
fun getMsg(uin: String?): String {
val u = getUser(uin)
return u?.msg ?: ""
}
fun remove(uin: String?) {
if (uin == null) return
tempList.clear()
tempList.addAll(autoRenewList)
val removeItemList = ArrayList<AutoRenewFireItem>()
for (u in tempList) {
if (u.uin == uin) removeItemList.add(u)
}
tempList.removeAll(removeItemList)
save(tempList)
}
fun remove(uin: Long) {
remove(uin.toString())
}
fun hasEnabled(uin: String?): Boolean {
for (u in autoRenewList) {
if (u.uin == uin) return true
}
return false
}
fun hasEnabled(uin: Long): Boolean {
return hasEnabled(uin.toString())
}
private fun needSend(): Boolean {
val cfg = getExFriendCfg()!!
val nextTime = cfg.getLongOrDefault(TIME, 0L)
val presetTime = cfg.getStringOrDefault(TIMEPRESET, "00:00:05").run {
if (this.isEmpty()) {
return@run "00:00:05".split(":")
} else {
return@run this.split(":")
}
}
if (nextTime - System.currentTimeMillis() < 0) {
val cal = Calendar.getInstance(Locale.CHINA)
cal.add(Calendar.DATE, 1)
cal.set(Calendar.HOUR, presetTime[0].toInt())
cal.set(Calendar.MINUTE, presetTime[1].toInt())
cal.set(Calendar.SECOND, presetTime[2].toInt())
cal.set(Calendar.MILLISECOND, 0)
cfg.putLong(TIME, cal.timeInMillis)
cfg.save()
return true
}
return false
}
fun doAutoSend() {
mHandler.post(mRunnable)
}
fun resetTime() {
val cfg = getExFriendCfg()!!
cfg.putLong(TIME, 0L)
cfg.save()
}
fun resetList() {
val cfg = getExFriendCfg()!!
autoRenewList.clear()
cfg.putString(LIST, "")
cfg.save()
}
}
class AutoRenewFireItem(var uin: String, var msg: String = "") {
val isGlobalMode: Boolean
get() {
return msg.isEmpty()
}
override fun toString(): String {
val sb = StringBuilder()
sb.append(uin)
if (msg.isNotEmpty()) sb.append("[--]").append(msg)
return sb.toString()
}
companion object {
fun parse(string: String): AutoRenewFireItem {
val arr = string.split("[--]")
return if (arr.size == 2) {
AutoRenewFireItem(arr[0], arr[1])
} else {
AutoRenewFireItem(arr[0])
}
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2022 qwq233@qwq2333.top
* https://github.com/cinit/QAuxiliary
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version and our eula as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/
package me.kyuubiran.util
import android.content.Context
import android.os.Looper
import android.view.View
import android.widget.Toast
import cc.ioctl.util.Reflex
import io.github.qauxv.SyncUtils
import io.github.qauxv.util.Initiator
import io.github.qauxv.util.Toasts
import java.lang.reflect.Method
import java.lang.reflect.Modifier
fun Context.showToastBySystem(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
if (Looper.getMainLooper() == Looper.myLooper())
Toast.makeText(this, text, duration).show()
else SyncUtils.runOnUiThread { showToastBySystem(text, duration) }
}
fun Context.showToastByTencent(
text: CharSequence,
type: Int = Toasts.TYPE_INFO,
duration: Int = Toast.LENGTH_SHORT
) {
if (Looper.getMainLooper() == Looper.myLooper())
Toasts.showToast(this, type, text, duration)
else SyncUtils.runOnUiThread { showToastByTencent(text, duration) }
}
fun View.setViewZeroSize() {
this.layoutParams.height = 0
this.layoutParams.width = 0
}
fun getObjectOrNull(obj: Any?, objName: String, clz: Class<*>? = null): Any? {
return Reflex.getInstanceObjectOrNull(obj, objName, clz)
}
fun putObject(obj: Any, name: String, value: Any?, type: Class<*>? = null) {
Reflex.setInstanceObject(obj, name, type, value)
}
fun loadClass(clzName: String): Class<*> {
return Initiator.load(clzName)
}
fun getMethods(clzName: String): Array<Method> {
return Initiator.load(clzName).declaredMethods
}
fun getMethods(clz: Class<Any>): Array<Method> {
return clz.declaredMethods
}
val Method.isStatic: Boolean
get() = Modifier.isStatic(this.modifiers)
val Method.isPrivate: Boolean
get() = Modifier.isPrivate(this.modifiers)
val Method.isPublic: Boolean
get() = Modifier.isPublic(this.modifiers)
fun makeSpaceMsg(str: String): String {
val sb = StringBuilder()
if (str.length > 1) {
for (i in str.indices) {
sb.append(str[i])
if (i != str.length - 1) sb.append(" ")
}
} else {
sb.append(str)
}
return sb.toString()
}