19 Commits

Author SHA1 Message Date
976526ab79 fix: #350 2024-08-04 13:37:11 +08:00
cdc9ca1a72 fix: #351 2024-08-04 13:25:27 +08:00
609e87d0ec fix: move gpro api #346 2024-07-25 01:11:13 +08:00
cf01a25ea6 fix: #346 2024-07-25 00:35:56 +08:00
f090ef1937 fix rkey type 2024-07-22 07:54:29 +08:00
1cc033498c Version Restriction Notice 2024-07-19 03:35:21 +08:00
7eaa470dd2 fix #334 2024-07-17 17:27:16 +08:00
c49861d982 fix #340 2024-07-17 17:25:16 +08:00
36ed55d220 fix #337 2024-07-16 21:05:39 +08:00
e2f27cb36a fix #339 2024-07-16 20:56:09 +08:00
65ddca2ea4 fix: optimize import 2024-07-07 18:03:19 +08:00
a6f570e4f0 fix: remove debug 2024-07-07 18:02:41 +08:00
29dfc1590b feat: ocr 2024-07-07 17:51:51 +08:00
ffeda0a472 fix: msf core 2024-07-06 22:36:45 +08:00
0e5add2146 fix: some compatibility issues on 9.0.70+
It is not recommended to use this build for lower versions
2024-07-06 22:29:10 +08:00
be9ff46134 Merge pull request #325 from StarryKira/v1.0.9
fix get_image
2024-06-06 12:44:10 +08:00
1d6ac3e022 fix get_image 2024-06-06 12:32:59 +08:00
2db187e3d5 (#319) 2024-04-11 12:41:23 +08:00
18ec586b12 试着修复group_increase事件在大群中依然可能出现target_id=0的故障 (#318) 2024-04-11 01:04:52 +08:00
442 changed files with 24280 additions and 8939 deletions

View File

@ -3,7 +3,7 @@ name: Build Shamrock APK
on:
workflow_dispatch:
push:
branches: [ master ]
branches: [ v1.0.9 ]
paths-ignore:
- '**.md'
- '**.txt'
@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup JDK 17
uses: actions/setup-java@v4
@ -97,4 +97,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: "${{ env.SHAMROCK_VERSION_x86_64 }}"
path: "${{ env.APK_FILE_X86_64 }}"
path: "${{ env.APK_FILE_X86_64 }}"

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "kritor"]
path = kritor/kritor
url = https://github.com/KarinJS/kritor

View File

@ -16,19 +16,22 @@
## 简介
☘ 基于 Lsposed(**Non**-Riru) 实现 Kritor 标准的 QQ 机器人框架!
☘ 基于 Lsposed(**Non**-Riru) 实现 OneBot 标准的 QQ 机器人框架!
> 本项目仅提供学习与交流用途请在24小时内删除。
> 本项目目的是研究 Xposed 和 LSPosed 框架的使用。 Epic 框架开发相关知识。
> Riru可能导致封禁请减少使用。
> 如有违反法律,请联系删除。
> 请勿在任何平台宣传,宣扬,转发本项目,请勿恶意修改企业安装包造成相关企业产生损失,如有违背,必将追责到底。
> 官方论坛,[点我直达](https://forum.libfekit.so/)
## 兼容|迁移|替代 说明
- 一键移植:本项目基于 go-cqhttp 的文档进行开发实现
- 平行部署:可多平台部署。
仅支持QQ9.0.70以上的版本,低版本问题将不再修复与处理
- 一键移植:本项目基于 go-cqhttp 的文档进行开发实现。
- 平行部署:可多平台部署,未来将会支持 Docker 部署的教程。
## 相关项目
<table>

View File

@ -9,6 +9,6 @@ java {
}
dependencies {
//implementation(DEPENDENCY_PROTOBUF)
implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
}

View File

@ -1,9 +0,0 @@
package kritor.service
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
annotation class Grpc(
val serviceName: String,
val funcName: String,
)

View File

@ -0,0 +1,8 @@
package moe.fuqiuluo.symbols
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class OneBotHandler(
val actionName: String,
val alias: Array<String> = []
)

View File

@ -5,8 +5,6 @@ import kotlinx.serialization.protobuf.ProtoBuf
import kotlin.reflect.KClass
val EMPTY_BYTE_ARRAY = ByteArray(0)
interface Protobuf<T: Protobuf<T>>
inline fun <reified T: Protobuf<T>> ByteArray.decodeProtobuf(to: KClass<T>? = null): T {

View File

@ -17,7 +17,7 @@ android {
minSdk = 27
targetSdk = 34
versionCode = getVersionCode()
versionName = "1.1.0" + ".r${getGitCommitCount()}." + getVersionName()
versionName = "1.1.1.onebot" + ".r${getGitCommitCount()}." + getVersionName()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@ -201,8 +201,14 @@ dependencies {
implementation("io.coil-kt:coil-compose:2.4.0")
implementation(kotlinx("io-jvm", "0.1.16"))
implementation(ktor("server", "core"))
implementation(ktor("server", "host-common"))
implementation(ktor("server", "status-pages"))
implementation(ktor("server", "netty"))
implementation(ktor("server", "content-negotiation"))
implementation(ktor("client", "core"))
implementation(ktor("client", "okhttp"))
implementation(ktor("client", "content-negotiation"))
implementation(ktor("client", "cio"))
implementation(ktor("serialization", "kotlinx-json"))
implementation(project(":xposed"))

View File

@ -37,6 +37,7 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
${SRC_DIR}
md5.cpp
cqcode.cpp
silk.cpp
message.cpp
shamrock.cpp)

138
app/src/main/cpp/cqcode.cpp Normal file
View File

@ -0,0 +1,138 @@
#include <stdexcept>
#include "cqcode.h"
inline void replace_string(std::string& str, const std::string& from, const std::string& to) {
size_t startPos = 0;
while ((startPos = str.find(from, startPos)) != std::string::npos) {
str.replace(startPos, from.length(), to);
startPos += to.length();
}
}
inline int utf8_next_len(const std::string& str, size_t offset)
{
uint8_t c = (uint8_t)str[offset];
if (c >= 0xFC)
return 6;
else if (c >= 0xF8)
return 5;
else if (c >= 0xF0)
return 4;
else if (c >= 0xE0)
return 3;
else if (c >= 0xC0)
return 2;
else if (c > 0x00)
return 1;
else
return 0;
}
void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::string, std::string>>& dest) {
std::string cache;
bool is_start = false;
std::string key_tmp;
std::unordered_map<std::string, std::string> kv;
for(size_t i = 0; i < code.size(); i++) {
int utf8_char_len = utf8_next_len(code, i);
if(utf8_char_len == 0) {
continue;
}
std::string_view c(&code[i],utf8_char_len);
if (c == "[") {
if (is_start) {
throw illegal_code();
} else {
if (!cache.empty()) {
std::unordered_map<std::string, std::string> kv;
replace_string(cache, "&#91;", "[");
replace_string(cache, "&#93;", "]");
replace_string(cache, "&amp;", "&");
kv.emplace("_type", "text");
kv.emplace("text", cache);
dest.push_back(kv);
cache.clear();
}
std::string_view cq_flag(&code[i],4);
if(cq_flag == "[CQ:"){
is_start = true;
i += 3;
}else{
cache += c;
}
}
}
else if (c == "=") {
if (is_start) {
if (cache.empty()) {
throw illegal_code();
} else {
if (key_tmp.empty()) {
key_tmp.append(cache);
cache.clear();
} else {
cache += c;
}
}
} else {
cache += c;
}
}
else if (c == ",") {
if (is_start) {
if (kv.count("_type") == 0 && !cache.empty()) {
kv.emplace("_type", cache);
cache.clear();
} else {
if (!key_tmp.empty()) {
replace_string(cache, "&#91;", "[");
replace_string(cache, "&#93;", "]");
replace_string(cache, "&#44;", ",");
replace_string(cache, "&amp;", "&");
kv.emplace(key_tmp, cache);
cache.clear();
key_tmp.clear();
}
}
} else {
cache += c;
}
}
else if (c == "]") {
if (is_start) {
if (!cache.empty()) {
if (!key_tmp.empty()) {
replace_string(cache, "&#91;", "[");
replace_string(cache, "&#93;", "]");
replace_string(cache, "&#44;", ",");
replace_string(cache, "&amp;", "&");
kv.emplace(key_tmp, cache);
} else {
kv.emplace("_type", cache);
}
dest.push_back(kv);
kv.clear();
key_tmp.clear();
cache.clear();
is_start = false;
}
} else {
cache += c;
}
}
else {
cache += c;
i += (utf8_char_len - 1);
}
}
if (!cache.empty()) {
std::unordered_map<std::string, std::string> kv;
replace_string(cache, "&#91;", "[");
replace_string(cache, "&#93;", "]");
replace_string(cache, "&amp;", "&");
kv.emplace("_type", "text");
kv.emplace("text", cache);
dest.push_back(kv);
}
}

View File

@ -0,0 +1,87 @@
#include "jni.h"
#include <vector>
#include <string>
#include <algorithm>
struct Honor {
int id;
std::string name;
std::string icon_url;
int priority;
};
int calc_honor_flag(int honor_id, char honor_flag);
jobject make_honor_object(JNIEnv *env, jobject user_id, const Honor& honor);
extern "C"
JNIEXPORT jobject JNICALL
Java_moe_fuqiuluo_shamrock_remote_action_handlers_GetTroopHonor_nativeDecodeHonor(JNIEnv *env, jobject thiz,
jstring user_id,
jint honor_id,
jbyte honor_flag) {
static std::vector<Honor> honor_list = {
Honor{1, "龙王", "https://qzonestyle.gtimg.cn/aoi/sola/20200213150116_n4PxCiurbm.png", 1},
Honor{2, "群聊之火", "https://qzonestyle.gtimg.cn/aoi/sola/20200217190136_92JEGFKC5k.png", 3},
Honor{3, "群聊炽焰", "https://qzonestyle.gtimg.cn/aoi/sola/20200217190204_zgCTeSrMq1.png", 4},
Honor{5, "冒尖小春笋", "https://qzonestyle.gtimg.cn/aoi/sola/20200213150335_tUJCAtoKVP.png", 5},
Honor{6, "快乐源泉", "https://qzonestyle.gtimg.cn/aoi/sola/20200213150434_3tDmsJExCP.png", 7},
Honor{7, "学术新星", "https://sola.gtimg.cn/aoi/sola/20200515140645_j0X6gbuHNP.png", 8},
Honor{8, "顶尖学霸", "https://sola.gtimg.cn/aoi/sola/20200515140639_0CtWOpfVzK.png", 9},
Honor{9, "至尊学神", "https://sola.gtimg.cn/aoi/sola/20200515140628_P8UEYBjMBT.png", 10},
Honor{10, "一笔当先", "https://sola.gtimg.cn/aoi/sola/20200515140654_4r94tSCdaB.png", 11},
Honor{11, "奋进小翠竹", "https://sola.gtimg.cn/aoi/sola/20200812151819_wbj6z2NGoB.png", 6},
Honor{12, "氛围魔杖", "https://sola.gtimg.cn/aoi/sola/20200812151831_4ZJgQCaD1H.png", 2},
Honor{13, "壕礼皇冠", "https://sola.gtimg.cn/aoi/sola/20200930154050_juZOAMg7pt.png", 12},
};
int flag = calc_honor_flag(honor_id, honor_flag);
if ((honor_id != 1 && honor_id != 2 && honor_id != 3) || flag != 1) {
auto honor = *std::find_if(honor_list.begin(), honor_list.end(), [&honor_id](auto &honor) {
return honor.id == honor_id;
});
return make_honor_object(env, user_id, honor);
} else {
auto honor = *std::find_if(honor_list.begin(), honor_list.end(), [&honor_id](auto &honor) {
return honor.id == honor_id;
});
std::string url = "https://static-res.qq.com/static-res/groupInteract/vas/a/" + std::to_string(honor_id) + "_1.png";
honor = Honor{honor_id, honor.name, url, honor.priority};
return make_honor_object(env, user_id, honor);
}
}
int calc_honor_flag(int honor_id, char honor_flag) {
int flag;
if (honor_flag == 0) {
return 0;
}
if (honor_id == 1) {
flag = honor_flag;
} else if (honor_id == 2 || honor_id == 3) {
flag = honor_flag >> 2;
} else if (honor_id != 4) {
return 0;
} else {
flag = honor_flag >> 4;
}
return flag & 3;
}
jobject make_honor_object(JNIEnv *env, jobject user_id, const Honor& honor) {
jclass GroupMemberHonor = env->FindClass("moe/fuqiuluo/shamrock/remote/service/data/GroupMemberHonor");
jmethodID GroupMemberHonor_init = env->GetMethodID(GroupMemberHonor, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
auto user_id_str = (jstring) user_id;
jstring honor_desc = env->NewStringUTF(honor.name.c_str());
jstring uin_name = env->NewStringUTF("");
jstring honor_icon_url = env->NewStringUTF(honor.icon_url.c_str());
jobject ret = env->NewObject(GroupMemberHonor, GroupMemberHonor_init, user_id_str, uin_name, honor_icon_url, 0, honor.id, honor_desc);
env->DeleteLocalRef(GroupMemberHonor);
env->DeleteLocalRef(user_id_str);
env->DeleteLocalRef(honor_desc);
env->DeleteLocalRef(honor_icon_url);
return ret;
}

View File

@ -0,0 +1,20 @@
#ifndef UNTITLED_CQCODE_H
#define UNTITLED_CQCODE_H
#include <string>
#include <unordered_map>
#include <vector>
#include <exception>
class illegal_code: std::exception {
public:
[[nodiscard]] const char * what() const noexcept override {
return "Error cq code.";
}
};
void decode_cqcode(const std::string& code, std::vector<std::unordered_map<std::string, std::string>>& dest);
void encode_cqcode(const std::vector<std::unordered_map<std::string, std::string>>& segment, std::string& dest);
#endif //UNTITLED_CQCODE_H

View File

@ -1,4 +1,5 @@
#include "jni.h"
#include "cqcode.h"
#include <random>
inline void replace_string(std::string& str, const std::string& from, const std::string& to) {
@ -11,7 +12,7 @@ inline void replace_string(std::string& str, const std::string& from, const std:
extern "C"
JNIEXPORT jlong JNICALL
Java_qq_service_msg_MessageHelper_createMessageUniseq(JNIEnv *env, jobject thiz,
Java_moe_fuqiuluo_shamrock_helper_MessageHelper_createMessageUniseq(JNIEnv *env, jobject thiz,
jint chat_type,
jlong time) {
static std::random_device rd;
@ -31,6 +32,123 @@ Java_moe_fuqiuluo_shamrock_helper_MessageHelper_getChatType(JNIEnv *env, jobject
return (int32_t) ((int64_t) msg_id & 0xffL);
}
extern "C"
JNIEXPORT jobject JNICALL
Java_moe_fuqiuluo_shamrock_helper_MessageHelper_nativeDecodeCQCode(JNIEnv *env, jobject thiz,
jstring code) {
jclass ArrayList = env->FindClass("java/util/ArrayList");
jmethodID NewArrayList = env->GetMethodID(ArrayList, "<init>", "()V");
jmethodID ArrayListAdd = env->GetMethodID(ArrayList, "add", "(Ljava/lang/Object;)Z");
jobject arrayList = env->NewObject(ArrayList, NewArrayList);
const char* cCode = env->GetStringUTFChars(code, nullptr);
std::string cppCode = cCode;
std::vector<std::unordered_map<std::string, std::string>> dest;
try {
decode_cqcode(cppCode, dest);
} catch (illegal_code& code) {
return arrayList;
}
jclass HashMap = env->FindClass("java/util/HashMap");
jmethodID NewHashMap = env->GetMethodID(HashMap, "<init>", "()V");
jclass String = env->FindClass("java/lang/String");
jmethodID NewString = env->GetMethodID(String, "<init>", "([BLjava/lang/String;)V");
jstring charset = env->NewStringUTF("UTF-8");
jmethodID put = env->GetMethodID(HashMap, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
for (auto& map : dest) {
jobject hashMap = env->NewObject(HashMap, NewHashMap);
for (const auto& pair : map) {
jbyteArray keyArray = env->NewByteArray((int) pair.first.size());
jbyteArray valueArray = env->NewByteArray((int) pair.second.size());
env->SetByteArrayRegion(keyArray, 0, (int) pair.first.size(), (jbyte*)pair.first.c_str());
env->SetByteArrayRegion(valueArray, 0, (int) pair.second.size(), (jbyte*)pair.second.c_str());
auto key = (jstring) env->NewObject(String, NewString, keyArray, charset);
auto value = (jstring) env->NewObject(String, NewString, valueArray, charset);
env->CallObjectMethod(hashMap, put, key, value);
}
env->CallBooleanMethod(arrayList, ArrayListAdd, hashMap);
}
env->DeleteLocalRef(ArrayList);
env->DeleteLocalRef(HashMap);
env->DeleteLocalRef(String);
env->DeleteLocalRef(charset);
env->ReleaseStringUTFChars(code, cCode);
return arrayList;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_moe_fuqiuluo_shamrock_helper_MessageHelper_nativeEncodeCQCode(JNIEnv *env, jobject thiz,
jobject segment_list) {
jclass List = env->FindClass("java/util/List");
jmethodID ListSize = env->GetMethodID(List, "size", "()I");
jmethodID ListGet = env->GetMethodID(List, "get", "(I)Ljava/lang/Object;");
jclass Map = env->FindClass("java/util/Map");
jmethodID MapGet = env->GetMethodID(Map, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
jmethodID entrySetMethod = env->GetMethodID(Map, "entrySet", "()Ljava/util/Set;");
jclass setClass = env->FindClass("java/util/Set");
jmethodID iteratorMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
jclass entryClass = env->FindClass("java/util/Map$Entry");
jmethodID getKeyMethod = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
jmethodID getValueMethod = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
std::string result;
jint size = env->CallIntMethod(segment_list, ListSize);
for (int i = 0; i < size; i++ ) {
jobject segment = env->CallObjectMethod(segment_list, ListGet, i);
jobject entrySet = env->CallObjectMethod(segment, entrySetMethod);
jobject iterator = env->CallObjectMethod(entrySet, iteratorMethod);
auto type = (jstring) env->CallObjectMethod(segment, MapGet, env->NewStringUTF("_type"));
auto typeString = env->GetStringUTFChars(type, nullptr);
if (strcmp(typeString, "text") == 0) {
auto text = (jstring) env->CallObjectMethod(segment, MapGet, env->NewStringUTF("text"));
auto textString = env->GetStringUTFChars(text, nullptr);
std::string tmpValue = textString;
replace_string(tmpValue, "&", "&amp;");
replace_string(tmpValue, "[", "&#91;");
replace_string(tmpValue, "]", "&#93;");
replace_string(tmpValue, ",", "&#44;");
result.append(tmpValue);
env->ReleaseStringUTFChars(text, textString);
} else {
result.append("[CQ:");
result.append(typeString);
while (env->CallBooleanMethod(iterator, env->GetMethodID(env->GetObjectClass(iterator), "hasNext", "()Z"))) {
jobject entry = env->CallObjectMethod(iterator, env->GetMethodID(env->GetObjectClass(iterator), "next", "()Ljava/lang/Object;"));
auto key = (jstring) env->CallObjectMethod(entry, getKeyMethod);
auto value = (jstring) env->CallObjectMethod(entry, getValueMethod);
auto keyString = env->GetStringUTFChars(key, nullptr);
auto valueString = env->GetStringUTFChars(value, nullptr);
if (strcmp(keyString, "_type") != 0) {
std::string tmpValue = valueString;
replace_string(tmpValue, "&", "&amp;");
replace_string(tmpValue, "[", "&#91;");
replace_string(tmpValue, "]", "&#93;");
replace_string(tmpValue, ",", "&#44;");
result.append(",").append(keyString).append("=").append(tmpValue);
}
env->ReleaseStringUTFChars(key, keyString);
env->ReleaseStringUTFChars(value, valueString);
env->DeleteLocalRef(entry);
env->DeleteLocalRef(key);
env->DeleteLocalRef(value);
}
result.append("]");
}
env->ReleaseStringUTFChars(type, typeString);
}
env->DeleteLocalRef(List);
env->DeleteLocalRef(Map);
env->DeleteLocalRef(setClass);
env->DeleteLocalRef(entryClass);
return env->NewStringUTF(result.c_str());
}
extern "C"
JNIEXPORT jlong JNICALL
Java_moe_fuqiuluo_shamrock_helper_MessageHelper_insertChatTypeToMsgId(JNIEnv *env, jobject thiz,

View File

@ -12,7 +12,8 @@
extern "C"
JNIEXPORT jstring JNICALL
Java_moe_fuqiuluo_shamrock_xposed_actions_interacts_Init_testNativeLibrary(JNIEnv *env, jobject thiz) {
Java_moe_fuqiuluo_shamrock_xposed_hooks_PullConfig_00024Companion_testNativeLibrary(JNIEnv *env,
jobject thiz) {
return env->NewStringUTF("加载Shamrock库成功~");
}

View File

@ -4,7 +4,6 @@ package moe.fuqiuluo.shamrock
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -53,7 +52,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
@ -67,7 +65,6 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import moe.fuqiuluo.shamrock.tools.GlobalUi
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.app.Logger
import moe.fuqiuluo.shamrock.ui.app.RuntimeState
@ -82,12 +79,13 @@ import moe.fuqiuluo.shamrock.ui.theme.RANDOM_SUB_TITLE
import moe.fuqiuluo.shamrock.ui.theme.RANDOM_TITLE
import moe.fuqiuluo.shamrock.ui.theme.ShamrockTheme
import moe.fuqiuluo.shamrock.ui.tools.NoIndication
import moe.fuqiuluo.shamrock.ui.tools.ShamrockTab
import moe.fuqiuluo.shamrock.ui.tools.ShamrockTabV2
import moe.fuqiuluo.shamrock.ui.tools.getShamrockVersion
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LaunchedEffect(Unit) {
while (true) {
@ -107,9 +105,8 @@ class MainActivity : ComponentActivity() {
isAppearanceLightStatusBars = true
}
WindowCompat.setDecorFitsSystemWindows(window, true)
broadcastToModule { putExtra("__cmd", "fetchPort") }
}
GlobalUi = Handler(mainLooper)
}
}
@ -165,7 +162,7 @@ private fun AppMainView() {
}
val ctx = LocalContext.current
LaunchedEffect(isFined) {
LaunchedEffect(isFined.value) {
if (isFined.value) {
AppRuntime.log(LocalString.logCentralLoadSuccessfully)
Toast.makeText(ctx, LocalString.frameworkYes, Toast.LENGTH_SHORT).show()
@ -296,25 +293,70 @@ private fun AnimatedTab(
val lastSelectedState = remember {
mutableIntStateOf(0)
}
val enter = remember {
scaleIn(animationSpec = TweenSpec(150, easing = FastOutLinearInEasing))
}
val exit = remember {
scaleOut(animationSpec = TweenSpec(150, easing = FastOutSlowInEasing))
}
val defaultConst = SELECTED_TABLE[index * 2]
val selectedConst = SELECTED_TABLE[(index * 2) + 1]
val isFirst: Boolean = (lastSelectedState.value and defaultConst) != defaultConst
ShamrockTab(
var icon: @Composable (() -> Unit)? = null
var text: @Composable (() -> Unit)? = null
if (curSelected) {
text = {
AnimatedVisibility(visibleState = MutableTransitionState(false).also {
it.targetState =
isFirst || lastSelectedState.value and selectedConst == selectedConst
}, enter = enter, exit = exit, modifier = Modifier) {
Text(
text = titleWithIcon.first,
color = GlobalColor.TabItem,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(bottom = 5.dp)
.indication(
remember { MutableInteractionSource() },
rememberRipple(color = Color.Transparent)
)
)
}
}
} else {
icon = {
Icon(
painter = painterResource(id = titleWithIcon.second),
contentDescription = titleWithIcon.first,
tint = Color.Unspecified,
modifier = Modifier
.height(24.dp)
.width(24.dp)
.padding(bottom = 5.dp)
.indication(
remember { MutableInteractionSource() },
rememberRipple(color = Color.Transparent)
)
)
}
}
ShamrockTabV2(
selected = curSelected,
onClick = {
scope.launch {
state.scrollToPage(index, 0f)
}
},
text = text,
icon = icon,
selectedContentColor = Color.Transparent,
unselectedContentColor = Color.Transparent,
indication = null,
titleWithIcon = titleWithIcon,
visibleState = MutableTransitionState(false).also {
it.targetState = isFirst || lastSelectedState.value and selectedConst == selectedConst
}
indication = null
)
lastSelectedState.value.let {
var tmp = it

View File

@ -1,2 +0,0 @@
package moe.fuqiuluo.shamrock.app.config

View File

@ -1,33 +0,0 @@
package moe.fuqiuluo.shamrock.app.config
import android.content.Context
import moe.fuqiuluo.shamrock.config.ConfigKey
import moe.fuqiuluo.shamrock.ui.service.internal.broadcastToModule
object ShamrockConfig {
internal fun getConfigPref(ctx: Context) = ctx.getSharedPreferences("config", 0)
internal inline operator fun <reified Type> get(ctx: Context, key: ConfigKey<Type>): Type {
val preferences = getConfigPref(ctx)
return when(Type::class) {
Int::class -> preferences.getInt(key.name(), key.default() as Int) as Type
Long::class -> preferences.getLong(key.name(), key.default() as Long) as Type
String::class -> preferences.getString(key.name(), key.default() as String) as Type
Boolean::class -> preferences.getBoolean(key.name(), key.default() as Boolean) as Type
else -> throw IllegalArgumentException("Unsupported type")
}
}
internal inline operator fun <reified Type> set(ctx: Context, key: ConfigKey<Type>, value: Type) {
val preferences = getConfigPref(ctx)
val editor = preferences.edit()
when(Type::class) {
Int::class -> editor.putInt(key.name(), value as Int)
Long::class -> editor.putLong(key.name(), value as Long)
String::class -> editor.putString(key.name(), value as String)
Boolean::class -> editor.putBoolean(key.name(), value as Boolean)
else -> throw IllegalArgumentException("Unsupported type")
}
editor.apply()
}
}

View File

@ -0,0 +1,395 @@
package moe.fuqiuluo.shamrock.ui.app
import android.content.Context
import moe.fuqiuluo.shamrock.ui.service.internal.broadcastToModule
object ShamrockConfig {
fun getSSLKeyPath(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("key_store", "")!!
}
fun setSSLKeyPath(ctx: Context, path: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("key_store", path).apply()
pushUpdate(ctx)
}
fun getSSLPort(ctx: Context): Int {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getInt("ssl_port", 5701)
}
fun setSSLPort(ctx: Context, port: Int) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putInt("ssl_port", port).apply()
pushUpdate(ctx)
}
fun getSSLAlias(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("ssl_alias", "")!!
}
fun setSSLAlias(ctx: Context, alias: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("ssl_alias", alias).apply()
pushUpdate(ctx)
}
fun getSSLPwd(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("ssl_pwd", "")!!
}
fun setSSLPwd(ctx: Context, alias: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("ssl_pwd", alias).apply()
pushUpdate(ctx)
}
fun getSSLPrivatePwd(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("ssl_private_pwd", "")!!
}
fun setSSLPrivatePwd(ctx: Context, alias: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("ssl_private_pwd", alias).apply()
pushUpdate(ctx)
}
fun getHttpAddr(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("http_addr", "")!!
}
fun setHttpAddr(ctx: Context, v: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("http_addr", v).apply()
pushUpdate(ctx)
}
fun isPro(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("pro_api", false)
}
fun setPro(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("pro_api", v).apply()
ctx.broadcastToModule {
putExtra("type", "restart")
putExtra("__cmd", "change_port")
}
pushUpdate(ctx)
}
fun getToken(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("token", null) ?: ""
}
fun setToken(ctx: Context, v: String?) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("token", v).apply()
pushUpdate(ctx)
}
fun isWs(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("ws", false)
}
fun setWs(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("ws", v).apply()
pushUpdate(ctx)
}
fun isWsClient(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("ws_client", false)
}
fun setWsClient(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("ws_client", v).apply()
pushUpdate(ctx)
}
fun isTablet(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("tablet", false)
}
fun setTablet(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("tablet", v).apply()
pushUpdate(ctx)
}
fun isUseCQCode(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("use_cqcode", false)
}
fun setUseCQCode(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("use_cqcode", v).apply()
pushUpdate(ctx)
}
fun isWebhook(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("webhook", false)
}
fun setWebhook(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("webhook", v).apply()
pushUpdate(ctx)
}
fun getWsAddr(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("ws_addr", "")!!
}
fun setWsAddr(ctx: Context, v: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("ws_addr", v).apply()
pushUpdate(ctx)
}
fun getUploadResourceGroup(ctx: Context): String {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getString("up_res_group", "100000000")!!
}
fun setUploadResourceGroup(ctx: Context, v: String) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putString("up_res_group", v).apply()
pushUpdate(ctx)
}
fun getHttpPort(ctx: Context): Int {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getInt("port", 5700)
}
fun setHttpPort(ctx: Context, v: Int) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putInt("port", v).apply()
ctx.broadcastToModule {
putExtra("type", "port")
putExtra("port", v)
putExtra("__cmd", "change_port")
}
pushUpdate(ctx)
}
fun getWsPort(ctx: Context): Int {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getInt("ws_port", 5800)
}
fun setWsPort(ctx: Context, v: Int) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putInt("ws_port", v).apply()
ctx.broadcastToModule {
putExtra("type", "ws_port")
putExtra("port", v)
putExtra("__cmd", "change_port")
}
pushUpdate(ctx)
}
fun is2B(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("2B", false)
}
fun set2B(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("2B", v).apply()
}
fun setAutoClean(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("auto_clear", v).apply()
}
fun isAutoClean(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("auto_clear", false)
}
fun isDebug(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("debug", false)
}
fun setDebug(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("debug", v).apply()
}
fun isAntiTrace(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("anti_qq_trace", true)
}
fun isForbidUselessProcess(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("forbid_useless_process", false)
}
fun setForbidUselessProcess(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("forbid_useless_process", v).apply()
}
fun setAntiTrace(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("anti_qq_trace", v).apply()
}
fun isInjectPacket(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("inject_packet", false)
}
fun setInjectPacket(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("inject_packet", v).apply()
}
fun enableAutoStart(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("enable_auto_start", false)
}
fun disableAutoSyncSetting(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("disable_auto_sync_setting", false)
}
fun enableAliveReply(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("alive_reply", false)
}
fun allowShell(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("shell", false)
}
fun setAutoStart(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("enable_auto_start", v).apply()
}
fun setDisableAutoSyncSetting(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("disable_auto_sync_setting", v).apply()
}
fun setAliveReply(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("alive_reply", v).apply()
}
fun setShellStatus(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("shell", v).apply()
}
fun enableSelfMsg(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("enable_self_msg", false)
}
fun enableOldBDH(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("enable_old_bdh", false)
}
fun setEnableOldBDH(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("enable_old_bdh", v).apply()
}
fun enableSyncMsgAsSentMsg(ctx: Context): Boolean {
val preferences = ctx.getSharedPreferences("config", 0)
return preferences.getBoolean("enable_sync_msg_as_sent_msg", false)
}
fun setEnableSelfMsg(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("enable_self_msg", v).apply()
}
fun setEnableSyncMsgAsSentMsg(ctx: Context, v: Boolean) {
val preferences = ctx.getSharedPreferences("config", 0)
preferences.edit().putBoolean("enable_sync_msg_as_sent_msg", v).apply()
}
fun getConfigMap(ctx: Context): Map<String, Any?> {
val preferences = ctx.getSharedPreferences("config", 0)
return mapOf(
"tablet" to preferences.getBoolean("tablet", false),
"port" to preferences.getInt("port", 5700),
"ws" to preferences.getBoolean("ws", false),
"ws_port" to preferences.getInt("ws_port", 5800),
"ssl_port" to preferences.getInt("ssl_port", 5701),
"http" to preferences.getBoolean("webhook", false),
"http_addr" to preferences.getString("http_addr", ""),
"ws_client" to preferences.getBoolean("ws_client", false),
"use_cqcode" to preferences.getBoolean("use_cqcode", false),
"ws_addr" to preferences.getString("ws_addr", ""),
"ssl_alias" to preferences.getString("ssl_alias", ""),
"pro_api" to preferences.getBoolean("pro_api", false),
"token" to preferences.getString("token", null),
"ssl_pwd" to preferences.getString("ssl_pwd", ""),
"inject_packet" to preferences.getBoolean("inject_packet", false),
"debug" to preferences.getBoolean("debug", false),
"anti_qq_trace" to preferences.getBoolean("anti_qq_trace", true),
"ssl_private_pwd" to preferences.getString("ssl_private_pwd", ""),
"key_store" to preferences.getString("key_store", ""),
"enable_self_msg" to preferences.getBoolean("enable_self_msg", false),
"echo_number" to preferences.getBoolean("echo_number", false),
"shell" to preferences.getBoolean("shell", false),
"alive_reply" to preferences.getBoolean("alive_reply", false),
"enable_sync_msg_as_sent_msg" to preferences.getBoolean("enable_sync_msg_as_sent_msg", false),
"disable_auto_sync_setting" to preferences.getBoolean("disable_auto_sync_setting", false),
"forbid_useless_process" to preferences.getBoolean("forbid_useless_process", false),
"enable_old_bdh" to preferences.getBoolean("enable_old_bdh", false),
"up_res_group" to preferences.getString("up_res_group", ""),
)
}
fun pushUpdate(ctx: Context) {
ctx.broadcastToModule {
getConfigMap(ctx).forEach { (key, value) ->
if (value == null) {
val v: String? = null
this.putExtra(key, v)
} else {
when (value) {
is Int -> this.putExtra(key, value)
is Long -> this.putExtra(key, value)
is Short -> this.putExtra(key, value)
is Byte -> this.putExtra(key, value)
is String -> this.putExtra(key, value)
is ByteArray -> this.putExtra(key, value)
is Boolean -> this.putExtra(key, value)
is Float -> this.putExtra(key, value)
is Double -> this.putExtra(key, value)
}
}
}
putExtra("__cmd", "push_config")
}
}
}

View File

@ -5,6 +5,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@ -24,7 +25,6 @@ import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -45,12 +45,10 @@ import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import coil.size.Size
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import moe.fuqiuluo.shamrock.R
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.app.Level
import moe.fuqiuluo.shamrock.app.config.ShamrockConfig
import moe.fuqiuluo.shamrock.config.*
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
import moe.fuqiuluo.shamrock.ui.theme.GlobalColor
import moe.fuqiuluo.shamrock.ui.theme.LocalString
import moe.fuqiuluo.shamrock.ui.theme.ThemeColor
@ -74,6 +72,110 @@ fun DashboardFragment(
InformationCard(ctx)
APIInfoCard(ctx)
FunctionCard(scope, ctx, LocalString.functionSetting)
SSLCard(ctx)
}
}
@Composable
private fun SSLCard(ctx: Context) {
ActionBox(
modifier = Modifier.padding(top = 12.dp),
painter = painterResource(id = R.drawable.baseline_security_24),
title = LocalString.sslSetting
) {
Column {
Divider(
modifier = Modifier,
color = GlobalColor.Divider,
thickness = 0.2.dp
)
val sslPort = remember { mutableStateOf(ShamrockConfig.getSSLPort(ctx).toString()) }
TextItem(
title = "SSL端口",
desc = "端口范围在0~65565并确保可用。",
text = sslPort,
hint = "请输入端口号",
error = "端口范围应在0~65565",
checker = {
it.isNotBlank() && kotlin.runCatching { it.toInt() in 0..65565 }.getOrDefault(false)
},
confirm = {
val newPort = sslPort.value.toInt()
ShamrockConfig.setSSLPort(ctx, newPort)
AppRuntime.log("设置SSL(HTTP)端口为$newPort,立即生效尝试中。")
}
)
val keyStore = remember { mutableStateOf(ShamrockConfig.getSSLKeyPath(ctx)) }
TextItem(
title = "SSL证书",
desc = "BKS签名的证书。",
text = keyStore,
hint = "输入证书路径",
error = "证书路径不合法或不存在",
checker = {
it.isNotBlank()
},
confirm = {
val new = keyStore.value
ShamrockConfig.setSSLKeyPath(ctx, new)
AppRuntime.log("设置SSL证书为[$new]。")
}
)
val alias = remember { mutableStateOf(ShamrockConfig.getSSLAlias(ctx)) }
TextItem(
title = "SSL别名",
desc = "BKS签名的别名确保大小写区分正确。",
text = alias,
hint = "输入签名别名",
error = "别名不合法",
checker = {
it.isNotBlank()
},
confirm = {
val new = alias.value
ShamrockConfig.setSSLAlias(ctx, new)
AppRuntime.log("设置SSL别名为[$new]。")
}
)
val sslPwd = remember { mutableStateOf(ShamrockConfig.getSSLPwd(ctx)) }
TextItem(
title = "SSL密码",
desc = "BKS签名的密码。",
text = sslPwd,
hint = "输入签名密码",
error = "密码不合法",
checker = {
it.isNotBlank()
},
confirm = {
val new = sslPwd.value
ShamrockConfig.setSSLPwd(ctx, new)
AppRuntime.log("设置SSL密码为[$new]。")
}
)
val sslPrivatePwd = remember { mutableStateOf(ShamrockConfig.getSSLPrivatePwd(ctx)) }
TextItem(
title = "SSL Private密码",
desc = "BKS签名的Private密码。",
text = sslPrivatePwd,
hint = "输入Private密码",
error = "密码不合法",
checker = {
it.isNotBlank()
},
confirm = {
val new = sslPrivatePwd.value
ShamrockConfig.setSSLPrivatePwd(ctx, new)
AppRuntime.log("设置SSL Private密码为[$new]。")
}
)
}
}
}
@ -93,35 +195,93 @@ private fun APIInfoCard(
thickness = 0.2.dp
)
val rpcPort = remember { mutableStateOf(ShamrockConfig[ctx, RPCPort].toString()) }
val wsPort = remember { mutableStateOf(ShamrockConfig.getWsPort(ctx).toString()) }
val port = remember { mutableStateOf(ShamrockConfig.getHttpPort(ctx).toString()) }
TextItem(
title = "RPC服务端口",
title = "主动HTTP端口",
desc = "端口范围在0~65565并确保可用。",
text = rpcPort,
text = port,
hint = "请输入端口号",
error = "端口范围应在0~65565",
checker = {
it.isNotBlank() && kotlin.runCatching { it.toInt() in 0..65565 }
.getOrDefault(false) && rpcPort.value != it
it.isNotBlank() && kotlin.runCatching { it.toInt() in 0..65565 }.getOrDefault(false) && wsPort.value != it
},
confirm = {
val newPort = rpcPort.value.toInt()
ShamrockConfig[ctx, RPCPort] = newPort
val newPort = port.value.toInt()
ShamrockConfig.setHttpPort(ctx, newPort)
AppRuntime.log("设置主动HTTP监听端口为$newPort,立即生效尝试中。")
}
)
val rpcAddress = remember { mutableStateOf(ShamrockConfig[ctx, RPCAddress]) }
TextItem(
title = "回调RPC地址",
desc = "例如kritor.support:8081",
text = rpcAddress,
title = "主动WebSocket端口",
desc = "端口范围在0~65565并确保可用。",
text = wsPort,
hint = "请输入端口号",
error = "端口范围应在0~65565",
checker = {
it.isNotBlank() && kotlin.runCatching { it.toInt() in 0..65565 }.getOrDefault(false) && it != port.value
},
confirm = {
val newPort = wsPort.value.toInt()
ShamrockConfig.setWsPort(ctx, newPort)
AppRuntime.log("设置主动WebSocket监听端口为$newPort")
}
)
val webHookAddress = remember { mutableStateOf(ShamrockConfig.getHttpAddr(ctx)) }
TextItem(
title = "回调HTTP地址",
desc = "例如http://shamrock.moe:80。",
text = webHookAddress,
hint = "请输入回调地址",
error = "输入的地址不合法",
checker = {
it.isNotBlank()
},
confirm = {
if (it.startsWith("http://") || it.startsWith("https://")) {
ShamrockConfig.setHttpAddr(ctx, webHookAddress.value)
AppRuntime.log("设置回调HTTP地址为[${webHookAddress.value}]。")
} else {
Toast.makeText(ctx, "回调地址不合法", Toast.LENGTH_SHORT).show()
webHookAddress.value = ""
}
}
)
val wsAddress = remember { mutableStateOf(ShamrockConfig.getWsAddr(ctx)) }
TextItem(
title = "被动WebSocket地址",
desc = "例如ws://shamrock.moe:81多个使用逗号分隔。",
text = wsAddress,
hint = "请输入被动地址",
error = "输入的地址不合法",
checker = {
true
},
confirm = {
if (it.startsWith("ws://") || it.startsWith("wss://") || it.isBlank()) {
ShamrockConfig.setWsAddr(ctx, wsAddress.value)
AppRuntime.log("设置被动WebSocket地址为[${wsAddress.value}]。")
} else {
Toast.makeText(ctx, "被动WebSocket地址不合法", Toast.LENGTH_SHORT).show()
wsAddress.value = ""
}
}
)
val authToken = remember { mutableStateOf(ShamrockConfig.getToken(ctx)) }
TextItem(
title = "鉴权Token",
desc = "用于鉴权的Token。",
text = authToken,
hint = "请填写鉴权token",
error = "输入的参数不合法",
checker = { true },
confirm = {
ShamrockConfig[ctx, RPCAddress] = rpcAddress.value
AppRuntime.log("设置回调RPC地址为[${rpcAddress.value}]。")
ShamrockConfig.setToken(ctx, authToken.value)
AppRuntime.log("设置鉴权Token为[${authToken.value}]。")
}
)
@ -154,32 +314,50 @@ private fun FunctionCard(
Function(
title = "强制平板模式",
desc = "强制QQ使用平板模式实现共存登录。",
isSwitch = ShamrockConfig[ctx, ForceTablet]
isSwitch = ShamrockConfig.isTablet(ctx)
) {
ShamrockConfig[ctx, ForceTablet] = it
ShamrockConfig.setTablet(ctx, it)
return@Function true
}
Function(
title = "主动RPC",
desc = "Kritor协议实现RPC",
isSwitch = ShamrockConfig[ctx, ActiveRPC]
title = "HTTP回调",
desc = "OneBot标准的HTTPAPI回调Shamrock作为Client。",
isSwitch = ShamrockConfig.isWebhook(ctx)
) {
ShamrockConfig[ctx, ActiveRPC] = it
ShamrockConfig.setWebhook(ctx, it)
return@Function true
}
Function(
title = "被动RPC",
desc = "Kritor协议实现RPC",
isSwitch = ShamrockConfig[ctx, PassiveRPC]
title = "消息格式为CQ码",
desc = "HTTPAPI回调的消息格式关闭则为消息段。",
isSwitch = ShamrockConfig.isUseCQCode(ctx)
) {
ShamrockConfig[ctx, PassiveRPC] = it
ShamrockConfig.setUseCQCode(ctx, it)
return@Function true
}
Function(
title = "主动WebSocket",
desc = "OneBot标准WebSocketShamrock作为Server。",
isSwitch = ShamrockConfig.isWs(ctx)
) {
ShamrockConfig.setWs(ctx, it)
return@Function true
}
Function(
title = "被动WebSocket",
desc = "OneBot标准WebSocketShamrock作为Client。",
isSwitch = ShamrockConfig.isWsClient(ctx)
) {
ShamrockConfig.setWsClient(ctx, it)
return@Function true
}
run {
val uploadResourceGroup = remember { mutableStateOf(ShamrockConfig[ctx, ResourceGroup]) }
val uploadResourceGroup = remember { mutableStateOf(ShamrockConfig.getUploadResourceGroup(ctx)) }
Column(
modifier = Modifier
.absolutePadding(left = 8.dp, right = 8.dp, top = 12.dp, bottom = 0.dp)
@ -202,11 +380,23 @@ private fun FunctionCard(
},
confirm = {
val groupId = uploadResourceGroup.value
ShamrockConfig[ctx, ResourceGroup] = groupId
ShamrockConfig.setUploadResourceGroup(ctx, groupId)
AppRuntime.log("设置接受资源群聊为[$groupId]。")
}
)
}
/*
Function(
title = "专业级接口",
desc = "如果你不知道你在做什么,请不要开启本功能。",
descColor = Color.Red,
isSwitch = ShamrockConfig.isPro(ctx)
) {
ShamrockConfig.setPro(ctx, it)
AppRuntime.log("专业级API = $it", Level.WARN)
return@Function true
}*/
}
}
}

View File

@ -23,14 +23,7 @@ import androidx.compose.ui.unit.sp
import moe.fuqiuluo.shamrock.R
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.app.Level
import moe.fuqiuluo.shamrock.app.config.ShamrockConfig
import moe.fuqiuluo.shamrock.config.AliveReply
import moe.fuqiuluo.shamrock.config.AntiJvmTrace
import moe.fuqiuluo.shamrock.config.B2Mode
import moe.fuqiuluo.shamrock.config.DebugMode
import moe.fuqiuluo.shamrock.config.EnableOldBDH
import moe.fuqiuluo.shamrock.config.EnableSelfMessage
import moe.fuqiuluo.shamrock.ui.service.handlers.InitHandler
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
import moe.fuqiuluo.shamrock.ui.theme.GlobalColor
import moe.fuqiuluo.shamrock.ui.theme.LocalString
import moe.fuqiuluo.shamrock.ui.tools.NoticeTextDialog
@ -75,9 +68,9 @@ fun LabFragment() {
title = LocalString.b2Mode,
desc = LocalString.b2ModeDesc,
descColor = it,
isSwitch = ShamrockConfig[ctx, B2Mode]
isSwitch = ShamrockConfig.is2B(ctx)
) {
ShamrockConfig[ctx, B2Mode] = it
ShamrockConfig.set2B(ctx, it)
scope.toast(ctx, LocalString.restartToast)
return@Function true
}
@ -86,10 +79,10 @@ fun LabFragment() {
title = LocalString.showDebugLog,
desc = LocalString.showDebugLogDesc,
descColor = it,
isSwitch = ShamrockConfig[ctx, DebugMode]
isSwitch = ShamrockConfig.isDebug(ctx)
) {
ShamrockConfig[ctx, DebugMode] = it
InitHandler.update(ctx)
ShamrockConfig.setDebug(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
}
@ -107,13 +100,54 @@ fun LabFragment() {
thickness = 0.2.dp
)
Function(
title = "禁止无用进程",
desc = "禁止QQ生成无用进程浪费内存可能造成部分功能闪退。",
descColor = color,
isSwitch = ShamrockConfig.isForbidUselessProcess(ctx)
) {
ShamrockConfig.setForbidUselessProcess(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
Function(
title = "自回复测试",
desc = "发送[ping],机器人发送一个具有调试信息的返回。",
descColor = color,
isSwitch = ShamrockConfig[ctx, AliveReply]
isSwitch = ShamrockConfig.enableAliveReply(ctx)
) {
ShamrockConfig[ctx, AliveReply] = it
ShamrockConfig.setAliveReply(ctx, it)
return@Function true
}
Function(
title = "开启Shell接口",
desc = "可能导致设备被入侵,请勿随意开启。",
descColor = color,
isSwitch = ShamrockConfig.allowShell(ctx)
) {
ShamrockConfig.setShellStatus(ctx, it)
return@Function true
}
Function(
title = "自动唤醒QQ",
desc = "QQ进程死亡时重新打开QQ进程前提本进程存活。",
descColor = color,
isSwitch = ShamrockConfig.enableAutoStart(ctx)
) {
ShamrockConfig.setAutoStart(ctx, it)
return@Function true
}
Function(
title = "禁止Shamrock同步设置",
desc = "禁止Shamrock同步设置防止恢复手动修改后的配置文件。",
descColor = color,
isSwitch = ShamrockConfig.disableAutoSyncSetting(ctx)
) {
ShamrockConfig.setDisableAutoSyncSetting(ctx, it)
return@Function true
}
@ -160,14 +194,25 @@ fun LabFragment() {
thickness = 0.2.dp
)
Function(
title = LocalString.injectPacket,
desc = LocalString.injectPacketDesc,
descColor = color,
isSwitch = ShamrockConfig.isInjectPacket(ctx)
) {
ShamrockConfig.setInjectPacket(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
Function(
title = LocalString.antiTrace,
desc = LocalString.antiTraceDesc,
descColor = color,
isSwitch = ShamrockConfig[ctx, AntiJvmTrace]
isSwitch = ShamrockConfig.isAntiTrace(ctx)
) {
ShamrockConfig[ctx, AntiJvmTrace] = it
scope.toast(ctx, LocalString.restartToast)
ShamrockConfig.setAntiTrace(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
@ -232,10 +277,21 @@ fun LabFragment() {
title = "自发消息推送",
desc = "推送Bot发送的消息未做特殊处理请勿打开。",
descColor = it,
isSwitch = ShamrockConfig[ctx, EnableSelfMessage]
isSwitch = ShamrockConfig.enableSelfMsg(ctx)
) {
ShamrockConfig[ctx, EnableSelfMessage] = it
InitHandler.update(ctx)
ShamrockConfig.setEnableSelfMsg(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
Function(
title = "同步消息推送类型异换",
desc = "推送来自同号异设备消息,将同步消息作为自发消息推送。",
descColor = it,
isSwitch = ShamrockConfig.enableSyncMsgAsSentMsg(ctx)
) {
ShamrockConfig.setEnableSyncMsgAsSentMsg(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
@ -243,10 +299,10 @@ fun LabFragment() {
title = "启用旧版资源上传系统",
desc = "如果NT内核无法上传资源请打开本开关。",
descColor = it,
isSwitch = ShamrockConfig[ctx, EnableOldBDH]
isSwitch = ShamrockConfig.enableOldBDH(ctx)
) {
ShamrockConfig[ctx, EnableOldBDH] = it
InitHandler.update(ctx)
ShamrockConfig.setEnableOldBDH(ctx, it)
ShamrockConfig.pushUpdate(ctx)
return@Function true
}
}

View File

@ -0,0 +1,108 @@
@file:OptIn(DelicateCoroutinesApi::class)
package moe.fuqiuluo.shamrock.ui.service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat.startActivity
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.url
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import moe.fuqiuluo.shamrock.remote.structures.CommonResult
import moe.fuqiuluo.shamrock.remote.structures.CurrentAccount
import moe.fuqiuluo.shamrock.remote.structures.Status
import moe.fuqiuluo.shamrock.tools.GlobalClient
import moe.fuqiuluo.shamrock.ui.app.AppRuntime.AccountInfo
import moe.fuqiuluo.shamrock.ui.app.AppRuntime.log
import moe.fuqiuluo.shamrock.ui.app.AppRuntime.state
import moe.fuqiuluo.shamrock.ui.app.Level
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
import moe.fuqiuluo.shamrock.ui.service.internal.broadcastToModule
import java.net.ConnectException
import java.util.Timer
import kotlin.concurrent.timer
object DashboardInitializer {
private var servicePort: Int = 0
private lateinit var heartbeatTimer: Timer
operator fun invoke(context: Context, port: Int) {
servicePort = port
initHeartbeat(true, context)
}
private fun initHeartbeat(reload: Boolean, context: Context) {
if (::heartbeatTimer.isInitialized && !reload) {
return
}
if (::heartbeatTimer.isInitialized) {
heartbeatTimer.cancel()
}
heartbeatTimer = timer("heartbeat", false, 0, 1000L * 30) {
checkService(context)
}
}
private fun checkService(context: Context) {
GlobalScope.launch {
try {
GlobalClient.get {
url("http://127.0.0.1:$servicePort/get_account_info")
val token = ShamrockConfig.getToken(context)
if (token.isNotBlank()) {
//header("Authorization", "Bearer $token")
parameter("access_token", token)
}
}.let {
if (it.status == HttpStatusCode.OK) {
val result: CommonResult<CurrentAccount> = Json.decodeFromString(it.bodyAsText())
state.isFined.value = result.retcode == 0
if (result.retcode == Status.InternalHandlerError.code) {
log("账号未登录。", Level.WARN)
} else if (result.retcode != 0) {
log("尝试从接口获取账号信息失败,未知错误。", Level.ERROR)
} else {
AccountInfo.let { account ->
account.uin.value = result.data.uin.toString()
account.nick.value = result.data.nick
}
}
} else {
state.isFined.value = false
log("尝试从接口获取账号信息失败,服务运行异常。", Level.ERROR)
}
}
} catch (e: ConnectException) {
state.isFined.value = false
context.broadcastToModule {
putExtra("__cmd", "checkAndStartService")
}
if (ShamrockConfig.enableAutoStart(context)) {
log("检测到Service死亡正在尝试重新启动")
GlobalScope.launch(Dispatchers.Main) {
val packageName = "com.tencent.mobileqq"
val className = "com.tencent.mobileqq.activity.SplashActivity"
val intent = Intent()
intent.component = ComponentName(packageName, className)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra("from", "shamrock")
startActivity(context, intent, Bundle.EMPTY)
}
}
} catch (e: Throwable) {
state.isFined.value = false
log(e.stackTraceToString(), Level.ERROR)
}
}
}
}

View File

@ -0,0 +1,15 @@
package moe.fuqiuluo.shamrock.ui.service.handlers
import android.content.ContentValues
import android.content.Context
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.service.DashboardInitializer
object FetchPortHandler: ModuleHandler() {
override val cmd: String = "success"
override fun onReceive(callbackId: Int, values: ContentValues, context: Context) {
AppRuntime.state.supportVoice.value = values.getAsBoolean("voice")
DashboardInitializer(context, values.getAsInteger("port"))
}
}

View File

@ -3,37 +3,13 @@ package moe.fuqiuluo.shamrock.ui.service.handlers
import android.content.ContentValues
import android.content.Context
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.app.config.ShamrockConfig
import moe.fuqiuluo.shamrock.config.*
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
internal object InitHandler: ModuleHandler() {
override val cmd: String = "init"
override fun onReceive(callbackId: Int, values: ContentValues, context: Context) {
update(context)
}
fun update(context: Context) {
AppRuntime.log("推送QQ进程初始化设置数据包成功...")
val maps = hashMapOf<String, Any?>()
ActiveRPC.update(context, maps)
AliveReply.update(context, maps)
AntiJvmTrace.update(context, maps)
DebugMode.update(context, maps)
EnableOldBDH.update(context, maps)
EnableSelfMessage.update(context, maps)
ForceTablet.update(context, maps)
PassiveRPC.update(context, maps)
ResourceGroup.update(context, maps)
RPCAddress.update(context, maps)
RPCPort.update(context, maps)
callback(context, 1, maps)
}
private inline fun <reified T> ConfigKey<T>.update(context: Context, map: HashMap<String, Any?>) {
map[name()] = ShamrockConfig[context, this]
callback(context, callbackId, ShamrockConfig.getConfigMap(context))
}
}

View File

@ -29,8 +29,8 @@ abstract class ModuleHandler {
}
}
}
putExtra("__cmd", cmd)
putExtra("__hash", callbackId)
putExtra("__cmd", cmd)
}
}
}

View File

@ -1,49 +0,0 @@
package moe.fuqiuluo.shamrock.ui.service.handlers
import android.content.ContentValues
import android.content.Context
import android.widget.Toast
import moe.fuqiuluo.shamrock.tools.GlobalUi
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import java.util.Timer
import kotlin.concurrent.timer
import kotlin.concurrent.timerTask
object SwitchStatus: ModuleHandler() {
override val cmd: String
get() = "switch_status"
private var lastActiveTime = 0L
private var timer: Timer? = null
override fun onReceive(callbackId: Int, values: ContentValues, context: Context) {
val voiceSwitch = values.getAsBoolean("voice")
val nickname = values.getAsString("nickname")
val account = values.getAsString("account")
if (lastActiveTime == 0L) GlobalUi.post {
Toast.makeText(context, "激活成功", Toast.LENGTH_SHORT).show()
}
AppRuntime.state.apply {
isFined.value = true
coreVersion.value = values.getAsString("core_version")
coreName.value = "LSPosed"
supportVoice.value = voiceSwitch
}
AppRuntime.AccountInfo.apply {
uin.value = account
nick.value = nickname
}
lastActiveTime = System.currentTimeMillis()
startTimer()
}
private fun startTimer() {
timer?.cancel()
timer = timer("SwitchStatus", true, 0, 5_000) {
if (lastActiveTime != 0L && System.currentTimeMillis() - lastActiveTime > 30 * 1000) {
AppRuntime.state.isFined.value = false
lastActiveTime = 0
}
}
}
}

View File

@ -5,9 +5,9 @@ import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import moe.fuqiuluo.shamrock.ui.service.ModuleTalker
import moe.fuqiuluo.shamrock.ui.service.handlers.*
import android.net.Uri
class MultifunctionalProvider: ContentProvider() {
override fun insert(uri: Uri, content: ContentValues?): Uri {
@ -28,8 +28,8 @@ class MultifunctionalProvider: ContentProvider() {
override fun onCreate(): Boolean {
ModuleTalker.register(InitHandler)
ModuleTalker.register(FetchPortHandler)
ModuleTalker.register(LogHandler)
ModuleTalker.register(SwitchStatus)
return true
}
@ -58,7 +58,7 @@ class MultifunctionalProvider: ContentProvider() {
inline fun Context.broadcastToModule(intentBuilder: Intent.() -> Unit) {
val intent = Intent()
intent.action = "moe.fuqiuluo.kritor.dynamic"
intent.action = "moe.fuqiuluo.onebot.dynamic"
intent.intentBuilder()
sendBroadcast(intent)
}

View File

@ -1,35 +1,23 @@
package moe.fuqiuluo.shamrock.ui.tools
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.Indication
import androidx.compose.foundation.background
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.selectable
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@ -43,10 +31,8 @@ import androidx.compose.ui.layout.LastBaseline
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
@ -149,18 +135,20 @@ private fun TabBaselineLayout(
text: @Composable (() -> Unit)?,
icon: @Composable (() -> Unit)?
) {
Layout({
if (text != null) {
Box(
Modifier
.layoutId("text")
.padding(horizontal = HorizontalTextPadding)
) { text() }
Layout(
{
if (text != null) {
Box(
Modifier
.layoutId("text")
.padding(horizontal = HorizontalTextPadding)
) { text() }
}
if (icon != null) {
Box(Modifier.layoutId("icon")) { icon() }
}
}
if (icon != null) {
Box(Modifier.layoutId("icon")) { icon() }
}
}) { measurables, constraints ->
) { measurables, constraints ->
val textPlaceable = text?.let {
measurables.first { it.layoutId == "text" }.measure(
// Measure with loose constraints for height as we don't want the text to take up more
@ -254,72 +242,27 @@ private fun Placeable.PlacementScope.placeTextAndIcon(
}
@Composable
fun ShamrockTab(
fun ShamrockTabV2(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
text: (@Composable (() -> Unit))? = null,
icon: (@Composable (() -> Unit))? = null,
selectedContentColor: Color = GlobalColor.TabSelected,
unselectedContentColor: Color = selectedContentColor,
indication: Indication? = rememberRipple(bounded = true, color = selectedContentColor),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
titleWithIcon: Pair<String, Int>,
visibleState: MutableTransitionState<Boolean>
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
var text: @Composable (() -> Unit)? = null
var icon: @Composable (() -> Unit)? = null
if (!selected) {
icon = {
Icon(
painter = painterResource(id = titleWithIcon.second),
contentDescription = titleWithIcon.first,
tint = Color.Unspecified,
modifier = Modifier
.height(24.dp)
.width(24.dp)
.padding(bottom = 5.dp)
.indication(
remember { MutableInteractionSource() },
rememberRipple(color = Color.Transparent)
)
)
}
} else {
text = {
val style = MaterialTheme.typography
.fromToken(PrimaryNavigationTabTokens.LabelTextFont)
.copy(textAlign = TextAlign.Center)
ProvideTextStyle(style) {
AnimatedVisibility(
visibleState = visibleState,
enter = remember {
scaleIn(animationSpec = TweenSpec(150, easing = FastOutLinearInEasing))
},
exit = remember {
scaleOut(animationSpec = TweenSpec(150, easing = FastOutSlowInEasing))
},
modifier = Modifier
) {
Text(
text = titleWithIcon.first,
color = GlobalColor.TabItem,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(bottom = 5.dp)
.indication(
remember { MutableInteractionSource() },
rememberRipple(color = Color.Transparent)
)
)
}
}
val styledText: @Composable (() -> Unit)? = text?.let {
@Composable {
val style =
MaterialTheme.typography.fromToken(PrimaryNavigationTabTokens.LabelTextFont)
.copy(textAlign = TextAlign.Center)
ProvideTextStyle(style, content = text)
}
}
ShamrockTab(
ShamrockTabV2(
selected,
onClick,
modifier,
@ -329,15 +272,12 @@ fun ShamrockTab(
interactionSource,
indication
) {
TabBaselineLayout(
icon = icon,
text = text
)
TabBaselineLayout(icon = icon, text = styledText)
}
}
@Composable
fun ShamrockTab(
fun ShamrockTabV2(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.2.0" apply false
id("com.android.application") version "8.2.1" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
id("com.android.library") version "8.2.0" apply false
id("com.android.library") version "8.2.1" apply false
}

View File

@ -1,5 +1,5 @@
plugins {
kotlin("jvm") version "1.9.22"
kotlin("jvm") version "1.9.21"
}
repositories {

View File

@ -7,6 +7,10 @@ val DEPENDENCY_ANDROIDX = arrayOf(
"androidx.activity:activity-compose:1.7.2",
)
const val DEPENDENCY_JSON5K = "io.github.xn32:json5k:0.3.0"
const val DEPENDENCY_PROTOBUF = "com.google.protobuf:protobuf-java:3.24.0"
const val DEPENDENCY_JAVA_WEBSOCKET = "org.java-websocket:Java-WebSocket:1.5.4"
fun room(name: String) = "androidx.room:room-$name:${Versions.roomVersion}"
fun kotlinx(name: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$name:$version"
@ -15,9 +19,8 @@ fun ktor(target: String, name: String): String {
return "io.ktor:ktor-$target-$name:${Versions.ktorVersion}"
}
fun grpc(name: String, version: String) = "io.grpc:grpc-$name:$version"
object Versions {
const val roomVersion = "2.5.0"
const val ktorVersion = "2.3.3"
}

42
kritor/.gitignore vendored
View File

@ -1,42 +0,0 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -1,76 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.google.protobuf") version "0.9.4"
}
android {
namespace = "moe.whitechi73.kritor"
compileSdk = 34
defaultConfig {
minSdk = 24
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
protobuf(files("kritor/protos"))
implementation("com.google.protobuf:protobuf-java:4.26.0")
implementation(kotlinx("coroutines-core", "1.8.0"))
implementation(grpc("stub", "1.62.2"))
implementation(grpc("protobuf", "1.62.2"))
implementation(grpc("kotlin-stub", "1.4.1"))
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:4.26.0"
}
plugins {
create("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.62.2"
}
create("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
create("grpc")
create("grpckt")
}
it.builtins {
create("java")
}
}
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}

Submodule kritor/kritor deleted from 27669a8f57

View File

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -15,7 +15,7 @@ dependencies {
implementation("com.google.devtools.ksp:symbol-processing-api:1.9.21-1.0.15")
implementation("com.squareup:kotlinpoet:1.14.2")
//implementation(DEPENDENCY_PROTOBUF)
implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")

View File

@ -1,83 +0,0 @@
@file:Suppress("UNCHECKED_CAST")
@file:OptIn(KspExperimental::class)
package moe.fuqiuluo.ksp.impl
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import kritor.service.Grpc
class GrpcProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
): SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(Grpc::class.qualifiedName!!)
val actions = (symbols as Sequence<KSFunctionDeclaration>).toList()
if (actions.isEmpty()) return emptyList()
// 怎么返回nullable的结果
val packageName = "kritor.handlers"
val funcBuilder = FunSpec.builder("handleGrpc")
.addModifiers(KModifier.SUSPEND)
.addParameter("cmd", String::class)
.addParameter("data", ByteArray::class)
.returns(ByteArray::class)
val fileSpec = FileSpec.scriptBuilder("AutoGrpcHandlers", packageName)
logger.warn("Found ${actions.size} grpc-actions")
//logger.error(resolver.getClassDeclarationByName("io.kritor.AuthReq").toString())
//logger.error(resolver.getJavaClassByName("io.kritor.AuthReq").toString())
//logger.error(resolver.getKotlinClassByName("io.kritor.AuthReq").toString())
actions.forEach { action ->
val methodName = action.qualifiedName?.asString()!!
val grpcMethod = action.getAnnotationsByType(Grpc::class).first()
val service = grpcMethod.serviceName
val funcName = grpcMethod.funcName
funcBuilder.addStatement("if (cmd == \"${service}.${funcName}\") {\t")
val reqType = action.parameters[0].type.toString()
val rspType = action.returnType.toString()
funcBuilder.addStatement("val resp: $rspType = $methodName($reqType.parseFrom(data))")
funcBuilder.addStatement("return resp.toByteArray()")
funcBuilder.addStatement("}")
}
funcBuilder.addStatement("return EMPTY_BYTE_ARRAY")
fileSpec
.addStatement("import io.kritor.authentication.*")
.addStatement("import io.kritor.core.*")
.addStatement("import io.kritor.customization.*")
.addStatement("import io.kritor.developer.*")
.addStatement("import io.kritor.file.*")
.addStatement("import io.kritor.friend.*")
.addStatement("import io.kritor.group.*")
.addStatement("import io.kritor.guild.*")
.addStatement("import io.kritor.message.*")
.addStatement("import io.kritor.web.*")
.addFunction(funcBuilder.build())
.addImport("moe.fuqiuluo.symbols", "EMPTY_BYTE_ARRAY")
runCatching {
codeGenerator.createNewFile(
dependencies = Dependencies(aggregating = false),
packageName = packageName,
fileName = fileSpec.name
).use { outputStream ->
outputStream.writer().use {
fileSpec.build().writeTo(it)
}
}
}
return emptyList()
}
}

View File

@ -0,0 +1,85 @@
@file:OptIn(KspExperimental::class)
@file:Suppress("LocalVariableName", "UNCHECKED_CAST")
package moe.fuqiuluo.ksp.impl
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.getKotlinClassByName
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import moe.fuqiuluo.symbols.OneBotHandler
class OneBotHandlerProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
): SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val ActionManagerNode = resolver.getClassDeclarationByName("moe.fuqiuluo.shamrock.remote.action.ActionManager")
?: resolver.getKotlinClassByName("moe.fuqiuluo.shamrock.remote.action.ActionManager")
?: resolver.getClassDeclarationByName("ActionManager")
val symbols = resolver.getSymbolsWithAnnotation(OneBotHandler::class.qualifiedName!!)
val unableToProcess = symbols.filterNot { it.validate() }
if (ActionManagerNode != null) {
val oneBotHandlers = (symbols.filter {
it is KSClassDeclaration && it.validate() && it.classKind == ClassKind.OBJECT
} as Sequence<KSClassDeclaration>).toList()
if (oneBotHandlers.isNotEmpty()) {
ActionManagerNode.accept(ActionManagerVisitor(oneBotHandlers), Unit)
}
}
return unableToProcess.toList()
}
inner class ActionManagerVisitor(
private val actionHandlers: List<KSClassDeclaration>
): KSVisitorVoid() {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
val packageName = classDeclaration.packageName.asString()
// generate kotlin `init { }`
val fileSpec = FileSpec.builder(packageName, classDeclaration.qualifiedName?.asString() ?: run {
throw IllegalStateException("ActionManagerVisitor: classDeclaration.qualifiedName is null")
}).addFunction(FunSpec.builder("initManager").apply {
actionHandlers.forEach { handler ->
// fetch the params of the annotation
val annotation = handler.getAnnotationsByType(OneBotHandler::class).first()
val actionName = annotation.actionName
val alias = annotation.alias
alias.forEach { name ->
addStatement("actionMap[\"$name\"] = ${handler.simpleName.asString()}")
}
addStatement("actionMap[\"$actionName\"] = ${handler.simpleName.asString()}")
}
}.build()).apply {
addImport("moe.fuqiuluo.shamrock.remote.action.ActionManager", "actionMap")
actionHandlers.forEach {
addImport(it.packageName.asString(), it.simpleName.asString())
}
}.build()
codeGenerator.createNewFile(
dependencies = Dependencies(aggregating = false),
packageName = packageName,
fileName = "Auto" + classDeclaration.simpleName.asString()
).use { outputStream ->
outputStream.writer().use {
fileSpec.writeTo(it)
}
}
}
}
}

View File

@ -32,7 +32,7 @@ class ProtobufProcessor(
}.toList()
if (actions.isNotEmpty()) {
actions.forEachIndexed { _, clz ->
actions.forEachIndexed { index, clz ->
if (clz.isInternal()) return@forEachIndexed
if (clz.isPrivate()) return@forEachIndexed

View File

@ -1,5 +1,5 @@
@file:Suppress("UNCHECKED_CAST", "LocalVariableName", "PrivatePropertyName")
@file:OptIn(KspExperimental::class, KspExperimental::class)
@file:OptIn(KspExperimental::class)
package moe.fuqiuluo.ksp.impl
@ -27,14 +27,10 @@ class XposedHookProcessor(
private val logger: KSPLogger
): SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(
annotationName = XposedHook::class.qualifiedName!!,
inDepth = true
)
logger.warn("Found ${symbols.count()} classes annotated with XposedHook")
val symbols = resolver.getSymbolsWithAnnotation(XposedHook::class.qualifiedName!!)
val unableToProcess = symbols.filterNot { it.validate() }
val actions = (symbols.filter {
it is KSClassDeclaration && it.classKind == ClassKind.CLASS
it is KSClassDeclaration && it.validate() && it.classKind == ClassKind.CLASS
} as Sequence<KSClassDeclaration>).toList()
if (actions.isNotEmpty()) {
@ -50,7 +46,7 @@ class XposedHookProcessor(
}
val context = ClassName("android.content", "Context")
val packageName = "moe.fuqiuluo.shamrock.xposed.actions"
val packageName = "moe.fuqiuluo.shamrock.xposed.hooks"
val fileSpec = FileSpec.builder(packageName, "AutoActionLoader").addFunction(FunSpec.builder("runFirstActions")
.addParameter("ctx", context)
.apply {
@ -100,6 +96,16 @@ class XposedHookProcessor(
}
}
}
return unableToProcess.toList()
}
inner class ActionLoaderVisitor(
private val firstActions: List<KSClassDeclaration>,
private val serviceActions: List<KSClassDeclaration>,
): KSVisitorVoid() {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
}
}
}

View File

@ -4,12 +4,12 @@ import com.google.auto.service.AutoService
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import moe.fuqiuluo.ksp.impl.GrpcProcessor
import moe.fuqiuluo.ksp.impl.OneBotHandlerProcessor
@AutoService(SymbolProcessorProvider::class)
class GrpcProvider: SymbolProcessorProvider {
class OneBotHandlerProcessorProvider: SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return GrpcProcessor(
return OneBotHandlerProcessor(
environment.codeGenerator,
environment.logger
)

View File

@ -37,6 +37,7 @@ android {
}
dependencies {
implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
implementation(kotlinx("serialization-json", "1.6.2"))
@ -46,5 +47,5 @@ dependencies {
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

View File

@ -13,7 +13,7 @@ data class ButtonExtra(
@Serializable
data class Object1(
@ProtoNumber(1) val rows: List<Row>? = null,
@ProtoNumber(2) val appid: ULong? = null,
@ProtoNumber(2) val appid: Int? = null,
)
@Serializable

View File

@ -94,7 +94,8 @@ data class DeleteReq(
@Serializable
data class DownloadRkeyReq(
@ProtoNumber(1) val types: List<Int>
@ProtoNumber(1) val types: List<Int>,
@ProtoNumber(2) val downloadType: Int
)
@Serializable

View File

@ -1,7 +1,7 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.oidb.cmd0x11c5
import moe.fuqiuluo.symbols.EMPTY_BYTE_ARRAY
import com.google.protobuf.Internal.EMPTY_BYTE_ARRAY
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@ -9,7 +9,7 @@ import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class NtV2RichMediaRsp(
@ProtoNumber(1) val head: RspHead?,
@ProtoNumber(1) val head: RspHead,
@ProtoNumber(2) val upload: UploadRsp?,
@ProtoNumber(3) val download: DownloadRsp?,
@ProtoNumber(4) val downloadRkeyRsp: DownloadRkeyRsp?,
@ -52,11 +52,11 @@ data class DownloadRkeyRsp(
@Serializable
data class RKeyInfo(
@ProtoNumber(1) val rkey: String?,
@ProtoNumber(1) val rkey: String,
@ProtoNumber(2) val rkeyTtlSec: ULong?,
@ProtoNumber(3) val storeId: UInt = 0u,
@ProtoNumber(4) val rkeyCreateTime: UInt?,
@ProtoNumber(4) val type: UInt?,
@ProtoNumber(5) val type: UInt,
)
@Serializable

View File

@ -1,6 +1,6 @@
package protobuf.oidb.cmd0x388
import moe.fuqiuluo.symbols.EMPTY_BYTE_ARRAY
import com.google.protobuf.Internal.EMPTY_BYTE_ARRAY
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf

View File

@ -2,7 +2,7 @@
package protobuf.oidb.cmd0x388
import moe.fuqiuluo.symbols.EMPTY_BYTE_ARRAY
import com.google.protobuf.Internal.EMPTY_BYTE_ARRAY
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber

View File

@ -6,13 +6,9 @@ import com.tencent.mobileqq.app.BusinessObserver;
import com.tencent.mobileqq.app.MessageHandler;
import com.tencent.qphone.base.remote.ToServiceMsg;
import java.util.concurrent.ConcurrentHashMap;
import mqq.app.AppRuntime;
public abstract class AppInterface extends AppRuntime {
private final ConcurrentHashMap<String, BusinessHandler> allHandler = new ConcurrentHashMap<>();
public String getCurrentNickname() {
return "";
}

View File

@ -13,10 +13,6 @@ public abstract class BaseBusinessHandler extends OidbWrapper {
return null;
}
public void addBusinessObserver(ToServiceMsg toServiceMsg, BusinessObserver businessObserver, boolean z) {
}
public final <T> T decodePacket(byte[] data, String name, T obj) {
UniPacket uniPacket = new UniPacket(true);
try {
@ -28,10 +24,6 @@ public abstract class BaseBusinessHandler extends OidbWrapper {
}
}
public boolean msgCmdFilter(String str) {
return false;
}
protected abstract Set<String> getCommandList();
protected abstract Set<String> getPushCommandList();

View File

@ -8,8 +8,6 @@ public abstract class BusinessHandler extends BaseBusinessHandler {
public BusinessHandler(AppInterface appInterface) {
}
protected abstract Class<? extends BusinessObserver> observerClass();
@Override
public Set<String> getCommandList() {
return null;

View File

@ -146,6 +146,7 @@ public class TroopInfo {
public byte[] troopInfoExtByte;
public String troopLevelMap;
public String troopRemark;
@Deprecated
public String troopcode;
public short troopface;
public String troopmemo;
@ -154,6 +155,7 @@ public class TroopInfo {
public int trooptype;
public String troopuin;
public long udwCmdUinRingtoneID;
@Deprecated
public String uin;
public int wClickBAFTipCount;
public int wInsertBAFTipCount;

View File

@ -1,63 +1,92 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.Nullable;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
public class TroopMemberInfo {
public long active_point;
public static final long VALUE_DISTANCE_TO_SELF_UNKOWN = -100;
protected static final int VALUE_INVALID = -100;
public static final long VALUE_MEMBER_CLOSE_SHARE_LBS = -1001;
public int addState;
public byte age;
public String alias;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String autoremark;
public long cmduinFlagEx3Grocery;
public long credit_level;
public long datetime;
public String displayedNamePinyinFirst;
public short faceid;
@Nullable
public TroopMemberInfoExt extInfo;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String friendnick;
public long gagTimeStamp;
public String honorList;
public boolean isTroopFollowed;
public long join_time;
public long last_active_time;
public int level;
public int mBigClubVipType;
public byte mHonorRichFlag;
public boolean mIsShielded;
public int mVipType;
public String memberUid;
public String memberuin;
public int newRealLevel;
public TroopMemberNickInfo nickInfo;
public int realLevel;
public MemberRole role;
@Nullable
public TroopMemberSpecialTitleInfo specialTitleInfo;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String troopColorNick;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public int troopColorNickId;
@Deprecated(since = "推荐使用TroopMemberNickInfo")
public String troopnick;
public String troopuin;
public int globalTroopLevel = VALUE_INVALID;
public int flagEx = 0;
// QQ OLD API DATA
public long active_point;
public byte age;
public String alias;
public long cmduinFlagEx3Grocery;
public long datetime;
public short faceid;
public String hwCourse;
public int hwIdentity;
public String hwName;
public int isShowQZone;
public boolean isTroopFollowed;
public long join_time;
public long lastMsgUpdateHonorRichTime;
public long last_active_time;
public int level;
public int mBigClubTemplateId;
public int mBigClubVipLevel;
public int mBigClubVipType;
public int mGlamourLevel;
public byte mHonorRichFlag;
public int mIsHideBigClub;
public boolean mIsShielded;
public String mUniqueTitle;
public int mVipLevel;
public int mVipTemplateId;
public int mVipType;
public String memberuin;
public int newRealLevel;
public String pyAll_autoremark;
public String pyAll_friendnick;
public String pyAll_troopnick;
public String pyFirst_autoremark;
public String pyFirst_friendnick;
public String pyFirst_troopnick;
public int realLevel;
public String recommendRemark;
public byte sex;
public byte status;
public int tribeLevel;
public int tribePoint;
public String troopColorNick;
public int troopColorNickId;
public String troopnick;
public String troopremark;
public String troopuin;
public int qqVipInfo = 0;
public int superQqInfo = 0;
public int superVipInfo = 0;
public int hotChatGlamourLevel = -100;
public int globalTroopLevel = -100;
public int distance = 0;
public long msgseq = -100;
public double distanceToSelf = -100.0d;

View File

@ -0,0 +1,48 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.Nullable;
import kotlin.jvm.JvmField;
public class TroopMemberInfoExt {
@JvmField
public int commonFrdCnt;
@JvmField
public long flagEx3;
@JvmField
public int hwIdentity;
@JvmField
public long lastMsgUpdateHonorRichTime;
@JvmField
@Nullable
public String memberUin;
@JvmField
@Nullable
public byte[] nickIconRepeatMsgBuffer;
@JvmField
@Nullable
public String recommendRemark;
@JvmField
@Nullable
public String showNameForPinyin;
@JvmField
@Nullable
public String showNamePinyinAll;
@JvmField
@Nullable
public String showNamePinyinFirst;
@JvmField
@Nullable
public String troopUin;
}

View File

@ -0,0 +1,54 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.NotNull;
public class TroopMemberNickInfo {
@NotNull
public final String getAutoRemark() {
return null;
}
@NotNull
public final String getColorNick() {
return null;
}
public final int getColorNickId() {
return 0;
}
@NotNull
public final String getFriendNick() {
return null;
}
@NotNull
public final String getHBShowName() {
return null;
}
@NotNull
public final String getRemarkFromFriend() {
return null;
}
@NotNull
public final String getRemarkFromFriendV2() {
return null;
}
@NotNull
public final String getShowName() {
return null;
}
@NotNull
public final String getTroopNick() {
return null;
}
@NotNull
public final String getTroopUin() {
return null;
}
}

View File

@ -0,0 +1,29 @@
package com.tencent.mobileqq.data.troop;
import org.jetbrains.annotations.NotNull;
public class TroopMemberSpecialTitleInfo {
public final int getExpireTimeSec() {
return 0;
}
@NotNull
public final String getFriendNick() {
return null;
}
@NotNull
public final String getSpecialTitle() {
return null;
}
@NotNull
public final String getTroopUin() {
return null;
}
@NotNull
public final String getUin() {
return null;
}
}

View File

@ -0,0 +1,15 @@
package com.tencent.mobileqq.l0.b;
public class a {
public void b () {
}
public void c (c cVar) {
}
public void onUpdate(int progress, boolean z, Object obj) {
}
}

View File

@ -0,0 +1,23 @@
package com.tencent.mobileqq.l0.b;
import android.graphics.Point;
import java.util.ArrayList;
public class b {
// text
public String a;
// confidence
public int b;
// coordinates
public ArrayList<Point> c;
/* renamed from: d */
public int d;
/* renamed from: e */
public boolean e;
}

View File

@ -0,0 +1,39 @@
package com.tencent.mobileqq.l0.b;
import java.util.ArrayList;
import java.util.HashMap;
public class c {
// image
public String a;
// width
public int b;
// height
public int c;
// lang
public String d;
// url
public String e;
// results
public ArrayList<b> f;
public ArrayList<String> g;
public HashMap<String, String> h;
public int i;
public int j;
public int k;
public String l;
public int m;
}

View File

@ -1,7 +1,7 @@
package com.tencent.mobileqq.msf.core;
public class MsfCore {
public static synchronized int getNextSeq() {
public synchronized int getNextSeq() {
return 0;
}
}

View File

@ -0,0 +1,22 @@
package com.tencent.mobileqq.msf.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import com.tencent.mobileqq.msf.core.MsfCore;
public class MsfService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static MsfCore getCore() {
return null;
}
}

View File

@ -0,0 +1,38 @@
package com.tencent.mobileqq.ocr.a;
public class a {
public String a;
// 1
public int b;
// file location
public String c;
// null
public String d;
// 0
public long e;
// md5
public String f;
// null
public String g;
// false
public boolean h;
// 0
public int i;
// 0
public int j;
// 0
public long k;
// null
public String l;
}

View File

@ -0,0 +1,10 @@
package com.tencent.mobileqq.ocr.api;
import mqq.app.api.IRuntimeService;
public interface IPicOcrService extends IRuntimeService {
void putOcrResult(String str, com.tencent.mobileqq.l0.b.c cVar);
void requestOcr(com.tencent.mobileqq.ocr.a.a aVar, com.tencent.mobileqq.l0.b.a callback);
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class OcrServiceImpl {
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class PicOcrServiceImpl {
}

View File

@ -0,0 +1,11 @@
package com.tencent.mobileqq.qqguildsdk.api.impl;
import com.tencent.qqnt.kernelgpro.nativeinterface.IKernelGuildService;
public class GProSessionImpl {
public IKernelGuildService getGuildService() {
return null;
}
}

View File

@ -1,20 +1,12 @@
package com.tencent.mobileqq.qqguildsdk.data;
import android.text.TextUtils;
import com.tencent.mobileqq.qqguildsdk.data.genc.GGProMedalInfo;
import com.tencent.mobileqq.qqguildsdk.data.genc.GGProNavigationInfo;
import com.tencent.mobileqq.qqguildsdk.data.genc.IGProMedalInfo;
import com.tencent.mobileqq.qqguildsdk.data.genc.IGProNavigationInfo;
import com.tencent.qqnt.kernel.nativeinterface.GProGuild;
import com.tencent.qqnt.kernel.nativeinterface.GProGuildSpeakableThreshold;
import com.tencent.qqnt.kernel.nativeinterface.GProMedalInfo;
import com.tencent.qqnt.kernel.nativeinterface.GProNavigationInfo;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Iterator;
public class GProGuildInfo implements IGProGuildInfo {

View File

@ -2,7 +2,7 @@ package com.tencent.mobileqq.qqguildsdk.data;
import androidx.annotation.NonNull;
import com.tencent.qqnt.kernel.nativeinterface.GProGuildRole;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
import java.util.Iterator;

View File

@ -1,6 +1,6 @@
package com.tencent.mobileqq.qqguildsdk.data;
import com.tencent.qqnt.kernel.nativeinterface.GProGuildRole;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import com.tencent.qqnt.kernel.nativeinterface.GProUser;
import java.util.ArrayList;

View File

@ -1,14 +1,9 @@
package com.tencent.mobileqq.qqguildsdk.data.genc;
import com.tencent.mobileqq.qqguildsdk.data.GProGuildRoleInfo;
import com.tencent.mobileqq.qqguildsdk.data.GProUserInfo;
import com.tencent.mobileqq.qqguildsdk.data.IGProGuildRoleInfo;
import com.tencent.mobileqq.qqguildsdk.data.IGProUserInfo;
import com.tencent.qqnt.kernel.nativeinterface.GProGuildRole;
import com.tencent.qqnt.kernel.nativeinterface.GProUser;
import java.util.ArrayList;
import java.util.Iterator;
public class GGProGuildMemberSearchResult implements IGProGuildMemberSearchResult {

View File

@ -1,7 +1,9 @@
package com.tencent.qphone.base.util;
public abstract class CodecWarpper {
public abstract void onResponse(int i2, Object obj, int i3);
// public abstract void onResponse(int i2, Object obj, int i3);
public abstract void onInvalidData(int i2, int i3, String str);
public abstract void onResponse(int i2, Object obj, int i3, byte[] bArr);

View File

@ -1,8 +1,8 @@
package com.tencent.qqnt.aio.api;
import com.tencent.mobileqq.qroute.QRouteApi;
import com.tencent.qqnt.kernel.nativeinterface.Contact;
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
public interface IAIOFileTransfer extends QRouteApi {
void sendLocalFile(Contact contact, String path, IOperateCallback cb);

View File

@ -0,0 +1,4 @@
package com.tencent.qqnt.kernel.api.impl;
public class GroupService {
}

View File

@ -18,11 +18,6 @@ public class MsgService {
public void addMsgListener(IKernelMsgListener listener) {
}
public void removeMsgListener(@NotNull IKernelMsgListener iKernelMsgListener) {
}
public String getRichMediaFilePathForGuild(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) {
return null;
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.util.ArrayList;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
public final class GProJoinGuildResult {
GProGuild guildInfo;
GProGuildInit guildInit;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
import java.io.Serializable;
public final class GProJoinGuildsResult implements Serializable {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
public final class GProRoleMemberList {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
public final class GProSearchMemberAndRoleResult {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
import java.io.Serializable;
public final class GrayTipGroupMember implements Serializable {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
import java.util.ArrayList;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import java.util.ArrayList;

View File

@ -0,0 +1,36 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberCommonListResult {
public long groupCode;
public int identifyFlag;
public long startUin;
public ArrayList<MemberCommonInfo> memberList = new ArrayList<>();
public String strErrorInfo = "";
public long getGroupCode() {
return this.groupCode;
}
public int getIdentifyFlag() {
return this.identifyFlag;
}
public ArrayList<MemberCommonInfo> getMemberList() {
return this.memberList;
}
public long getStartUin() {
return this.startUin;
}
public String getStrErrorInfo() {
return this.strErrorInfo;
}
public String toString() {
return "GroupMemberCommonListResult{groupCode=" + this.groupCode + ",startUin=" + this.startUin + ",identifyFlag=" + this.identifyFlag + ",memberList=" + this.memberList + ",strErrorInfo=" + this.strErrorInfo + ",}";
}
}

View File

@ -0,0 +1,101 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberCommonReq {
public long groupCode;
public int sourceType;
public String startUin = "";
public String identifyFlag = "";
public ArrayList<Long> uinList = new ArrayList<>();
public MemberCommonInfoFilter memberCommonFilter = new MemberCommonInfoFilter();
public String memberNum = "";
public String filterMethod = "";
public String onlineFlag = "";
public String realSpecialTitleFlag = "";
public String getFilterMethod() {
return this.filterMethod;
}
public long getGroupCode() {
return this.groupCode;
}
public String getIdentifyFlag() {
return this.identifyFlag;
}
public MemberCommonInfoFilter getMemberCommonFilter() {
return this.memberCommonFilter;
}
public String getMemberNum() {
return this.memberNum;
}
public String getOnlineFlag() {
return this.onlineFlag;
}
public String getRealSpecialTitleFlag() {
return this.realSpecialTitleFlag;
}
public int getSourceType() {
return this.sourceType;
}
public String getStartUin() {
return this.startUin;
}
public ArrayList<Long> getUinList() {
return this.uinList;
}
public void setFilterMethod(String str) {
this.filterMethod = str;
}
public void setGroupCode(long j2) {
this.groupCode = j2;
}
public void setIdentifyFlag(String str) {
this.identifyFlag = str;
}
public void setMemberCommonFilter(MemberCommonInfoFilter memberCommonInfoFilter) {
this.memberCommonFilter = memberCommonInfoFilter;
}
public void setMemberNum(String str) {
this.memberNum = str;
}
public void setOnlineFlag(String str) {
this.onlineFlag = str;
}
public void setRealSpecialTitleFlag(String str) {
this.realSpecialTitleFlag = str;
}
public void setSourceType(int i2) {
this.sourceType = i2;
}
public void setStartUin(String str) {
this.startUin = str;
}
public void setUinList(ArrayList<Long> arrayList) {
this.uinList = arrayList;
}
public String toString() {
return "GroupMemberCommonReq{groupCode=" + this.groupCode + ",startUin=" + this.startUin + ",identifyFlag=" + this.identifyFlag + ",uinList=" + this.uinList + ",memberCommonFilter=" + this.memberCommonFilter + ",memberNum=" + this.memberNum + ",filterMethod=" + this.filterMethod + ",onlineFlag=" + this.onlineFlag + ",realSpecialTitleFlag=" + this.realSpecialTitleFlag + ",sourceType=" + this.sourceType + ",}";
}
}

View File

@ -0,0 +1,81 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberExtListResult {
public long dataTime;
public long endUin;
public long groupCode;
public int levelNameSeq;
public int memberInfoSeq;
public int sysShowFlag;
public int timeToUpdate;
public int userShowFlag;
public int userShowFlagNew;
public ArrayList<MemberExtInfo> memberLevelInfo = new ArrayList<>();
public ArrayList<MemberLevelName> msgLevelName = new ArrayList<>();
public String strOwnerName = "";
public String strAdminName = "";
public ArrayList<MemberLevelName> msgLevelNameNew = new ArrayList<>();
public long getDataTime() {
return this.dataTime;
}
public long getEndUin() {
return this.endUin;
}
public long getGroupCode() {
return this.groupCode;
}
public int getLevelNameSeq() {
return this.levelNameSeq;
}
public int getMemberInfoSeq() {
return this.memberInfoSeq;
}
public ArrayList<MemberExtInfo> getMemberLevelInfo() {
return this.memberLevelInfo;
}
public ArrayList<MemberLevelName> getMsgLevelName() {
return this.msgLevelName;
}
public ArrayList<MemberLevelName> getMsgLevelNameNew() {
return this.msgLevelNameNew;
}
public String getStrAdminName() {
return this.strAdminName;
}
public String getStrOwnerName() {
return this.strOwnerName;
}
public int getSysShowFlag() {
return this.sysShowFlag;
}
public int getTimeToUpdate() {
return this.timeToUpdate;
}
public int getUserShowFlag() {
return this.userShowFlag;
}
public int getUserShowFlagNew() {
return this.userShowFlagNew;
}
public String toString() {
return "GroupMemberExtListResult{groupCode=" + this.groupCode + ",memberLevelInfo=" + this.memberLevelInfo + ",msgLevelName=" + this.msgLevelName + ",endUin=" + this.endUin + ",dataTime=" + this.dataTime + ",userShowFlag=" + this.userShowFlag + ",sysShowFlag=" + this.sysShowFlag + ",timeToUpdate=" + this.timeToUpdate + ",strOwnerName=" + this.strOwnerName + ",strAdminName=" + this.strAdminName + ",levelNameSeq=" + this.levelNameSeq + ",userShowFlagNew=" + this.userShowFlagNew + ",msgLevelNameNew=" + this.msgLevelNameNew + ",memberInfoSeq=" + this.memberInfoSeq + ",}";
}
}

View File

@ -0,0 +1,101 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public class GroupMemberExtReq {
public long groupCode;
public int sourceType;
public String beginUin = "";
public String dataTime = "";
public ArrayList<Long> uinList = new ArrayList<>();
public MemberExtInfoFilter memberExtFilter = new MemberExtInfoFilter();
public String seq = "";
public String uinNum = "";
public String groupType = "";
public String richCardNameVer = "";
public String getBeginUin() {
return this.beginUin;
}
public String getDataTime() {
return this.dataTime;
}
public long getGroupCode() {
return this.groupCode;
}
public String getGroupType() {
return this.groupType;
}
public MemberExtInfoFilter getMemberExtFilter() {
return this.memberExtFilter;
}
public String getRichCardNameVer() {
return this.richCardNameVer;
}
public String getSeq() {
return this.seq;
}
public int getSourceType() {
return this.sourceType;
}
public ArrayList<Long> getUinList() {
return this.uinList;
}
public String getUinNum() {
return this.uinNum;
}
public void setBeginUin(String str) {
this.beginUin = str;
}
public void setDataTime(String str) {
this.dataTime = str;
}
public void setGroupCode(long j2) {
this.groupCode = j2;
}
public void setGroupType(String str) {
this.groupType = str;
}
public void setMemberExtFilter(MemberExtInfoFilter memberExtInfoFilter) {
this.memberExtFilter = memberExtInfoFilter;
}
public void setRichCardNameVer(String str) {
this.richCardNameVer = str;
}
public void setSeq(String str) {
this.seq = str;
}
public void setSourceType(int i2) {
this.sourceType = i2;
}
public void setUinList(ArrayList<Long> arrayList) {
this.uinList = arrayList;
}
public void setUinNum(String str) {
this.uinNum = str;
}
public String toString() {
return "GroupMemberExtReq{groupCode=" + this.groupCode + ",beginUin=" + this.beginUin + ",dataTime=" + this.dataTime + ",uinList=" + this.uinList + ",memberExtFilter=" + this.memberExtFilter + ",seq=" + this.seq + ",uinNum=" + this.uinNum + ",groupType=" + this.groupType + ",richCardNameVer=" + this.richCardNameVer + ",sourceType=" + this.sourceType + ",}";
}
}

View File

@ -0,0 +1,32 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
import java.util.HashMap;
public class GroupMemberListResult {
public boolean finish;
public boolean hasRobot;
public ArrayList<GroupMemberInfoListId> ids = new ArrayList<>();
public HashMap<String, MemberInfo> infos = new HashMap<>();
public boolean getFinish() {
return this.finish;
}
public boolean getHasRobot() {
return this.hasRobot;
}
public ArrayList<GroupMemberInfoListId> getIds() {
return this.ids;
}
public HashMap<String, MemberInfo> getInfos() {
return this.infos;
}
public String toString() {
return "GroupMemberListResult{ids=" + this.ids + ",infos=" + this.infos + ",finish=" + this.finish + ",hasRobot=" + this.hasRobot + ",}";
}
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.MemberRole;
public final class GroupSimpleInfo implements IKernelModel {
String avatarUrl;
int discussToGroupMaxMsgSeq;

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
public interface IGProFetchChannelInvisibleRoleListCallback {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
public interface IGProFetchChannelLiveableRoleListCallback {

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
public interface IGProFetchGuildInfoCallback {
void onFetchGuildInfo(int code, String reason, GProGuild gProGuild);
}

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
import java.util.ArrayList;
public interface IGProFetchGuildListCallback {

View File

@ -1,5 +0,0 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGProResultCallback {
void onResult(int code, String msg, GProSecurityResult result);
}

View File

@ -0,0 +1,6 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberCommonCallback {
void onResult(int i2, String str, GroupMemberCommonListResult groupMemberCommonListResult);
}

View File

@ -0,0 +1,5 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberExtCallback {
void onResult(int i2, String str, GroupMemberExtListResult groupMemberExtListResult);
}

View File

@ -0,0 +1,6 @@
package com.tencent.qqnt.kernel.nativeinterface;
public interface IGroupMemberListCallback {
void onResult(int result, String str, GroupMemberListResult groupMemberListResult);
}

View File

@ -1,7 +1,25 @@
package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
public interface IKernelGroupService {
void getTransferableMemberInfo(long uin, IGetTransferableMemberCallback cb);
long addKernelGroupListener(IKernelGroupListener ln);
void getAllMemberList(long groupCode, boolean refresh, IGroupMemberListCallback iGroupMemberListCallback);
void getMemberCommonInfo(GroupMemberCommonReq groupMemberCommonReq, IGroupMemberCommonCallback iGroupMemberCommonCallback);
void getMemberExtInfo(GroupMemberExtReq groupMemberExtReq, IGroupMemberExtCallback iGroupMemberExtCallback);
void getMemberInfo(long j2, ArrayList<String> arrayList, boolean z, IOperateCallback iOperateCallback);
void getMemberInfoForMqq(long groupCode, ArrayList<String> uids, boolean z, IGroupMemberListCallback iGroupMemberListCallback);
void getNextMemberList(String sceneId, GroupMemberInfoListId groupMemberInfoListId, int i2, IGroupMemberListCallback iGroupMemberListCallback);
void getPrevMemberList(String sceneId, GroupMemberInfoListId groupMemberInfoListId, int i2, IGroupMemberListCallback iGroupMemberListCallback);
}

View File

@ -1,5 +1,8 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuildRole;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -1,5 +1,14 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProGuild;
import com.tencent.qqnt.kernelgpro.nativeinterface.GProRoleCreateInfo;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProCreateRoleCallback;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProFetchMemberListWithRoleCallback;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProFetchMemberRolesCallback;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProFetchRoleListPermissionCallback;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProFetchRolePermissionCallback;
import com.tencent.qqnt.kernelgpro.nativeinterface.IGProResultCallback;
import java.util.ArrayList;
public interface IKernelGuildService {

View File

@ -2,6 +2,7 @@ package com.tencent.qqnt.kernel.nativeinterface;
import java.util.ArrayList;
import java.util.HashMap;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
public interface IKernelMsgListener {
void onAddSendMsg(MsgRecord msgRecord);
@ -116,7 +117,7 @@ public interface IKernelMsgListener {
void onSendMsgError(long j2, Contact contact, int i2, String str);
void onSysMsgNotification(int i2, long j2, long j3, ArrayList<Byte> arrayList);
void onSysMsgNotification(int i2, long j2, long j3, boolean z, ArrayList<Byte> arrayList);
void onTempChatInfoUpdate(TempChatInfo tempChatInfo);

View File

@ -1,5 +1,7 @@
package com.tencent.qqnt.kernel.nativeinterface;
import com.tencent.qqnt.kernelpublic.nativeinterface.Contact;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;

Some files were not shown because too many files have changed in this diff Show More