add kyuubiran hooks
Signed-off-by: ACh Sulfate <xenonhydride@gmail.com>
This commit is contained in:
264
app/src/main/java/me/kyuubiran/dialog/AutoRenewFireDialog.kt
Normal file
264
app/src/main/java/me/kyuubiran/dialog/AutoRenewFireDialog.kt
Normal 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
|
||||
}
|
||||
}
|
||||
145
app/src/main/java/me/kyuubiran/hook/AutoRenewFire.kt
Normal file
145
app/src/main/java/me/kyuubiran/hook/AutoRenewFire.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
218
app/src/main/java/me/kyuubiran/hook/SimplifyQQSettingMe.kt
Normal file
218
app/src/main/java/me/kyuubiran/hook/SimplifyQQSettingMe.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
262
app/src/main/java/me/kyuubiran/util/AutoRenewFireMgr.kt
Normal file
262
app/src/main/java/me/kyuubiran/util/AutoRenewFireMgr.kt
Normal 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
app/src/main/java/me/kyuubiran/util/Utils.kt
Normal file
100
app/src/main/java/me/kyuubiran/util/Utils.kt
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user