From 69ff71e027d0fd454b86404336b908058f0b9528 Mon Sep 17 00:00:00 2001 From: eggmanQQQ2 <3671373519@qq.com> Date: Mon, 7 Jul 2025 10:26:00 +0800 Subject: [PATCH] feat : first --- DeleteEmptyFolders.java | 64 +++ apkinfo.py | 998 +++++++++++++++++++++++++++++++++ build.gradle | 50 ++ channel.txt | 1 + gradle.properties | 39 ++ gradlew | 160 ++++++ gradlew.bat | 90 +++ local.properties | 8 + mode.json | 79 +++ molistar.jks | Bin 0 -> 2812 bytes molistar_debug.jks | Bin 0 -> 2812 bytes project.gradle | 17 + readme.md | 52 ++ screenMatch.properties | 54 ++ screenMatch_example_dimens.xml | 431 ++++++++++++++ settings.gradle | 12 + 方案.txt | 108 ++++ 17 files changed, 2163 insertions(+) create mode 100644 DeleteEmptyFolders.java create mode 100644 apkinfo.py create mode 100644 build.gradle create mode 100644 channel.txt create mode 100644 gradle.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 local.properties create mode 100644 mode.json create mode 100644 molistar.jks create mode 100644 molistar_debug.jks create mode 100644 project.gradle create mode 100644 readme.md create mode 100644 screenMatch.properties create mode 100644 screenMatch_example_dimens.xml create mode 100644 settings.gradle create mode 100644 方案.txt diff --git a/DeleteEmptyFolders.java b/DeleteEmptyFolders.java new file mode 100644 index 0000000..bae34d4 --- /dev/null +++ b/DeleteEmptyFolders.java @@ -0,0 +1,64 @@ + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +public class DeleteEmptyFolders { + + // 白名单路径集合 + private static final Set WHITELIST_PATHS = new HashSet<>(); + + static { + // 添加白名单路径,请根据实际需求修改 +// WHITELIST_PATHS.add("/Users/zhanglaiman/WorkSpace/Molistar/core/src/main/java/com"); + } + + public static void main(String[] args) { + String targetDir = "/Users/zhanglaiman/WorkSpace/Molistar/app"; + File rootDir = new File(targetDir); + deleteEmptyFolders(rootDir); + } + + private static boolean isDirectoryEmpty(File dir) { + File[] files = dir.listFiles(); + if (files == null || files.length == 0) { + return true; + } + for (File file : files) { + if (file.isDirectory()) { + if (!isDirectoryEmpty(file)) { + return false; + } + } else if (file.getName().endsWith(".java")) { + return false; + } + } + return true; + } + + private static void deleteEmptyFolders(File dir) { + if (dir == null || !dir.exists() || dir.isFile()) { + return; + } + + // 跳过白名单中的路径 + if (WHITELIST_PATHS.contains(dir.getAbsolutePath())) { + System.out.println("跳过白名单路径: " + dir.getAbsolutePath()); + return; + } + + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteEmptyFolders(file); + } + } + } + + if (isDirectoryEmpty(dir)) { + System.out.println("删除空文件夹: " + dir.getAbsolutePath()); + dir.delete(); + } + } +} \ No newline at end of file diff --git a/apkinfo.py b/apkinfo.py new file mode 100644 index 0000000..f99f722 --- /dev/null +++ b/apkinfo.py @@ -0,0 +1,998 @@ +# This file is part of Androguard. +# +# Copyright (C) 2012, Anthony Desnos +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import StringIO +from struct import pack, unpack +from xml.sax.saxutils import escape +from zlib import crc32 +import re +import collections +import sys +import os +import logging +import types +import random +import string +import imp + +from xml.dom import minidom + +NS_ANDROID_URI = 'http://schemas.android.com/apk/res/android' + +ZIPMODULE = 1 + + +def read(filename, binary=True): + with open(filename, 'rb' if binary else 'r') as f: + return f.read() + + +def sign_apk(filename, keystore, storepass): + from subprocess import Popen, PIPE, STDOUT + compile = Popen([CONF["PATH_JARSIGNER"], "-sigalg", "MD5withRSA", + "-digestalg", "SHA1", "-storepass", storepass, "-keystore", + keystore, filename, "alias_name"], + stdout=PIPE, + stderr=STDOUT) + stdout, stderr = compile.communicate() + + +class Error(Exception): + """Base class for exceptions in this module.""" + pass + + +class FileNotPresent(Error): + pass + + +######################################################## APK FORMAT ######################################################## +class APK(object): + """ + This class can access to all elements in an APK file + + :param filename: specify the path of the file, or raw data + :param raw: specify if the filename is a path or raw data (optional) + :param mode: specify the mode to open the file (optional) + :param magic_file: specify the magic file (optional) + :param zipmodule: specify the type of zip module to use (0:chilkat, 1:zipfile, 2:patch zipfile) + + :type filename: string + :type raw: boolean + :type mode: string + :type magic_file: string + :type zipmodule: int + + :Example: + APK("myfile.apk") + APK(read("myfile.apk"), raw=True) + """ + + def __init__(self, + filename, + raw=False, + mode="r", + magic_file=None, + zipmodule=ZIPMODULE): + self.filename = filename + + self.xml = {} + self.axml = {} + self.arsc = {} + + self.package = "" + self.androidversion = {} + self.permissions = [] + self.declared_permissions = {} + self.valid_apk = False + + self.files = {} + self.files_crc32 = {} + + self.magic_file = magic_file + + if raw is True: + self.__raw = filename + else: + self.__raw = read(filename) + + self.zipmodule = zipmodule + + if zipmodule == 0: + self.zip = ChilkatZip(self.__raw) + elif zipmodule == 2: + from androguard.patch import zipfile + self.zip = zipfile.ZipFile(StringIO.StringIO(self.__raw), mode=mode) + else: + import zipfile + self.zip = zipfile.ZipFile(StringIO.StringIO(self.__raw), mode=mode) + + for i in self.zip.namelist(): + if i == "AndroidManifest.xml": + self.axml[i] = AXMLPrinter(self.zip.read(i)) + try: + self.xml[i] = minidom.parseString(self.axml[i].get_buff()) + except: + self.xml[i] = None + + if self.xml[i] != None: + self.package = self.xml[i].documentElement.getAttribute( + "package") + self.androidversion[ + "Code" + ] = self.xml[i].documentElement.getAttributeNS( + NS_ANDROID_URI, "versionCode") + self.androidversion[ + "Name" + ] = self.xml[i].documentElement.getAttributeNS( + NS_ANDROID_URI, "versionName") + + self.valid_apk = True + + def get_AndroidManifest(self): + """ + Return the Android Manifest XML file + + :rtype: xml object + """ + return self.xml["AndroidManifest.xml"] + + def is_valid_APK(self): + """ + Return true if the APK is valid, false otherwise + + :rtype: boolean + """ + return self.valid_apk + + def get_filename(self): + """ + Return the filename of the APK + + :rtype: string + """ + return self.filename + + def get_package(self): + """ + Return the name of the package + + :rtype: string + """ + return self.package + + def get_version_code(self): + """ + Return the android version code + + :rtype: string + """ + return self.androidversion["Code"] + + def get_version_name(self): + """ + Return the android version name + + :rtype: string + """ + return self.androidversion["Name"] + + def get_raw(self): + """ + Return raw bytes of the APK + + :rtype: string + """ + return self.__raw + + def get_elements(self, tag_name, attribute): + """ + Return elements in xml files which match with the tag name and the specific attribute + + :param tag_name: a string which specify the tag name + :param attribute: a string which specify the attribute + """ + l = [] + for i in self.xml: + for item in self.xml[i].getElementsByTagName(tag_name): + value = item.getAttributeNS(NS_ANDROID_URI, attribute) + value = self.format_value(value) + + l.append(str(value)) + return l + + def format_value(self, value): + if len(value) > 0: + if value[0] == ".": + value = self.package + value + else: + v_dot = value.find(".") + if v_dot == 0: + value = self.package + "." + value + elif v_dot == -1: + value = self.package + "." + value + return value + + def get_element(self, tag_name, attribute, **attribute_filter): + """ + Return element in xml files which match with the tag name and the specific attribute + + :param tag_name: specify the tag name + :type tag_name: string + :param attribute: specify the attribute + :type attribute: string + + :rtype: string + """ + for i in self.xml: + for item in self.xml[i].getElementsByTagName(tag_name): + skip_this_item = False + for attr, val in attribute_filter.items(): + attr_val = item.getAttributeNS(NS_ANDROID_URI, attr) + if attr_val != val: + skip_this_item = True + break + + if skip_this_item: + continue + + value = item.getAttributeNS(NS_ANDROID_URI, attribute) + + if len(value) > 0: + return value + return None + + def get_max_sdk_version(self): + """ + Return the android:maxSdkVersion attribute + + :rtype: string + """ + return self.get_element("uses-sdk", "maxSdkVersion") + + def get_min_sdk_version(self): + """ + Return the android:minSdkVersion attribute + + :rtype: string + """ + return self.get_element("uses-sdk", "minSdkVersion") + + def get_target_sdk_version(self): + """ + Return the android:targetSdkVersion attribute + + :rtype: string + """ + return self.get_element("uses-sdk", "targetSdkVersion") + + def get_android_manifest_axml(self): + """ + Return the :class:`AXMLPrinter` object which corresponds to the AndroidManifest.xml file + + :rtype: :class:`AXMLPrinter` + """ + try: + return self.axml["AndroidManifest.xml"] + except KeyError: + return None + + def get_android_manifest_xml(self): + """ + Return the xml object which corresponds to the AndroidManifest.xml file + + :rtype: object + """ + try: + return self.xml["AndroidManifest.xml"] + except KeyError: + return None + + def show(self): + + print "PACKAGE: ", self.get_package() + print "VERSION NAME:", self.get_version_name() + print "VERSION CODE:", self.get_version_code() + +################################## AXML FORMAT ######################################## +# Translated from +# http://code.google.com/p/android4me/source/browse/src/android/content/res/AXmlResourceParser.java + +UTF8_FLAG = 0x00000100 +CHUNK_STRINGPOOL_TYPE = 0x001C0001 +CHUNK_NULL_TYPE = 0x00000000 + + +class StringBlock(object): + + def __init__(self, buff): + self.start = buff.get_idx() + self._cache = {} + self.header_size, self.header = self.skipNullPadding(buff) + + self.chunkSize = unpack('> 8, header & 0xFF + + def getString(self, idx): + if idx in self._cache: + return self._cache[idx] + + if idx < 0 or not self.m_stringOffsets or idx >= len( + self.m_stringOffsets): + return "" + + offset = self.m_stringOffsets[idx] + + if self.m_isUTF8: + self._cache[idx] = self.decode8(offset) + else: + self._cache[idx] = self.decode16(offset) + + return self._cache[idx] + + def getStyle(self, idx): + # FIXME + return self.m_styles[idx] + + def decode8(self, offset): + str_len, skip = self.decodeLength(offset, 1) + offset += skip + + encoded_bytes, skip = self.decodeLength(offset, 1) + offset += skip + + data = self.m_charbuff[offset: offset + encoded_bytes] + + return self.decode_bytes(data, 'utf-8', str_len) + + def decode16(self, offset): + str_len, skip = self.decodeLength(offset, 2) + offset += skip + + encoded_bytes = str_len * 2 + + data = self.m_charbuff[offset: offset + encoded_bytes] + + return self.decode_bytes(data, 'utf-16', str_len) + + def decode_bytes(self, data, encoding, str_len): + string = data.decode(encoding, 'replace') + if len(string) != str_len: + warning("invalid decoded string length") + return string + + def decodeLength(self, offset, sizeof_char): + length = ord(self.m_charbuff[offset]) + + sizeof_2chars = sizeof_char << 1 + fmt_chr = 'B' if sizeof_char == 1 else 'H' + fmt = "<2" + fmt_chr + + length1, length2 = unpack(fmt, self.m_charbuff[offset:(offset + sizeof_2chars)]) + + highbit = 0x80 << (8 * (sizeof_char - 1)) + + if (length & highbit) != 0: + return ((length1 & ~highbit) << (8 * sizeof_char)) | length2, sizeof_2chars + else: + return length1, sizeof_char + + def show(self): + print "StringBlock(%x, %x, %x, %x, %x, %x" % ( + self.start, + self.header, + self.header_size, + self.chunkSize, + self.stringsOffset, + self.flags) + for i in range(0, len(self.m_stringOffsets)): + print i, repr(self.getString(i)) + +START_DOCUMENT = 0 +END_DOCUMENT = 1 +START_TAG = 2 +END_TAG = 3 +TEXT = 4 + +ATTRIBUTE_IX_NAMESPACE_URI = 0 +ATTRIBUTE_IX_NAME = 1 +ATTRIBUTE_IX_VALUE_STRING = 2 +ATTRIBUTE_IX_VALUE_TYPE = 3 +ATTRIBUTE_IX_VALUE_DATA = 4 +ATTRIBUTE_LENGHT = 5 + +CHUNK_AXML_FILE = 0x00080003 +CHUNK_RESOURCEIDS = 0x00080180 +CHUNK_XML_FIRST = 0x00100100 +CHUNK_XML_START_NAMESPACE = 0x00100100 +CHUNK_XML_END_NAMESPACE = 0x00100101 +CHUNK_XML_START_TAG = 0x00100102 +CHUNK_XML_END_TAG = 0x00100103 +CHUNK_XML_TEXT = 0x00100104 +CHUNK_XML_LAST = 0x00100104 + + +class AXMLParser(object): + + def __init__(self, raw_buff): + self.reset() + + self.valid_axml = True + self.buff = BuffHandle(raw_buff) + + axml_file = unpack(' CHUNK_XML_LAST: + warning("invalid chunk type") + + # Fake START_DOCUMENT event. + if chunkType == CHUNK_XML_START_TAG and event == -1: + self.m_event = START_DOCUMENT + break + + self.buff.read(4) # /*chunkSize*/ + lineNumber = unpack('> 16) - 1 + attributeCount = attributeCount & 0xFFFF + self.m_classAttribute = unpack('> 16) - 1 + + self.m_classAttribute = (self.m_classAttribute & 0xFFFF) - 1 + + for i in range(0, attributeCount * ATTRIBUTE_LENGHT): + self.m_attributes.append(unpack('> 24 + + self.m_event = START_TAG + break + + if chunkType == CHUNK_XML_END_TAG: + self.m_namespaceUri = unpack('= len(self.m_attributes): + warning("Invalid attribute index") + + return offset + + def getAttributeCount(self): + if self.m_event != START_TAG: + return -1 + + return len(self.m_attributes) / ATTRIBUTE_LENGHT + + def getAttributePrefix(self, index): + offset = self.getAttributeOffset(index) + uri = self.m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI] + + prefix = self.getPrefixByUri(uri) + + if prefix == -1: + return "" + + return self.sb.getString(prefix) + + def getAttributeName(self, index): + offset = self.getAttributeOffset(index) + name = self.m_attributes[offset + ATTRIBUTE_IX_NAME] + + if name == -1: + return "" + + res = self.sb.getString(name) + if not res: + attr = self.m_resourceIDs[name] + if attr in SYSTEM_RESOURCES['attributes']['inverse']: + res = 'android:' + SYSTEM_RESOURCES['attributes']['inverse'][ + attr + ] + + return res + + def getAttributeValueType(self, index): + offset = self.getAttributeOffset(index) + return self.m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE] + + def getAttributeValueData(self, index): + offset = self.getAttributeOffset(index) + return self.m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA] + + def getAttributeValue(self, index): + offset = self.getAttributeOffset(index) + valueType = self.m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE] + if valueType == TYPE_STRING: + valueString = self.m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING] + return self.sb.getString(valueString) + # WIP + return "" + +# resource constants + +TYPE_ATTRIBUTE = 2 +TYPE_DIMENSION = 5 +TYPE_FIRST_COLOR_INT = 28 +TYPE_FIRST_INT = 16 +TYPE_FLOAT = 4 +TYPE_FRACTION = 6 +TYPE_INT_BOOLEAN = 18 +TYPE_INT_DEC = 16 +TYPE_INT_HEX = 17 +TYPE_LAST_COLOR_INT = 31 +TYPE_LAST_INT = 31 +TYPE_NULL = 0 +TYPE_REFERENCE = 1 +TYPE_STRING = 3 + +TYPE_TABLE = { + TYPE_ATTRIBUTE: "attribute", + TYPE_DIMENSION: "dimension", + TYPE_FLOAT: "float", + TYPE_FRACTION: "fraction", + TYPE_INT_BOOLEAN: "int_boolean", + TYPE_INT_DEC: "int_dec", + TYPE_INT_HEX: "int_hex", + TYPE_NULL: "null", + TYPE_REFERENCE: "reference", + TYPE_STRING: "string", +} + +RADIX_MULTS = [0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010] +DIMENSION_UNITS = ["px", "dip", "sp", "pt", "in", "mm"] +FRACTION_UNITS = ["%", "%p"] + +COMPLEX_UNIT_MASK = 15 + + +def complexToFloat(xcomplex): + return (float)(xcomplex & 0xFFFFFF00) * RADIX_MULTS[(xcomplex >> 4) & 3] + + +def getPackage(id): + if id >> 24 == 1: + return "android:" + return "" + + +def format_value(_type, _data, lookup_string=lambda ix: ""): + if _type == TYPE_STRING: + return lookup_string(_data) + + elif _type == TYPE_ATTRIBUTE: + return "?%s%08X" % (getPackage(_data), _data) + + elif _type == TYPE_REFERENCE: + return "@%s%08X" % (getPackage(_data), _data) + + elif _type == TYPE_FLOAT: + return "%f" % unpack("=f", pack("=L", _data))[0] + + elif _type == TYPE_INT_HEX: + return "0x%08X" % _data + + elif _type == TYPE_INT_BOOLEAN: + if _data == 0: + return "false" + return "true" + + elif _type == TYPE_DIMENSION: + return "%f%s" % (complexToFloat(_data), DIMENSION_UNITS[_data & COMPLEX_UNIT_MASK]) + + elif _type == TYPE_FRACTION: + return "%f%s" % (complexToFloat(_data) * 100, FRACTION_UNITS[_data & COMPLEX_UNIT_MASK]) + + elif _type >= TYPE_FIRST_COLOR_INT and _type <= TYPE_LAST_COLOR_INT: + return "#%08X" % _data + + elif _type >= TYPE_FIRST_INT and _type <= TYPE_LAST_INT: + return "%d" % long2int(_data) + + return "<0x%X, type 0x%02X>" % (_data, _type) + + +class AXMLPrinter(object): + + def __init__(self, raw_buff): + self.axml = AXMLParser(raw_buff) + self.xmlns = False + + self.buff = u'' + + while True and self.axml.is_valid(): + _type = self.axml.next() + + if _type == START_DOCUMENT: + self.buff += u'\n' + elif _type == START_TAG: + self.buff += u'<' + self.getPrefix(self.axml.getPrefix( + )) + self.axml.getName() + u'\n' + self.buff += self.axml.getXMLNS() + + for i in range(0, self.axml.getAttributeCount()): + self.buff += "%s%s=\"%s\"\n" % ( + self.getPrefix( + self.axml.getAttributePrefix(i)), + self.axml.getAttributeName(i), + self._escape(self.getAttributeValue(i))) + + self.buff += u'>\n' + + elif _type == END_TAG: + self.buff += "\n" % ( + self.getPrefix(self.axml.getPrefix()), self.axml.getName()) + + elif _type == TEXT: + self.buff += "%s\n" % self.axml.getText() + + elif _type == END_DOCUMENT: + break + + # pleed patch + def _escape(self, s): + s = s.replace("&", "&") + s = s.replace('"', """) + s = s.replace("'", "'") + s = s.replace("<", "<") + s = s.replace(">", ">") + return escape(s) + + def get_buff(self): + return self.buff.encode('utf-8') + + def get_xml(self): + return minidom.parseString(self.get_buff()).toprettyxml( + encoding="utf-8") + + def get_xml_obj(self): + return minidom.parseString(self.get_buff()) + + def getPrefix(self, prefix): + if prefix is None or len(prefix) == 0: + return u'' + + return prefix + u':' + + def getAttributeValue(self, index): + _type = self.axml.getAttributeValueType(index) + _data = self.axml.getAttributeValueData(index) + + return format_value(_type, _data, lambda _: self.axml.getAttributeValue(index)) + + +class SV(object): + + def __init__(self, size, buff): + self.__size = size + self.__value = unpack(self.__size, buff)[0] + + def _get(self): + return pack(self.__size, self.__value) + + def __str__(self): + return "0x%x" % self.__value + + def __int__(self): + return self.__value + + def get_value_buff(self): + return self._get() + + def get_value(self): + return self.__value + + def set_value(self, attr): + self.__value = attr + + +class SVs(object): + + def __init__(self, size, ntuple, buff): + self.__size = size + + self.__value = ntuple._make(unpack(self.__size, buff)) + + def _get(self): + l = [] + for i in self.__value._fields: + l.append(getattr(self.__value, i)) + return pack(self.__size, *l) + + def _export(self): + return [x for x in self.__value._fields] + + def get_value_buff(self): + return self._get() + + def get_value(self): + return self.__value + + def set_value(self, attr): + self.__value = self.__value._replace(**attr) + + def __str__(self): + return self.__value.__str__() + + +class BuffHandle(object): + + def __init__(self, buff): + self.__buff = buff + self.__idx = 0 + + def size(self): + return len(self.__buff) + + def set_idx(self, idx): + self.__idx = idx + + def get_idx(self): + return self.__idx + + def readNullString(self, size): + data = self.read(size) + return data + + def read_b(self, size): + return self.__buff[self.__idx:self.__idx + size] + + def read_at(self, offset, size): + return self.__buff[offset:offset + size] + + def read(self, size): + if isinstance(size, SV): + size = size.value + + buff = self.__buff[self.__idx:self.__idx + size] + self.__idx += size + + return buff + + def end(self): + return self.__idx == len(self.__buff) + + +class Buff(object): + + def __init__(self, offset, buff): + self.offset = offset + self.buff = buff + + self.size = len(buff) + + +def long2int(l): + if l > 0x7fffffff: + l = (0x7fffffff & l) - 0x80000000 + return l diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e7cb245 --- /dev/null +++ b/build.gradle @@ -0,0 +1,50 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext.kotlin_version = '1.8.22' + + println "\n\n\n" + println '当前选择版本 Version Name:'+ version_name + println '当前选择版本 Version Code:'+ Integer.valueOf(version_code) + println "\n\n\n" + + repositories { + mavenCentral() + mavenLocal() + jcenter() + google() + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://jitpack.io' } + maven { url 'https://repo1.maven.org/maven2/' } + maven { url 'https://raw.githubusercontent.com/martinloren/AabResGuard/mvn-repo' } + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.4.2' + // aRouter + classpath "com.alibaba:arouter-register:1.0.2" + //realm 数据库插件 + classpath "io.realm:realm-gradle-plugin:10.16.1" + // android 资源混淆插件 + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.tencent.vasdolly:plugin:3.0.6' + + classpath "com.bytedance.android:aabresguard-plugin:0.1.10" + classpath "com.github.liujingxing:XmlClassGuard:1.2.6" + } +} + +allprojects { + repositories { + mavenCentral() + google() + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://jitpack.io' } + maven { url 'https://repo1.maven.org/maven2/' } + } + //网络慢的话就去 https://maven.aliyun.com/mvn/view 里面找个代理的仓库。 +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/channel.txt b/channel.txt new file mode 100644 index 0000000..1d5e7c6 --- /dev/null +++ b/channel.txt @@ -0,0 +1 @@ +official \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8137862 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,39 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4096M -Dkotlin.daemon.jvm.options\="-Xmx4096M" -XX\:MaxMetaspaceSize\=4096m + +org.gradle.daemon=true +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +android.useAndroidX=true +android.enableJetifier=true +android.enableResourceOptimizations=false +android.injected.testOnly=false +# mob +MobSDK.spEdition=FP + +minify_enabled=false + +# 是否隔离模式:换包名、换签名、隐藏部分SDK-KEY信息、去掉部分SDK(Google相关) +isolation_mode=true + +channel_file=channel.txt +CRASHLYTICS_COLLECTION_ENABLED=false + +COMPILE_SDK_VERSION=34 +MIN_SDK_VERSION=21 +TARGET_SDK_VERSION=34 + +version_name=1.0.26 +version_code=42 + +#systemProp.https.proxyHost=127.0.0.1 +#systemProp.https.proxyPort=7890 diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..2e57e88 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Mon Feb 26 14:07:21 CST 2024 +sdk.dir=/Users/zhanglaiman/Library/Android/sdk diff --git a/mode.json b/mode.json new file mode 100644 index 0000000..84aaa19 --- /dev/null +++ b/mode.json @@ -0,0 +1,79 @@ +{ + "code": 200, + "message": "success", + "data": { + "bigList": [], + "customChargeProd": [], + "defaultPay": 2, + "defaultPayH5": 2, + "list": [ + { + "chargeProdId": "coin_charge_01", + "prodName": "7000 Coins", + "money": 1.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 1, + "chargeGoldNum": 7000, + "currencySign": "USD", + "payChannel": "google_play_billing" + }, + { + "chargeProdId": "coin_charge_02", + "prodName": "70000 Coins", + "money": 10.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 2, + "chargeGoldNum": 70000, + "currencySign": "USD", + "payChannel": "google_play_billing" + }, + { + "chargeProdId": "coin_charge_03", + "prodName": "140000 Coins", + "money": 20.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 3, + "chargeGoldNum": 140000, + "currencySign": "USD", + "payChannel": "google_play_billing" + }, + { + "chargeProdId": "coin_charge_04", + "prodName": "350000 Coins", + "money": 50.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 4, + "chargeGoldNum": 350000, + "currencySign": "USD", + "payChannel": "google_play_billing" + }, + { + "chargeProdId": "coin_charge_05", + "prodName": "560000 Coins", + "money": 80.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 5, + "chargeGoldNum": 560000, + "currencySign": "USD", + "payChannel": "google_play_billing" + }, + { + "chargeProdId": "coin_charge_06", + "prodName": "700000 Coins", + "money": 100.0, + "giftGoldNum": 0, + "channel": 1, + "seqNo": 6, + "chargeGoldNum": 700000, + "currencySign": "USD", + "payChannel": "google_play_billing" + } + ] + }, + "timestamp": 1751363110053 +} \ No newline at end of file diff --git a/molistar.jks b/molistar.jks new file mode 100644 index 0000000000000000000000000000000000000000..9fde0636594817a182d7bc85bf63db4eff89a0fa GIT binary patch literal 2812 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 literal 0 HcmV?d00001 diff --git a/project.gradle b/project.gradle new file mode 100644 index 0000000..6255762 --- /dev/null +++ b/project.gradle @@ -0,0 +1,17 @@ +ext { + isolationMode = Boolean.valueOf(isolation_mode) + println("是否隔离模式:" + isolationMode) + + package_name = project.hasProperty('package_name') ? package_name + : (isolationMode ? 'com.example.gogo.none' : 'com.example.live') + println("applicationId is " + package_name) +} + +boolean isReleaseBuildType() { + for (String s : gradle.startParameter.taskNames) { + if (s.contains("Release") | s.contains("release")) { + return true + } + } + return false +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..abade91 --- /dev/null +++ b/readme.md @@ -0,0 +1,52 @@ +# 初始化项目 +命令行教学 +``` + git clone git@code.aliyun.com:qingxun/qx_android_client.git +``` + +# 拉取或推送 +拉取 +``` + git subtree pull --prefix={dirName} {repoUrl} {branchName} +``` +推送 +``` + git subtree push --prefix={dirName} {repoUrl} {branchName} +``` +>{dirName} 为 core 、library 或者 nim_uikit + +>{repoUrl} 为对应的子仓库地址 + +>{branchName} 为要拉取或者推送的分支名 + +## 指定渠道打包 +``` +java -jar packer-ng-2.0.0.jar generate --channels=yingyongbao --output=build/jiaku/apk --input=/Users/MadisonRong/dev/Android/erban-release/2.4.3/erban_client-release_aligned_signed-yingyongbao_243_jiagu_sign.apk +``` + +## python查看渠道信息 +支持查看单个apk文件或者文件夹下的所有apk文件。 +``` +python packer-ng-v2.py xxxx.apk +``` + +##应用框架 +- MVP(按照 刘镓旗的博客 的来封装) +- CoreEvent(yy的框架?有预料不到的bug) +- rxJava + +##sdk的应用场景 +- 声网(开闭麦),声网有关房间操作封装在RtcEngineManager +- 云信(朋友,粉丝,黑名单[房间,关系],队列,自定义消息,最近联系人,监听信息),云信有关房间的操作封装在IMNeteaseManager + +##功能 +- 礼物(GiftModel和GiftDialog包换获取数据和显示数据所有逻辑) +- 魔法(MagicModel,MagicDialog包换获取数据和显示数据所有逻辑) +- 座驾(商城,车库,CarActvity) +- 贵族(NobleUtil,NobleDataManager里面实现了贵族资源,权限的获取) +- 表情(FaceCoreImpl,下载和配置) +- 房间(房间设置,跳坑,上麦,下麦,锁坑,禁言,显示在MicroView和MicroViewAdapter,实现逻辑在AvRoomModel和AvRoomPresenter和ButtonItemFactory里面) +- 公屏(MessageView云信自定义消息的显示) +- 动画(帧动画[表情],属性动画[礼物],SVGA[座驾,房间背景,礼物特效,魔法]) +- 统计(未完善,入口StatisticManager) +- 云信自定义信息(CustomAttachment和CustomAttachParser) \ No newline at end of file diff --git a/screenMatch.properties b/screenMatch.properties new file mode 100644 index 0000000..16c3eb4 --- /dev/null +++ b/screenMatch.properties @@ -0,0 +1,54 @@ +############################################################################ +# Start with '#' is annotate. # +# In front of '=' is key, cannot be modified. # +# More information to visit: # +# http://blog.csdn.net/fesdgasdgasdg/article/details/52325590 # +# http://download.csdn.net/detail/fesdgasdgasdg/9913744 # +# https://github.com/mengzhinan/PhoneScreenMatch # +############################################################################ +# +# You need to refresh or reopen the project every time you modify the configuration, +# or you can't get the latest configuration parameters. +# +############################################################################# +# +# Base dp value for screen match. Cut the screen into [base_dp] parts. +# Data type is double. System default value is 360. +# I advise you not to modify the value, be careful !!!!!!!!! _^_ *_* +base_dp=360 +# Also need to match the phone screen of [match_dp]. +# If you have another dp values. +# System default values is 240,320,384,392,400,410,411,480,533,592,600,640,662,720,768,800,811,820,960,961,1024,1280,1365 +match_dp= +# If you not wanna to match dp values above. Write some above values here, append value with "," . +# For example: 811,961,1365 +ignore_dp= +# They're not android module name. If has more��split with , Symbol. +# If you set, it will not show in SelectDialog. +# If you have, write here and append value with "," . +# For example: testLibrary,commonModule +# System default values is .gradle, gradle, .idea, build, .git +ignore_module_name= +# Use which module under the values/dimen.xml file to do the base file, +# and generated dimen.xml file store in this module? +# Default value is 'app'. +match_module=app +# Don't show select dialog again when use this plugin. +# System screen match will use the last selected module name or default module name. +# You can give value true or false. Default value is false. +not_show_dialog=false +# Do you want to generate the default example dimens.xml file? +# In path of .../projectName/screenMatch_example_dimens.xml, It does not affect your project code. +# You can give value true or false. Default value is false. +not_create_default_dimens=false +# Does the font scale the same size as the DP? May not be accuracy. +# You can give value true or false. Default value is true. Also need scaled. +is_match_font_sp=true +# Do you want to create values-wXXXdp folder or values-swXXXdp folder ? +# I suggest you create values-swXXXdp folder, +# because I had a problem when I was working on the horizontal screen adapter. +# values-swXXXdp folder can solve my problem. +# If you want create values-swXXXdp folder, set "create_values_sw_folder=true", +# otherwise set "create_values_sw_folder=true". +# Default values is true. +create_values_sw_folder=true diff --git a/screenMatch_example_dimens.xml b/screenMatch_example_dimens.xml new file mode 100644 index 0000000..026f490 --- /dev/null +++ b/screenMatch_example_dimens.xml @@ -0,0 +1,431 @@ + + + + + @dimen/dp_15 + + + + -60dp + -30dp + -20dp + -12dp + -10dp + -8dp + -5dp + -2dp + -1dp + 0dp + 0.1dp + 0.5dp + 1dp + 1.5dp + 2dp + 2.5dp + 3dp + 3.5dp + 4dp + 4.5dp + 5dp + 6dp + 7dp + 7.5dp + 8dp + 9dp + 10dp + 11dp + 12dp + 13dp + 14dp + 15dp + 16dp + 17dp + 18dp + 19dp + 20dp + 21dp + 22dp + 23dp + 24dp + 25dp + 26dp + 27dp + 28dp + 29dp + 30dp + 31dp + 32dp + 33dp + 34dp + 35dp + 36dp + 37dp + 38dp + 39dp + 40dp + 41dp + 42dp + 43dp + 44dp + 45dp + 46dp + 47dp + 48dp + 49dp + 50dp + 51dp + 52dp + 53dp + 54dp + 55dp + 56dp + 57dp + 58dp + 59dp + 60dp + 61dp + 62dp + 63dp + 64dp + 65dp + 66dp + 67dp + 68dp + 69dp + 70dp + 71dp + 72dp + 73dp + 74dp + 75dp + 76dp + 77dp + 78dp + 79dp + 80dp + 81dp + 82dp + 83dp + 84dp + 85dp + 86dp + 87dp + 88dp + 89dp + 90dp + 91dp + 92dp + 93dp + 94dp + 95dp + 96dp + 97dp + 98dp + 99dp + 100dp + 101dp + 102dp + 103dp + 104dp + 104.5dp + 105dp + 106dp + 107dp + 108dp + 109dp + 110dp + 111dp + 112dp + 113dp + 114dp + 115dp + 116dp + 117dp + 118dp + 119dp + 120dp + 121dp + 122dp + 123dp + 124dp + 125dp + 126dp + 127dp + 128dp + 129dp + 130dp + 131dp + 132dp + 133dp + 134dp + 134.5dp + 135dp + 136dp + 137dp + 138dp + 139dp + 140dp + 141dp + 142dp + 143dp + 144dp + 145dp + 146dp + 147dp + 148dp + 149dp + 150dp + 151dp + 152dp + 153dp + 154dp + 155dp + 156dp + 157dp + 158dp + 159dp + 160dp + 161dp + 162dp + 163dp + 164dp + 165dp + 166dp + 167dp + 168dp + 169dp + 170dp + 171dp + 172dp + 173dp + 174dp + 175dp + 176dp + 177dp + 178dp + 179dp + 180dp + 181dp + 182dp + 183dp + 184dp + 185dp + 186dp + 187dp + 188dp + 189dp + 190dp + 191dp + 191.25dp + 192dp + 193dp + 194dp + 195dp + 196dp + 197dp + 198dp + 199dp + 200dp + 201dp + 202dp + 203dp + 204dp + 205dp + 206dp + 207dp + 208dp + 209dp + 210dp + 211dp + 212dp + 213dp + 214dp + 215dp + 216dp + 217dp + 218dp + 219dp + 220dp + 221dp + 222dp + 223dp + 224dp + 225dp + 226dp + 227dp + 228dp + 229dp + 230dp + 231dp + 232dp + 233dp + 234dp + 235dp + 236dp + 237dp + 238dp + 239dp + 240dp + 241dp + 242dp + 243dp + 244dp + 245dp + 246dp + 247dp + 248dp + 249dp + 250dp + 251dp + 252dp + 253dp + 254dp + 255dp + 256dp + 257dp + 258dp + 259dp + 260dp + 261dp + 262dp + 263dp + 264dp + 265dp + 266dp + 267dp + 268dp + 269dp + 270dp + 271dp + 272dp + 273dp + 274dp + 275dp + 276dp + 277dp + 278dp + 279dp + 280dp + 281dp + 282dp + 283dp + 284dp + 285dp + 286dp + 287dp + 288dp + 289dp + 290dp + 291dp + 292dp + 293dp + 294dp + 295dp + 296dp + 297dp + 298dp + 299dp + 300dp + 301dp + 302dp + 303dp + 304dp + 305dp + 306dp + 307dp + 308dp + 309dp + 310dp + 311dp + 312dp + 313dp + 314dp + 315dp + 316dp + 317dp + 318dp + 319dp + 320dp + 321dp + 322dp + 323dp + 324dp + 325dp + 326dp + 327dp + 328dp + 329dp + 330dp + 331dp + 332dp + 333dp + 334dp + 335dp + 336dp + 337dp + 338dp + 339dp + 340dp + 341dp + 342dp + 343dp + 344dp + 345dp + 346dp + 347dp + 348dp + 349dp + 350dp + 351dp + 352dp + 353dp + 354dp + 355dp + 356dp + 357dp + 358dp + 359dp + 360dp + 365dp + 370dp + 400dp + 410dp + 422dp + 472dp + 500dp + 600dp + 640dp + 720dp + + + 6sp + 7sp + 8sp + 9sp + 10sp + 11sp + 12sp + 13sp + 14sp + 15sp + 16sp + 17sp + 18sp + 19sp + 20sp + 21sp + 22sp + 23sp + 24sp + 25sp + 28sp + 30sp + 32sp + 34sp + 36sp + 38sp + 40sp + 42sp + 48sp + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..4164643 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,12 @@ +include ':app' +include ':core' +include ':library' +include ':nim_uikit' +include ':easyphotos' +if (file("./modules/module_google/build.gradle").exists()) { + include ':modules:module_google' +} +include ':modules:module_base' +include ':libs:lib_core' +include ':libs:lib_utils' +include ':libs:lib_encipher' diff --git a/方案.txt b/方案.txt new file mode 100644 index 0000000..327524d --- /dev/null +++ b/方案.txt @@ -0,0 +1,108 @@ +# 项目重构技术方案 + +## 一、目标 +- **语言迁移**:将原有 Java 代码逐步迁移到 Kotlin。 +- **架构升级**:采用 MVVM(Model-View-ViewModel)架构,提高可维护性和可测试性。 +- **现代化异步处理**:使用 Kotlin 协程和 Flow 替代 RxJava,简化异步编程模型。 +- **事件总线更换**:使用 LiveDataBus , 替换现EventBus , LiveDataBus具备生命周期管理,不再需要单独注册回收,可跨进程通讯,可发粘性事件 +- **RecyclerView Adapter 优化**: 引入 DiffUtil, 提升全局刷新的效率,避免不必要的消耗 +- **更换屏幕适配方案**: 使用头条的适配方案 AndroidAutoSize 替换现屏幕适配方案 +- **模块化设计**:按功能模块划分代码结构,增强模块解耦和复用能力。 +- **模块路由TheRouter**:货拉拉提供的模块路由方案,替换掉已经不再更新维护的ARouter +- **Glide**:升级版本,尝试兼容 GZip ,提升图片加载效率 +- **混淆方案**:ProGuard + aabResGuard + xmlClassGuard+包名隔离 +--- + +## 二、技术选型 + +### 1. 编程语言 +- **Kotlin**:全面使用 Kotlin 替代 Java,利用其简洁语法、空安全等特性。 + +### 2. 架构模式 +- **MVVM(Model-View-ViewModel)** + - **Model**:数据层,包含网络请求、本地存储、业务逻辑等。 + - **View**:UI 层,由 Activity 和 Fragment 实现。 + - **ViewModel**:连接 Model 和 View 的桥梁,持有 UI 数据并提供生命周期感知的数据绑定。 + +### 3. 异步处理 +- **Kotlin Coroutines + Flow**:替代 RxJava,简化异步操作和响应式编程。 +- **LiveData/StateFlow**:用于在 ViewModel 和 View 之间传递 UI 数据变化。 + +### 4. RecyclerView +- **融云Provider方案/BaseRecyclerViewAdapterHelper**:选一个替代 原有adapter +- **DiffUtil**:工具会自动计算,差异化对比后只刷新对应的item,大幅度提升效率 + +### 5. 网络请求 +- **Retrofit + OkHttp**:保留现有 Retrofit 网络框架,结合 Kotlin 协程进行优化。 +- **协程封装**:统一封装网络请求,简化调用方式。 + +### 6. 数据库与本地缓存 +- **Room**:本地持久化数据使用 Room 数据库。 +- **CacheManager**:统一的缓存策略封装。//考虑 + +### 7. 事件总线 +- **LiveDataBus** :替代 EventBus/RxBus,实现跨组件跨进程通信,事件生命周期管理,粘性事件收发。 + +### 8. 混淆 +- **ProGuard** :基础混淆,自定义规则在 proguard-rules.pro 中。 +- **aabResGuard** :资源文件名混淆保护,白名单机制保留关键资源,支持多语言保护 +- **xmlClassGuard** :布局文件类名混淆,目录结构混淆,生成映射文件 +- **包名隔离** :测试包跟正式包采用不同 包名 + +### 9. 工具库 +- **Glide**:图片加载。尝试兼容GZip,因 Glide还充当下载 svga,mp4,如果GZip不能兼容这两种情况,那就只好放弃 +- **TheRouter**:货拉拉团队开源的路由框架,使用ktps效率,编译效率远高于ARouter +- **uCrop**:裁剪图片框架,打算集成源码进项目,好应对各种需求 +- **LeakCanary**:侦测内存泄漏 +- **AndroidAutoSize**:头条适配方案 +- **XCrash**:拦截 java层崩溃,so层崩溃,ANR,OOM 等,输出日志到本地,方便开发调试 +- **饺子播放器/ExoPlayer**:优先ExoPlayer,谷歌推荐,且不加入FFmpeg扩展的情况下1MB左右 +.... +--- + +## 三、预计模块划分 + +| 模块名 | 功能说明 | +|--------|----------| +| [core] | 基础库,如网络、数据库、全局工具类等 | +| [common] | 公共库 | +| `app` | app层 | +| `login` | 登录注册相关逻辑 | +| `home` | 首页相关逻辑 | +| `dynamic` | 社区模块 | +| `game` | 游戏模块 | +| `message` | 云信模块| +| `mine` | 用户模块 | +| `room` | 房间模块 | +| `pay` | 支付相关模块 | +| `google` | 谷歌代码模块 | +| `develop` | 开发模块,存放一些不上线的代码,正式包不引入该模块 |(非必要) +| `library` | 工具库模块 | + +--- + + +--- + +## 四、迁移计划 + +| 阶段 | 内容 | +|------|------|----------| +| Phase 0 | 必要库,第三方引入,gradle升级,混淆方案出包,等兼容测试(优先保障混淆插件能运行,其他可做取舍,不断试错找到最优解) +| Phase 1 | 基础设施搭建,Kotlin 支持,协程封装,模块划分,启动页搭建,路由搭建... +| Phase 2 | 用户登录注册、社区模块,用户模块 +| Phase 3 | 云信模块 +| Phase 4 | 游戏模块 +| Phase 5 | 房间模块 +| Phase 6 | 支付模块 +| Phase 7 | 后续工作... +.... +--- + +## 注意事项 + +1. **保持兼容性**:新旧代码共存期间需做好桥接,避免破坏已有流程。 +2. **代码质量保障**: +3. **文档同步更新**:重构过程中同步更新文档。 + +---