Commit 4a0909ac authored by wanglei's avatar wanglei

Initial commit

parent 3ba89098
......@@ -4,11 +4,11 @@ plugins {
}
android {
namespace 'com.base.smartfilemanager'
namespace 'com.base.superfilemanager'
compileSdk 34
defaultConfig {
applicationId "com.base.smartfilemanager"
applicationId "com.base.superfilemanager"
minSdk 24
targetSdk 34
versionCode 1
......@@ -48,6 +48,11 @@ dependencies {
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
implementation("com.facebook.android:facebook-android-sdk:[8,9)")
implementation platform('com.google.firebase:firebase-bom:32.3.1')
implementation 'com.google.firebase:firebase-analytics:21.6.2'
implementation("com.google.firebase:firebase-messaging")
implementation("com.blankj:utilcodex:1.31.1")
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
......
{
"project_info": {
"project_number": "993178188325",
"project_id": "testcleanmaster",
"storage_bucket": "testcleanmaster.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:993178188325:android:a7986c2607f23024825dcc",
"android_client_info": {
"package_name": "com.base.superfilemanager"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyCY5uqNx8qS5ZPvSIknbh2nH2v5vjzCmPI"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
import json
import os
import random
import re
import shutil
from base64 import b64encode, b64decode
import cv2
import numpy as np
import regex as re2
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
import string
import random
def encrypt(content):
try:
iv = get_random_bytes(12)
cipher = AES.new(aesKey.encode('utf-8'), AES.MODE_GCM, nonce=iv)
encrypt_data, tag = cipher.encrypt_and_digest(content.encode('utf-8'))
result = iv + encrypt_data + tag
return b64encode(result).decode('utf-8')
except Exception as e:
print('error: ' + str(e))
return content
def decrypt(content):
try:
combined = b64decode(content.encode('utf-8'))
iv = combined[:12]
tag = combined[-16:]
encrypt_data = combined[12:-16]
cipher = AES.new(aesKey.encode('utf-8'), AES.MODE_GCM, nonce=iv)
decrypt_data = cipher.decrypt_and_verify(encrypt_data, tag)
return decrypt_data.decode('utf-8')
except (ValueError, KeyError) as e:
print(e)
return content
def get_classes_impl(result, class_type, path='.'):
listdir = os.listdir(path)
for i in listdir:
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
get_classes_impl(result, class_type, path_join)
elif os.path.isfile(path_join):
if os.path.splitext(i)[-1][1:] in class_type:
result.append(path_join)
def get_classes(class_type=None):
if class_type is None:
class_type = ["kt", "java"]
result = []
get_classes_impl(result, class_type)
return result
def find_text_impl(reg, result, skip_name, path, is_one):
listdir = os.listdir(path)
for i in listdir:
if is_one and len(result) > 0:
return
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
find_text_impl(reg, result, skip_name, path_join, is_one)
elif os.path.isfile(path_join):
if path.endswith('.') and i.split('.')[-1] in ['py']:
continue
is_skip = False
for j in skip_name:
if j in i:
is_skip = True
break
if is_skip:
continue
with open(path_join, 'r', encoding='utf-8') as f:
if not f.readable():
continue
try:
text = f.read()
except ValueError:
continue
findall = re.findall(reg, text)
if len(findall) > 0:
result.extend(findall)
def find_text(reg, path='.', is_one=False, skip_name=None):
if skip_name is None:
skip_name = []
result = []
if os.path.isdir(path):
find_text_impl(reg, result, skip_name, path, is_one)
else:
with open(path, 'r', encoding='utf-8') as f:
if f.readable():
try:
text = f.read()
findall = re.findall(reg, text)
if len(findall) > 0:
result.extend(findall)
except ValueError:
pass
if is_one:
if len(result) > 0:
return result[0]
else:
return None
return result
def find_file_impl(reg, result, skip_name, path='.'):
listdir = os.listdir(path)
for i in listdir:
if len(result) > 0:
return
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
find_file_impl(reg, result, skip_name, path_join)
elif os.path.isfile(path_join):
if path.endswith('.') and i not in ["pro"]:
continue
is_skip = False
for j in skip_name:
if j in i:
is_skip = True
break
if is_skip:
continue
with open(path_join, 'r', encoding='utf-8') as f:
if not f.readable():
continue
try:
text = f.read()
except ValueError:
continue
findall = re.findall(reg, text)
if len(findall) > 0:
result.append(path_join)
def find_file(reg, path='.', skip_name=None):
if skip_name is None:
skip_name = []
result = []
if os.path.isdir(path):
find_file_impl(reg, result, skip_name, path)
else:
with open(path, 'r', encoding='utf-8') as f:
if f.readable():
try:
text = f.read()
findall = re.findall(reg, text)
if len(findall) > 0:
result.append(path)
except ValueError:
pass
return result[0] if len(result) > 0 else ""
def get_class_path(path):
with open(path, 'r') as f:
text = f.read()
package = re.search('package (.*?)\\s', text).group(1)
name = os.path.basename(path)
name = name[:name.rfind('.')]
return package + '.' + name
def replace_map_text(mapping, path='.'):
listdir = os.listdir(path)
for i in listdir:
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
replace_map_text(mapping, path_join)
elif os.path.isfile(path_join):
if path.endswith('.') and os.path.splitext(i)[-1][1:] not in ["pro"]:
continue
with open(path_join, 'r', encoding='utf-8') as f:
if not f.readable():
continue
try:
text = f.read()
except ValueError:
continue
with open(path_join, 'w', encoding='utf-8') as f:
reverse = sorted(list(mapping.keys()), key=len, reverse=True)
for name in reverse:
text = text.replace(str(name), mapping[name])
f.write(text)
def sub_map_text(mapping, is_reverse=True, path='.', skip_type=None, skip_name=None):
if len(mapping) == 0:
return []
if skip_type is None:
skip_type = []
if skip_name is None:
skip_name = []
result = []
sub_map_text_impl(mapping, result, skip_type, skip_name, is_reverse, path)
return result
def sub_map_text_impl(mapping, result, skip_type, skip_name, is_reverse=True, path='.'):
listdir = os.listdir(path)
for i in listdir:
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
sub_map_text_impl(mapping, result, skip_type, skip_name, is_reverse, path_join)
elif os.path.isfile(path_join):
if path.endswith('.') and os.path.splitext(i)[-1][1:] not in ["pro"]:
continue
if os.path.splitext(i)[-1][1:] in skip_type:
continue
is_skip = False
for j in skip_name:
if j in i:
is_skip = True
break
if is_skip:
continue
with open(path_join, 'r', encoding='utf-8') as f:
if not f.readable():
continue
try:
text = f.read()
except ValueError:
continue
with open(path_join, 'w', encoding='utf-8') as f:
reverse = sorted(list(mapping.keys()), key=len, reverse=is_reverse)
text_result = text
for name in reverse:
text_result = re2.sub(str(name), mapping[name], text_result)
if text_result != text:
result.append(path_join)
f.write(text_result)
def replace_text(old, new, path='.'):
listdir = os.listdir(path)
for i in listdir:
path_join = os.path.join(path, i)
if os.path.isdir(path_join):
if path.endswith('.') and i in ["build", "debug", "release"]:
continue
if path.endswith('src') and i in ["androidTest", "test"]:
continue
replace_text(old, new, path_join)
elif os.path.isfile(path_join):
if path.endswith('.') and os.path.splitext(i)[-1][1:] not in ["pro"]:
continue
with open(path_join, 'r', encoding='utf-8') as f:
if not f.readable():
continue
try:
text = f.read()
except ValueError:
continue
with open(path_join, 'w', encoding='utf-8') as f:
f.write(text.replace(old, new))
def get_random_package():
packages = [get_random_string(4) for _ in range(get_random_int(1, 3))]
return '.'.join(packages)
def check_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_mapping = loads.get('dir', {})
packages = find_text('package (.*?)[;]?\\s', 'src')
packages.append(applicationId)
packages = sorted(set(packages), key=packages.index)
packages.sort(key=len)
mapping = {}
for i in packages:
if applicationId not in i:
continue
value = old_mapping.get(i)
if value is not None:
mapping[i] = value
continue
mapping[i] = get_random_package()
value = mapping.get(i[:i.rfind('.')])
if value is not None:
mapping[i] = value + '.' + mapping[i]
loads["dir"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_class_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_mapping = loads.get('class', {})
mapping = {}
classes = get_classes()
for i in classes:
if '_D' in i:
continue
name = os.path.basename(i)
name = name[:name.rfind('.')]
value = old_mapping.get(name)
if value is not None:
mapping[name] = value
continue
mapping[name] = get_title(get_random_string(4), True)
loads["class"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_type_ids_mapping(ids_type):
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_mapping = loads.get(ids_type + "_ids", {})
mapping = {}
type_ids = find_text('<' + ids_type + ' name="(.*?)".*?>', get_path('.\\src\\main\\res'))
for i in type_ids:
if i.endswith('_D'):
continue
value = old_mapping.get(i)
if value is not None:
mapping[i] = value
continue
mapping[i] = get_random_string(5, False)
type_ids = find_text('<item name="(.*?)".*? type="' + ids_type + '">', get_path('.\\src\\main\\res'))
for i in type_ids:
value = old_mapping.get(i)
if value is not None:
mapping[i] = value
continue
mapping[i] = get_random_string(5, False)
if len(mapping) != 0:
loads[ids_type + "_ids"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_styleable_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_mapping = loads.get("styleable", {})
mapping = {}
styleables = find_text('<declare-styleable name=".*?">[\\s\\S]*?</declare-styleable>')
for i in styleables:
styleable_id = re.findall('<declare-styleable name="(.*?)">', i)[0]
mapping[styleable_id] = old_mapping.get(styleable_id, get_random_string(5, False))
styleables_attr = re.findall('<attr name="(.*?)".*?/>', i)
for attr in styleables_attr:
mapping[attr + '_A'] = old_mapping.get(attr + '_A', get_random_string(5))
if len(mapping) != 0:
loads["styleable"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def deal_ids_type(ids_type):
type_ids_mapping = check_type_ids_mapping(ids_type)
if len(type_ids_mapping) == 0:
return
print(type_ids_mapping)
sub_map = {}
for i in type_ids_mapping:
sub_map['<' + ids_type + ' name="' + i + '"'] = '<' + ids_type + ' name="' + type_ids_mapping[i] + '"'
sub_map['<item name="' + i + '"(.*? type="' + ids_type + '")>'] = \
'<item name="' + type_ids_mapping[i] + '"\\g<1>>'
sub_map['(?<=[^\\.])R\\.' + ids_type + '\\.' + i + '(?=\\W)'] = 'R.' + ids_type + '.' + type_ids_mapping[i]
sub_map['(?<=>|")@' + ids_type + '/' + i + '(?=<|")'] = '@' + ids_type + '/' + type_ids_mapping[i]
sub_map['<' + ids_type + '(.*?) parent="' + i + '"(.*?)>'] = \
'<' + ids_type + '\\g<1> parent="' + type_ids_mapping[i] + '"\\g<2>>'
sub_map_text(sub_map)
def check_strings_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
mapping = loads.get('strings', {})
string_values = find_text('<string name="(.*?)".*?>(.*?)</string>', get_path('.\\src\\main\\res\\values'))
strings = []
for i in string_values:
if i[0].endswith('___') or i[0] in ['app_name', 'facebook_app_id']:
continue
if mapping.get(i[1]) is not None:
continue
strings.append(i[1])
for i in strings:
mapping[i] = encrypt(i)
loads["strings"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_view_ids_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
mapping = loads.get('view_ids', {})
view_ids = find_text('"\\@\\+id/(.*?)"', get_path('.\\src\\main\\res'))
for i in view_ids:
if i == 'root':
continue
key = get_title(i)
if mapping.get(key) is not None:
continue
mapping[key] = get_random_string(7)
loads["view_ids"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_layout_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
mapping = loads.get('layout', {})
layout_path = get_path('.\\src\\main\\res\\layout')
listdir = os.listdir(layout_path)
for i in listdir:
name = i[:i.rfind('.')]
if mapping.get(name) is not None:
continue
mapping[name] = get_random_string(6)
loads["layout"] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def check_res_mapping(res_type):
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
mapping = loads.get(res_type, {})
res_path = get_path('.\\src\\main\\res')
listdir = os.listdir(res_path)
for i in listdir:
if not i.startswith(res_type):
continue
for j in os.listdir(os.path.join(res_path, i)):
if j.startswith('book_'):
continue
if j.endswith('.9.png'):
name = j[:-len('.9.png')]
else:
name = j.rsplit('.', 1)[0]
if mapping.get(name) is not None:
continue
mapping[name] = get_random_string(8)
if len(mapping) != 0:
loads[res_type] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def get_random_string(length=8, is_ascii=True):
s = ''
for i in range(length):
if is_ascii:
s += chr(random.randint(0, 25) + ord('a'))
else:
s += chr(random.randrange(0x0780, 0x07A5 + 1))
return s
def get_random_int(a=0, b=10000):
return random.randint(a, b)
def get_dictionary_string(length=10):
random_str = random.choice(string.ascii_letters)
random_str += ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length - 1))
return random_str
def check_obfuscation_dictionary():
if not os.path.exists('dictionary.txt'):
dictionary = set()
while len(dictionary) != 5000:
dictionary.add(get_dictionary_string())
with open('dictionary.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(dictionary))
with open('proguard-rules.pro', 'r', encoding='utf-8') as f:
text = f.read()
if '-obfuscationdictionary' in text:
return
text += '\n-obfuscationdictionary dictionary.txt'
text += '\n-classobfuscationdictionary dictionary.txt'
text += '\n-packageobfuscationdictionary dictionary.txt'
with open('proguard-rules.pro', 'w', encoding='utf-8') as f:
f.write(text)
def mkdir(dir_path):
try:
os.makedirs(dir_path)
except OSError:
pass
def replace_click_method():
try:
path = get_path('.\\src\\main\\java')
file = find_file('fun View.setTrackedOnClickListener', path)
if file:
class_path = get_class_path(file) + '.setTrackedOnClickListener'
# java类不能直接使用扩展函数
sub_map_text({
'([.@])setOnClickListener': '\\g<1>setTrackedOnClickListener',
}, path=path, skip_type=['java'])
add_import(class_path, get_classes(['kt']))
except IndexError:
return
def deal_code():
# 替换点击事件
replace_click_method()
# 生成包映射文件
mapping = check_mapping()
packages = list(mapping.keys())
print(mapping)
# 移动文件
root_path = get_path('.\\src\\main\\java')
for key in packages:
key = str(key)
old_path = get_path(root_path + '\\' + key.replace('.', '\\'))
new_path = get_path(root_path + '\\' + mapping[key].replace('.', '\\'))
if not os.path.exists(old_path):
mkdir(old_path)
if not os.path.exists(new_path):
mkdir(new_path)
listdir = os.listdir(old_path)
for i in listdir:
path_join = os.path.join(old_path, i)
if os.path.isdir(path_join):
continue
shutil.move(path_join, os.path.join(new_path, i))
shutil.rmtree(get_path(root_path + '\\' + applicationId[:applicationId.find('.')]))
# 修改manifest
with open(get_path('.\\src\\main\\AndroidManifest.xml'), 'r', encoding='utf-8') as f:
text = f.read()
findall = re.findall('android:name=(".*?")', text)
for i in findall:
if i.startswith('".'):
text = text.replace(i, '"' + applicationId + i[1:-1] + '"')
with open(get_path('.\\src\\main\\AndroidManifest.xml'), 'w', encoding='utf-8') as f:
f.write(text)
# 修改依赖
sub_map = {}
for i in mapping:
sub_map['(?<=\\W)' + i + '(?=\\W)'] = mapping[i]
# 过滤修改
sub_map['(?<=\\W)' + mapping[applicationId] + '.R(?=\\W)'] = applicationId + '.R'
sub_map['(?<=\\W)' + mapping[applicationId] + '.databinding(?=\\W)'] = applicationId + '.databinding'
sub_map['(?<=\\W)' + mapping[applicationId] + '.BuildConfig(?=\\W)'] = applicationId + '.BuildConfig'
sub_map_text(sub_map)
# 根包名下 R 单独处理
new_path = get_path(root_path + '\\' + mapping[packages[0]].replace('.', '\\'))
listdir = os.listdir(new_path)
result_path = []
for i in listdir:
if os.path.isdir(os.path.join(new_path, i)):
continue
result_path.append(os.path.join(new_path, i))
add_import(applicationId + '.R', result_path)
add_import(applicationId + '.BuildConfig', result_path)
# 类名
class_mapping = check_class_mapping()
classes = get_classes()
print(class_mapping)
for i in classes:
if '_D' in i:
continue
name = os.path.basename(i)
name = name[:name.rfind('.')]
shutil.move(i, i.replace(name, class_mapping[name]))
sub_map = {}
for i in class_mapping:
sub_map['(?<=\\W)' + i + '(?=\\W)'] = class_mapping[i]
sub_map_text(sub_map)
# 混淆字典
check_obfuscation_dictionary()
def deal_res_type(res_type):
type_mapping = check_res_mapping(res_type)
if len(type_mapping) == 0:
return
print(type_mapping)
res_path = get_path('.\\src\\main\\res')
listdir = os.listdir(res_path)
for i in listdir:
if not i.startswith(res_type):
continue
path_join = os.path.join(res_path, i)
for j in os.listdir(path_join):
if j.startswith('book_'):
continue
if j.endswith('.9.png'):
name = j[:-len('.9.png')]
else:
name = j.rsplit('.', 1)[0]
path = os.path.join(path_join, j)
if '.DS_Store' not in path:
shutil.move(path, path.replace(name, type_mapping[name]))
sub_map = {}
for i in type_mapping:
sub_map['R\\.' + res_type + '\\.' + i + '(\\W)'] = 'R.' + res_type + '.' + type_mapping[i] + '\\g<1>'
sub_map['(>|")@' + res_type + '/' + i + '(<|")'] = '\\g<1>@' + res_type + '/' + type_mapping[i] + '\\g<2>'
sub_map_text(sub_map)
def add_image_noise(path):
try:
image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if path.endswith('.9.png'):
return
# 添加随机噪声
noise = np.random.randint(0, 2, image.shape, np.uint8)
image = cv2.add(image, noise)
new_path = path.rsplit('.', 1)[0] + '.webp'
cv2.imwrite(new_path, image, [cv2.IMWRITE_WEBP_QUALITY, 75])
if new_path != path:
os.remove(path)
except AttributeError:
return
def deal_image():
res_path = get_path('.\\src\\main\\res')
listdir = os.listdir(res_path)
for i in listdir:
path_join = os.path.join(res_path, i)
if os.path.isdir(path_join):
for j in os.listdir(path_join):
add_image_noise(os.path.join(path_join, j))
def get_title(string, is_all_upper=False):
splits = string.split('_')
s = ''
for i in range(len(splits)):
if i == 0 and not is_all_upper:
s = splits[i]
elif len(splits[i]) > 0:
s += splits[i][0].upper() + splits[i][1:]
return s
def add_import(path, listfile):
for i in listfile:
with open(i, 'r', encoding='utf-8') as f:
text = f.read()
if len(re.findall('import ' + path, text)) != 0:
continue
if i.endswith('kt'):
text = re.sub('(package \\S+?\\s+?)(?=\\S)', '\\g<1>import ' + path + '\n', text, flags=re.S)
elif i.endswith('java'):
text = re.sub('(package \\S+?\\s+?)(?=\\S)', '\\g<1>import ' + path + ';\n', text, flags=re.S)
with open(i, 'w', encoding='utf-8') as f:
f.write(text)
def check_class_string_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
mapping = loads.get('class_string', {})
classes = get_classes(['kt'])
for path in classes:
if os.path.basename(path).startswith('AESHelper'):
continue
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
text = re.sub('@SuppressLint.*?\n', '\n', text)
text = re.sub('//[^"]*?(("[^"]*?){2})*?\n', '\n', text)
text = re.sub(' +\n', '\n', text)
text = re.sub('=\\s+', '= ', text)
text = re.sub(':\\s+', ': ', text)
text = re.sub(',\\s+', ', ', text)
text = re.sub('\n{2,}', '\n\n', text)
result_text = text
strings = re.findall('".*?[^\\\\]"', re.sub(r'@[^)]*', lambda x: x.group().replace('"', "'"), result_text))
last_string = ''
for string in strings:
if len(string) <= 4 or string.isspace() or \
string[1:-1] in ['\\\\n', '\\\\r', '\\\\\'', '\\\\\\"', '\\\\?', '&amp;', 'UTF-8']:
continue
if string in last_string:
continue
start = text.find(string)
if start == -1:
continue
index = start + 1
sign_stack = ['"']
while len(sign_stack) != 0:
if text[index] == '\\':
index += 1
elif text[index] == '"' and sign_stack[-1] == '"':
sign_stack.pop()
elif text[index] == '"':
sign_stack.append('"')
elif text[index] == '}' and sign_stack[-1] == '${':
sign_stack.pop()
elif text[index - 1:index + 1] == '${':
sign_stack.append('${')
index += 1
string = text[start:index]
last_string = string
params = []
result = string
if '$' in string:
count = 0
last = -1
res = []
for i in range(len(string)):
if last == -1 and string[i] == '$':
if count == 0:
last = i
elif last != -1:
if string[i] == '{':
count += 1
elif string[i] == '}':
count -= 1
if count == 0:
res.append((last, i + 1))
last = -1
elif count == 0:
last = -1
for i in re.findall('\\$\\w*', string):
if i == '$':
continue
find = string.find(i)
res.append((find, find + len(i)))
res = sorted(res, key=lambda l: l[0])
last_res = None
for i in res:
if last_res is not None and i[0] < last_res[1]:
continue
params.append(string[i[0]:i[1]])
last_res = i
for i in params:
result = result.replace(i, "[str]")
result = result[1:-1].replace('%', '%%').replace('\\\\', '\\').replace('[str]', '%s')
if result == aesKey:
continue
if mapping.get(result) is not None:
continue
mapping[result] = get_random_string(5, False) + '_D'
loads['class_string'] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def deal_class_string():
# 获取映射
string_mapping = check_class_string_mapping()
print(string_mapping)
with open(get_path('.\\src\\main\\res\\values\\strings.xml'), 'r', encoding='utf-8') as f:
text = f.read()
rfind = text.rfind('\n')
text_result = text[:rfind]
for i in string_mapping:
string = i
string = string.replace('&', '&amp;')
string = string.replace('?', '\\?')
string = string.replace('\'', '\\\'')
text_result += '\n <string name="' + string_mapping[i] + '" translatable="false">' + string + '</string>'
text_result += text[rfind:]
with open(get_path('.\\src\\main\\res\\values\\strings.xml'), 'w', encoding='utf-8') as f:
f.write(text_result)
# 搜索 Int.string() 路径
file_path = find_file('fun Int.string', get_path('.\\src\\main\\java'))
class_path = get_class_path(file_path) + '.string' if file_path else ""
classes = get_classes(['kt'])
for path in classes:
# 跳过 AESHelper 类中的字符串,避免嵌套
if os.path.basename(path).startswith('AESHelper'):
continue
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
text = re.sub('@SuppressLint.*?\n', '\n', text)
text = re.sub('//[^"]*?(("[^"]*?){2})*?\n', '\n', text)
text = re.sub(' +\n', '\n', text)
text = re.sub('=\\s+', '= ', text)
text = re.sub(':\\s+', ': ', text)
text = re.sub(',\\s+', ', ', text)
text = re.sub('\n{2,}', '\n\n', text)
result_text = text
strings = re.findall('".*?[^\\\\]"', re.sub(r'@[^)]*', lambda x: x.group().replace('"', "'"), result_text))
last_string = ''
for string in strings:
if len(string) <= 4 or string.isspace() or \
string[1:-1] in ['\\\\n', '\\\\r', '\\\\\'', '\\\\\\"', '\\\\?', '&amp;', 'UTF-8']:
continue
if string in last_string:
continue
start = text.find(string)
if start == -1:
continue
index = start + 1
sign_stack = ['"']
while len(sign_stack) != 0:
if text[index] == '\\':
index += 1
elif text[index] == '"' and sign_stack[-1] == '"':
sign_stack.pop()
elif text[index] == '"':
sign_stack.append('"')
elif text[index] == '}' and sign_stack[-1] == '${':
sign_stack.pop()
elif text[index - 1:index + 1] == '${':
sign_stack.append('${')
index += 1
# 得到完整字符串
string = text[start:index]
last_string = string
# 参数
params = []
result = string
# 处理字符串拼接
if '$' in string:
count = 0
last = -1
res = []
for i in range(len(string)):
if last == -1 and string[i] == '$':
if count == 0:
last = i
elif last != -1:
if string[i] == '{':
count += 1
elif string[i] == '}':
count -= 1
if count == 0:
res.append((last, i + 1))
last = -1
elif count == 0:
last = -1
for i in re.findall('\\$\\w*', string):
if i == '$':
continue
find = string.find(i)
res.append((find, find + len(i)))
res = sorted(res, key=lambda l: l[0])
last_res = None
for i in res:
if last_res is not None and i[0] < last_res[1]:
continue
params.append(string[i[0]:i[1]])
last_res = i
for i in params:
result = result.replace(i, "[str]")
for i in range(len(params)):
if params[i][1] == '{':
params[i] = params[i][2:-1]
else:
params[i] = params[i][1:]
# 每个参数加上 toString
params[i] = '(' + params[i] + ').toString()'
# 拼接部分替换为 %s
result = result[1:-1].replace('%', '%%').replace('\\\\', '\\').replace('[str]', '%s')
# aesKey 密钥不加密
if result == aesKey:
continue
result_text = result_text.replace(text[start:index], 'R.string.' + string_mapping[result] +
'.string(' + ', '.join(params) + ')')
# 添加 Int.string() 方法引用
if class_path:
if len(re.findall('import ' + class_path, text)) == 0:
result_text = re.sub('(package \\S+?\\s+?)(?=\\S)',
'\\g<1>import ' + class_path + '\n', result_text, flags=re.S)
# 添加资源 R 引用
if len(re.findall('import ' + applicationId + '.R', text)) == 0:
result_text = re.sub('(package \\S+?\\s+?)(?=\\S)',
'\\g<1>import ' + applicationId + '.R' + '\n', result_text, flags=re.S)
# 去除 const
result_text = re.sub('const (val.*?R\\.string\\.\\S+?\\.string\\(\\))', '\\g<1>', result_text)
with open(path, 'w', encoding='utf-8') as f:
f.write(result_text)
def check_xml_string_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_mapping = loads.get('xml_string', {})
strings = find_text('android:text="(?<!@string/)([^@"]+?)"', get_path('.\\src\\main\\res\\layout'),
skip_name=['notify', 'notity'])
mapping = {}
for i in strings:
value = old_mapping.get(i)
if value is not None:
mapping[i] = value
continue
# 添加 '_D' 结尾,资源混淆时过滤不用再次改名
mapping[i] = get_random_string(5, False) + '_D'
loads['xml_string'] = mapping
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return mapping
def deal_xml_string():
# 获取映射
string_mapping = check_xml_string_mapping()
print(string_mapping)
with open(get_path('.\\src\\main\\res\\values\\strings.xml'), 'r', encoding='utf-8') as f:
text = f.read()
rfind = text.rfind('\n')
text_result = text[:rfind]
for i in string_mapping:
string = i
string = string.replace('&', '&amp;')
string = string.replace('?', '\\?')
string = string.replace('\'', '\\\'')
text_result += '\n <string name="' + string_mapping[i] + '" translatable="false">' + string + '</string>'
text_result += text[rfind:]
with open(get_path('.\\src\\main\\res\\values\\strings.xml'), 'w', encoding='utf-8') as f:
f.write(text_result)
sub_map = {}
for i in string_mapping:
sub_map['(?<=android:text=")' + re.escape(i) + '(?=")'] = '@string/' + re.escape(string_mapping[i])
# 替换时跳过通知布局
sub_map_text(sub_map, path=get_path('.\\src\\main\\res\\layout'), skip_name=['notify', 'notity'])
def deal_code_string():
# 处理布局文件中的明文字符串
deal_xml_string()
# 处理代码文件中的明文字符串
deal_class_string()
def deal_res():
# 添加字符串为 string 资源
deal_code_string()
# 替换 TextView 为 AESTextView
aes_text_view = get_class_path(find_file('class AESTextView', get_path('.\\src\\main\\java')))
sub_map = {
'(?<=<)TextView': aes_text_view,
'(?<=<)com.noober.background.view.BLTextView': aes_text_view
}
sub_map_text(sub_map, path=get_path('.\\src\\main\\res\\layout'), skip_name=['notify', 'notity'])
# 加密 string
strings_mapping = check_strings_mapping()
print(strings_mapping)
sub_map = {}
for i in strings_mapping:
sub_map['<string(.*?)>' + re.escape(i) + '</string>'] = \
'<string\\g<1>>' + re.escape(strings_mapping[i]) + '</string>'
sub_map_text(sub_map, path=get_path('.\\src\\main\\res\\values'))
# 改 string id
deal_ids_type('string')
# 改 color id
deal_ids_type('color')
# 改 dimen id
deal_ids_type('dimen')
# 改 style id
deal_ids_type('style')
# 改 declare-styleable
styleable_mapping = check_styleable_mapping()
print(styleable_mapping)
sub_map = {}
styleables = find_text('<declare-styleable name=".*?">[\\s\\S]*?</declare-styleable>')
for i in styleables:
result = i
styleable_id = re.findall('<declare-styleable name="(.*?)">', i)[0]
result = result.replace('<declare-styleable name="' + styleable_id + '">',
'<declare-styleable name="' + styleable_mapping[styleable_id] + '">')
sub_map['R\\.styleable\\.' + styleable_id + '(\\W)'] = \
'R.styleable.' + styleable_mapping[styleable_id] + '\\g<1>'
styleables_attr = re.findall('<attr name="(.*?)".*?/>', i)
for attr in styleables_attr:
result = result.replace('<attr name="' + attr + '"', '<attr name="' + styleable_mapping[attr + '_A'] + '"')
sub_map['R\\.styleable\\.' + styleable_id + '_' + attr + '(\\W)'] = \
'R.styleable.' + styleable_mapping[styleable_id] + '_' + styleable_mapping[attr + '_A'] + '\\g<1>'
sub_map['app:' + attr + '='] = 'app:' + styleable_mapping[attr + '_A'] + '='
sub_map[re.escape(i)] = result
sub_map_text(sub_map)
# 改 view id
view_ids = find_text('"\\@\\+id/(.*?)"', get_path('.\\src\\main\\res'))
view_ids_mapping = check_view_ids_mapping()
print(view_ids_mapping)
sub_map = {}
for i in view_ids:
if i == 'root':
continue
key = get_title(i)
sub_map['(>|")@(\\+)?id/' + i + '(<|")'] = '\\g<1>@\\g<2>id/' + view_ids_mapping[key] + '\\g<3>'
sub_map['R\\.id\\.' + i + '(\\W)'] = 'R.id.' + view_ids_mapping[key] + '\\g<1>'
sub_map['([bB]inding\\??(?:\\.\\w+)?\\??)\\.' + key + '(\\W)'] = '\\g<1>.' + view_ids_mapping[key] + '\\g<2>'
sub_map['(?<=app:constraint_referenced_ids=".*?)' + i + '(?=.*?")'] = view_ids_mapping[key]
sub_map_text(sub_map)
# 改 layout 文件名
layout_mapping = check_layout_mapping()
print(layout_mapping)
layout_path = get_path('.\\src\\main\\res\\layout')
listdir = os.listdir(layout_path)
for i in listdir:
name = i[:i.rfind('.')]
if layout_mapping.get(name) is None:
continue
path_join = os.path.join(layout_path, i)
shutil.move(path_join, path_join.replace(name, layout_mapping[name]))
sub_map = {}
for i in layout_mapping:
sub_map['R\\.layout\\.' + i + '(\\W)'] = 'R.layout.' + layout_mapping[i] + '\\g<1>'
# ViewBinding 同步修改
sub_map[get_title(i, True) + 'Binding'] = get_title(layout_mapping[i], True) + 'Binding'
sub_map['(>|")@layout/' + i + '(<|")'] = '\\g<1>@layout/' + layout_mapping[i] + '\\g<2>'
sub_map_text(sub_map)
# 改 drawable 文件名
deal_res_type('drawable')
# 改 mipmap 文件名
deal_res_type('mipmap')
# 改 raw 文件名
deal_res_type('raw')
# 改 raw 文件名
deal_res_type('xml')
# 改图片内容
deal_image()
def check_activity_mapping():
try:
loads = json.load(open('mapping.json', 'r', encoding='utf-8'))
except (ValueError, IOError):
loads = {}
old_list = loads.get('activity', [])
old_paths = []
for i in old_list:
old_paths.append(i[:i.rfind('.')])
old_paths = sorted(set(old_paths), key=old_paths.index)
old_paths.sort(key=len)
classes = get_classes()
paths = []
root_path = get_path('.\\src\\main\\java')
for i in classes:
paths.append(i[len(root_path) + 1:i.rfind(os.sep)].replace(os.sep, '.'))
paths = sorted(set(paths), key=paths.index)
paths.sort(key=len)
new_list = []
for i in old_list:
if i[:i.rfind('.')] in paths:
new_list.append(i)
for i in paths:
if i in old_paths:
continue
# 每个路径下产生随机个垃圾类
for _ in range(random.randint(0, 3)):
name = get_title(get_random_string(4), True) + '_D'
new_list.append(i + '.' + name)
loads["activity"] = new_list
json.dump(loads, open('mapping.json', 'w', encoding='utf-8'), indent=4)
return new_list
def add_junk_activity():
root_path = get_path('.\\src\\main\\java')
new_list = check_activity_mapping()
count = len(new_list)
random.shuffle(new_list)
for i in range(count):
class_path = get_path(root_path + '\\' + new_list[i].replace('.', '\\') + '.kt')
# 获取垃圾类代码
code = get_random_activity_code(new_list[i], new_list[(i + 1) % count])
with open(class_path, 'w', encoding='utf-8') as f:
f.write(code)
print(class_path)
with open(get_path('.\\src\\main\\AndroidManifest.xml'), 'r', encoding='utf-8') as f:
text = f.read()
replace_string = '\n'
for i in new_list:
replace_string += ' <activity android:name="' + i + '" />\n'
text = text.replace(' </application>', replace_string + ''' </application>''')
with open(get_path('.\\src\\main\\AndroidManifest.xml'), 'w', encoding='utf-8') as f:
f.write(text)
def get_random_view():
return random.choice(
['TextView', 'Button', 'EditText', 'ImageView', 'FrameLayout', 'LinearLayout', 'CalendarView', 'CheckBox',
'CheckedTextView', 'DatePicker', 'ExpandableListView', 'GridLayout', 'GridView', 'HorizontalScrollView',
'ImageButton', 'ListView', 'RelativeLayout', 'ScrollView', 'SearchView', 'SeekBar', 'TableLayout'])
def get_random_activity_code(class1, class2):
# 代码中,class1 跳转 class2,确保垃圾类被引用到
package1 = class1[:class1.rfind('.')]
name1 = class1[len(package1) + 1:]
package2 = class2[:class2.rfind('.')]
name2 = class2[len(package2) + 1:]
view = get_random_view()
if package1 == package2:
import_code = ''
else:
import_code = '\nimport ' + class2
code = '''package ''' + package1 + '''
import android.content.Intent
import android.os.Bundle
import android.widget.''' + view + '''
import androidx.appcompat.app.AppCompatActivity''' + import_code + '''
class ''' + name1 + ''' : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(''' + view + '''(this))
startActivity(Intent(this, ''' + name2 + '''::class.java))
}
}'''
return code
def add_junk_method():
classes = get_classes(['kt'])
for path in classes:
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
if text.find('data class') != -1:
continue
text = re.sub('@SuppressLint.*?\n', '\n', text)
text = re.sub('//[^"]*?(("[^"]*?){2})*?\n', '\n', text)
text = re.sub(' +\n', '\n', text)
text = re.sub('=\\s+', '= ', text)
text = re.sub(':\\s+', ': ', text)
text = re.sub(',\\s+', ', ', text)
text = re.sub('/\\*[\\s\\S]*?\\*/', '', text)
text = re.sub('\n{2,}', '\n\n', text)
result_text = text
interfaces = re.findall('interface .*?{.*?}', text, flags=re.S)
for interface in interfaces:
start = text.find(interface)
start = text[:start].rfind('\n') + 1
if start == -1:
continue
index = start
count = 0
isStart = False
while (not isStart or count > 0) and index < len(text):
if text[index] == '{':
count += 1
if count == 1:
isStart = True
elif text[index] == '}':
count -= 1
index += 1
# 过滤接口及其内容
text = text.replace(text[start:index], '')
functions = re.findall('fun (?!interface ).*?\\(.*?\\)', text, flags=re.S)
for function in functions:
start = text.find(function)
start = text[:start].rfind('\n') + 1
if start == -1:
continue
index = start
count = 0
isStart = False
while (not isStart or count > 0) and index < len(text):
if text[index] == '(':
count += 1
if count == 1:
isStart = True
elif text[index] == ')':
count -= 1
index += 1
if 'abstract' in text[start:index]:
continue
count = 0
isStart = False
param_end = index
while (not isStart or count > 0) and index < len(text):
if not isStart and text[index] == '=':
break
if text[index] == '{':
count += 1
if count == 1:
isStart = True
param_end = index
elif text[index] == '}':
count -= 1
index += 1
if not isStart:
continue
fun_text = text[start:index]
header = text[start:param_end]
param_start = start + header.find('(')
context = re.search('fun (.*?\\.)?\\w*?', text[start:param_start])[1]
if context is None:
context = ''
param_string = text[param_start:param_end]
if 'override fun ' in header and '\n ' + \
re.sub('(final )?override fun ', 'super.', header[:header.index(param_string)]) in fun_text:
methods = []
for i in range(random.randint(0, 5)):
result = add_one_method(fun_text, param_string, header, context)
if result is None or len(result) != 3:
continue
fun_text, method_text1, method_text2 = result
methods.insert(0, method_text2)
methods.insert(0, method_text1)
methods.insert(0, fun_text)
fun_text = '\n\n'.join(methods)
else:
methods = []
for i in range(random.randint(0, 5)):
result = add_one_method(fun_text, param_string, header, context)
if result is None or len(result) != 2:
continue
fun_text, method_text = result
methods.insert(0, method_text)
methods.insert(0, fun_text)
fun_text = '\n\n'.join(methods)
result_text = result_text.replace(text[start:index], fun_text)
with open(path, 'w', encoding='utf-8') as f:
f.write(result_text)
def add_one_method(fun_text, param_string, header, context):
if 'suspend ' in header:
suspend = 'suspend '
else:
suspend = ''
body = fun_text[len(header):]
intent = len(fun_text) - len(fun_text.lstrip())
param = param_string[1:param_string.rfind(')')]
param = re.sub('<.*?>', '', param)
param = re.sub('\\([^(]*?\\)', '()', param)
param = [i.strip().split(':')[0] for i in param.split(',')]
for i in range(len(param)):
if param[i].startswith('vararg'):
param[i] = '*' + param[i].split(' ')[-1]
elif param[i].startswith('@'):
param[i] = param[i].split(' ')[-1]
if 'override fun ' in header and '\n ' + \
re.sub('(final )?override fun ', 'super.', header[:header.index(param_string)]) in fun_text:
if ':' in param_string[param_string.rfind(')'):]:
return
method_split = [i.rstrip() for i in fun_text.split('\n')]
if method_split[0].rstrip()[-1] != '{' or method_split[-1].lstrip()[0] != '}':
return
method_body = method_split[1:-1]
super_index = -1
for i in range(len(method_body)):
if 'super.' in method_body[i]:
if super_index >= 0:
super_index = -1
break
super_index = i
if super_index < 0:
return
method_text1 = method_split.copy()
method_name1 = get_random_string(is_ascii=False)
method_text1[0] = method_text1[0].replace(header, ' ' * intent + 'private ' + suspend + 'fun ' +
context + method_name1 + param_string)
method_text1[1:-1] = method_body[:super_index]
method_text2 = method_split.copy()
method_name2 = get_random_string(is_ascii=False)
method_text2[0] = method_text2[0].replace(header, ' ' * intent + 'private ' + suspend + 'fun ' +
context + method_name2 + param_string)
method_text2[1:-1] = method_body[super_index + 1:]
method_body[super_index + 1:] = [' ' * (intent + 4) + method_name2 + '(' + ', '.join(param) + ')']
method_body[:super_index] = [' ' * (intent + 4) + method_name1 + '(' + ', '.join(param) + ')']
method_split[1:-1] = method_body
method_text1 = '\n'.join(method_text1)
method_text2 = '\n'.join(method_text2)
fun_text = '\n'.join(method_split)
return fun_text, method_text1, method_text2
else:
method_name = get_random_string(is_ascii=False)
method_text = fun_text
if ':' in param_string[param_string.rfind(')'):]:
method_text = method_text.replace(header, ' ' * intent + 'private ' + suspend + 'fun ' + context +
method_name + param_string)
fun_text = fun_text.replace(body, '{\n' + ' ' * (intent + 4) + 'return ' +
method_name + '(' + ', '.join(param) + ')\n' + ' ' * intent + '}')
else:
method_text = method_text.replace(header, ' ' * intent + 'private ' + suspend + 'fun ' + context +
method_name + param_string)
fun_text = fun_text.replace(body, '{\n' + ' ' * (intent + 4) +
method_name + '(' + ', '.join(param) + ')\n' + ' ' * intent + '}')
return fun_text, method_text
def add_junk_code():
classes = get_classes(['kt'])
for path in classes:
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
if text.find('data class') != -1:
continue
text = re.sub('@SuppressLint.*?\n', '\n', text)
text = re.sub('//[^"]*?(("[^"]*?){2})*?\n', '\n', text)
text = re.sub(' +\n', '\n', text)
text = re.sub('=\\s+', '= ', text)
text = re.sub(':\\s+', ': ', text)
text = re.sub(',\\s+', ', ', text)
text = re.sub('/\\*[\\s\\S]*?\\*/', '', text)
text = re.sub('\n{2,}', '\n\n', text)
functions = re.findall('((?=fun .*?\\([^}]*?\\)|init |val.*?by lazy)[^}]*?\\{)', text)
result_text = text
for function in functions:
start = text.find(function)
start = text[:start].rfind('\n') + 1
if start == -1:
continue
index = start
count = 0
isStart = False
while (not isStart or count > 0) and index < len(text):
if text[index] == '(':
count += 1
if count == 1:
isStart = True
elif text[index] == ')':
count -= 1
if not isStart and text[index] == '{':
break
index += 1
count = 0
isStart = False
while (not isStart or count > 0) and index < len(text):
if text[index] == '{':
count += 1
if count == 1:
isStart = True
elif text[index] == '}':
count -= 1
index += 1
fun_text = text[start:index]
fun_text = fun_text.split('\n')
intent_stack = []
last_intent = 0
log_indexes = []
skip_text = [False for _ in fun_text]
i = 0
while i < len(fun_text):
if 'object : ' in fun_text[i] and fun_text[i].rstrip().endswith('{'):
intent = len(fun_text[i]) - len(fun_text[i].lstrip())
while i < len(fun_text) - 1:
i += 1
skip_text[i] = True
if fun_text[i].lstrip().startswith('}') and \
len(fun_text[i]) - len(fun_text[i].lstrip()) == intent:
break
i += 1
for i in range(len(fun_text)):
if skip_text[i]:
continue
intent = len(fun_text[i]) - len(fun_text[i].lstrip())
if intent == 0 or intent % 4 != 0:
continue
if intent > last_intent and i > 0:
j = i - 1
while len(fun_text[j]) - len(fun_text[j].lstrip()) == 0 and j >= 0:
j -= 1
if j >= 0:
for k in range(int((intent - last_intent) / 4)):
intent_stack.append(j)
elif intent < last_intent:
for k in range(int((last_intent - intent) / 4)):
intent_stack.pop()
if len(intent_stack) > 0 and intent >= last_intent \
and len(fun_text[i].strip()) != 0 and fun_text[i].lstrip()[0] != ')':
last_text = fun_text[intent_stack[-1]]
if last_text[-1] != '{':
continue
if last_text.lstrip()[0] == ')':
last_text_intent = len(last_text) - len(last_text.lstrip())
j = intent_stack[-1] - 1
while j >= 0:
temp_text = fun_text[j]
if len(temp_text) - len(temp_text.lstrip()) == last_text_intent:
break
j -= 1
if j >= 0:
last_text = fun_text[j] + last_text.strip()
if 'when' not in last_text and '$' not in last_text and \
'object' not in last_text and 'class ' not in last_text and 'interface ' not in last_text:
log_indexes.append((i, intent))
last_intent = intent
log_indexes.reverse()
for i in log_indexes:
if random.random() > 0.35:
continue
junk_code = get_random_junk_code()
junk_code = junk_code.replace('\n', '\n' + ' ' * i[1])[1:]
fun_text.insert(i[0], junk_code)
result_text = result_text.replace(text[start:index], '\n'.join(fun_text))
with open(path, 'w', encoding='utf-8') as f:
f.write(result_text)
add_import('android.util.Log', classes)
def get_random_junk_code():
junk_code = random.choice(junk_codes)
count = 1
while True:
random_str = get_random_string(is_ascii=False)
replace = junk_code.replace('[str' + str(count) + ']', random_str)
if junk_code == replace:
break
junk_code = replace
count += 1
count = 1
while True:
random_int = get_random_int()
replace = junk_code.replace('[int' + str(count) + ']', str(random_int))
if junk_code == replace:
break
junk_code = replace
count += 1
return junk_code
junk_codes = [
'''
if (System.currentTimeMillis() < [int1]) {
println(System.currentTimeMillis())
}''',
'''
Log.d("[str1]", "[str1]")''',
'''
val [str1] = [int1]..[int1] + 10
println([str1].random())''',
'''
val [str1]: (Int, Int) -> Int = { [str2], [str3] -> [str2] + [str3] }
println([str1].invoke([int1], [int2]))''',
'''
class [str1](val [str2]: String, val [str3]: Int)
println([str1]("[str4]", [int1]))''',
'''
open class [str1] {
open fun [str2]() {}
}
class [str3] : [str1]() {
override fun [str2]() {
println("[str5]")
}
}
val [str4] = [str3]()
println([str4])''',
'''
fun String.[str1](): Boolean {
return this == this.reversed()
}
val [str2] = "[str4]"
val [str3] = [str2].[str1]()
println([str3])''',
'''
fun [str1]([str2]: Int, [str3]: Int): Int {
return [str2] + [str3]
}
println([str1]([int1], [int2]))''',
'''
val [str1] = { [str2]: String -> println([str2]) }
[str1]("[str3]!")''',
'''
fun [str1]([str2]: String) {
println("tag, $[str2]!")
}
[str1]("[str3]")''',
'''
fun [str1]() {
fun [str2]([str3]: Int) {
println([str3])
}
[str2]([int1])
[str2]([int2])
[str2]([int3])
}
[str1]()''',
'''
class [str1] {
inner class [str2] {
fun [str3]() {
println("[str5]")
}
}
}
val [str4] = [str1]().[str2]()
[str4].[str3]()''',
'''
class [str1] {
fun [str2]() {
println("[str3]")
}
}
[str1]().[str2]()''',
'''
abstract class [str1] {
abstract fun [str2]()
}
class [str3] : [str1]() {
override fun [str2]() {
println("[str5]")
}
}
val [str4] = [str3]()
[str4].[str2]()''',
'''
class [str1]<T>(val [str2]: T)
val [str3] = [str1]([int1])
val [str4] = [str1]("[str5]")
println([str3])
println([str4])''',
'''
fun <T> [str1]([str2]: T) {
println([str2])
}
[str1]([int1])
[str1]("[str3]")''',
'''
class [str1]<T, K>(val [str3]: T, val [str4]: K) {
init {
println("[str5]")
}
}
val [str2] = [str1]("[str6]", [int1])
println([str2])''',
'''
class [str1]<T>(val [str2]: T, val [str3]: Int) {
constructor([str4]: T) : this([str4], [int1]) {
println("[str5]")
}
}
println([str1]("[str6]"))''',
'''
val [str1] = [int1]
val [str2] = [int2]
val [str3] = [str1] * [str2] * [str2]
println([str3])''',
'''
fun [str1]([str3]: Int, [str4]: Int): Int {
return [str3] + [str4]
}
val [str2] = [str1]([int1], [int2])
println([str2])''',
'''
fun [str1]([str4]: Int, [str5]: Int): Int {
fun [str2]([str6]: Int, [str7]: Int): Int {
return [str6] + [str7]
}
return [str2]([str4], [str5])
}
val [str3] = [str1]([int1], [int2])
println([str3])''',
'''
class [str1] {
val [str2] by lazy {
println("[str4]")
"[str5]!"
}
}
val [str3] = [str1]()
println([str3].[str2])''',
'''
class [str1]<T>([str3]: T) {
init {
println("[str4]: $[str3]")
}
}
println([str1]("[str2]"))''',
'''
class [str1]<T>(val [str3]: T, val [str4]: Int) {
constructor([str3]: T) : this([str3], [int1]) {
println("[str4]")
}
}
println([str1]("[str2]"))''',
'''
class [str1]<T>(val [str4]: T)
val [str2] = [str1]([int1])
val [str3] = [str1]("[str5]")
println([str2].[str4])
println([str3].[str4])'''
]
def class_disorder():
classes = get_classes(['kt'])
for path in classes:
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
if text.find('data class') != -1:
continue
text = re.sub('@SuppressLint.*?\n', '\n', text)
text = re.sub('//[^"]*?(("[^"]*?){2})*?\n', '\n', text)
text = re.sub(' +\n', '\n', text)
text = re.sub('=\\s+', '= ', text)
text = re.sub(':\\s+', ': ', text)
text = re.sub(',\\s+', ', ', text)
text = re.sub('/\\*[\\s\\S]*?\\*/', '', text)
text = re.sub('\n{2,}', '\n\n', text)
lines = text.split('\n')
count = 0
begin = 0
result = []
start = -1
end = 0
for index in range(len(lines)):
if len(lines[index].strip()) == 0:
continue
if start == -1:
if lines[index][-1] == '{':
start = index
count += 1
continue
for char in lines[index]:
if char in ['{', '(']:
count += 1
elif char in ['}', ')']:
if count == 1:
end = index
count -= 1
if count == 1:
if lines[index][-1] in ['}', ')'] and begin != 0:
if 'get()' in lines[begin] or 'set(value)' in lines[begin]:
for i in range(len(result)):
if result[i][1] == begin:
result[i] = (result[i][0], index + 1)
break
else:
result.append((begin, index + 1))
begin = 0
elif lines[index][-1] not in ['{', '(']:
strip = lines[index].lstrip()
if strip[0] == '.' or strip.startswith('get() =') or strip.startswith('set(value) = '):
for i in range(len(result)):
if result[i][1] == index:
result[i] = (result[i][0], index + 1)
break
else:
result.append((index, index + 1))
elif count == 2 and (lines[index][-1] in ['{', '('] or lines[index].endswith('->')) \
and lines[begin][-1] not in ['{', '(']:
begin = index
result_temp = []
i = 0
while i < len(result):
begin = result[i][0]
while lines[result[i][0]].lstrip().startswith('@'):
i += 1
result_temp.append([begin, result[i][1]])
i += 1
result = result_temp
random.shuffle(result)
result_sort = result.copy()
var_weight = {}
for i in re.findall('va[lr] (\\w+)', text):
var_weight[i] = len(re.findall('\\W' + re.escape(i) + '\\W', text))
for var in sorted(var_weight, key=lambda k: var_weight[k]):
for i in range(len(result)):
findall = re.findall('va[lr] (' + re.escape(var) + ')', lines[result[i][0]])
if len(findall) == 0:
continue
result_sort.remove(result[i])
result_sort.insert(0, result[i])
break
result = result_sort
text = '\n'.join(lines[:start + 1]) + '\n'
for i in result:
text += '\n' + '\n'.join(lines[i[0]:i[1]]) + '\n'
text += '\n'.join(lines[end:])
with open(path, 'w', encoding='utf-8') as f:
f.write(text)
def deal_junk():
# 添加垃圾类
add_junk_activity()
# 添加方法嵌套
add_junk_method()
# 添加垃圾代码
add_junk_code()
# 类成员乱序
class_disorder()
def get_path(path):
paths = path.split('\\')
return str(os.path.join(*paths))
def main():
if not os.path.exists(get_path('.\\src\\main\\java\\' + applicationId[:applicationId.find('.')])):
return
# 资源混淆
deal_res()
# 垃圾代码
deal_junk()
# 代码处理
deal_code()
if __name__ == '__main__':
if os.path.exists('build.gradle'):
gradle_path = 'build.gradle'
elif os.path.exists('build.gradle.kts'):
gradle_path = 'build.gradle.kts'
else:
exit('找不到 build.gradle 文件')
applicationId = re.search('applicationId .*?["\'](.*?)["\']', open(gradle_path, 'r', encoding='utf-8').read())[1]
aesKey = find_text(' aesKey ?= ?"(.*?)"', get_path('.\\src\\main\\java'))[0]
print('包名: ' + applicationId, 'aesKey: ' + aesKey)
if find_text('release ?{\\s*?(?:isM|m)inifyEnabled(?: ?= ?| )true', is_one=True) is None:
exit('官方混淆未开启')
if find_text('\\s*?ndk ?{', is_one=True) is None:
exit('ndk未配置')
main()
package com.base.smartfilemanager
package com.base.superfilemanager
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
......
......@@ -44,6 +44,10 @@
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
</application>
</manifest>
\ No newline at end of file
package com.base.smartfilemanager
package com.base.superfilemanager
import android.app.Application
......
package com.base.smartfilemanager
package com.base.superfilemanager
class MyApplication : BaseApplication() {
override fun init() {
......
package com.base.smartfilemanager.activity
package com.base.superfilemanager.activity
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import com.base.smartfilemanager.R
import com.base.smartfilemanager.adapter.FileBrowseAdapter
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.APK
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Audio
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.DOC
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Image
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.PDF
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.PPT
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.TXT
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Video
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.XLS
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.ZIP
import com.base.smartfilemanager.databinding.ActivityFileCategoryBinding
import com.base.smartfilemanager.helps.BaseActivity
import com.base.smartfilemanager.helps.file.FileEx.deleteDirectory
import com.base.smartfilemanager.helps.file.FileHelps
import com.base.smartfilemanager.view.FileDeleteDialog
import com.base.smartfilemanager.view.FileDeleteDialog.showFileDeleteDialog
import com.base.superfilemanager.adapter.FileBrowseAdapter
import com.base.superfilemanager.bean.FileBean
import com.base.superfilemanager.bean.FileCategoryBean
import com.base.superfilemanager.bean.FileCategoryBean.Companion.APK
import com.base.superfilemanager.bean.FileCategoryBean.Companion.Audio
import com.base.superfilemanager.bean.FileCategoryBean.Companion.DOC
import com.base.superfilemanager.bean.FileCategoryBean.Companion.Image
import com.base.superfilemanager.bean.FileCategoryBean.Companion.PDF
import com.base.superfilemanager.bean.FileCategoryBean.Companion.PPT
import com.base.superfilemanager.bean.FileCategoryBean.Companion.TXT
import com.base.superfilemanager.bean.FileCategoryBean.Companion.Video
import com.base.superfilemanager.bean.FileCategoryBean.Companion.XLS
import com.base.superfilemanager.bean.FileCategoryBean.Companion.ZIP
import com.base.superfilemanager.databinding.ActivityFileCategoryBinding
import com.base.superfilemanager.helps.BaseActivity
import com.base.superfilemanager.helps.file.FileEx.deleteDirectory
import com.base.superfilemanager.helps.file.FileHelps
import com.base.superfilemanager.view.FileDeleteDialog.showFileDeleteDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
......
package com.base.smartfilemanager.activity
package com.base.superfilemanager.activity
import android.graphics.Typeface
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.smartfilemanager.fragment.FileCategoryFragment
import com.base.smartfilemanager.databinding.ActivityMainBinding
import com.base.smartfilemanager.helps.BaseActivity
import com.base.superfilemanager.fragment.FileCategoryFragment
import com.base.superfilemanager.databinding.ActivityMainBinding
import com.base.superfilemanager.helps.BaseActivity
class MainActivity : BaseActivity<ActivityMainBinding>() {
......
package com.base.superfilemanager.activity
import android.annotation.SuppressLint
import android.graphics.Color
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.view.updatePadding
import com.base.superfilemanager.databinding.ActivityLayoutWebPrivacyBinding
import com.base.superfilemanager.helps.BaseActivity
import com.blankj.utilcode.util.BarUtils
class PrivacyWebActivity : BaseActivity<ActivityLayoutWebPrivacyBinding>() {
override val binding: ActivityLayoutWebPrivacyBinding by lazy {
ActivityLayoutWebPrivacyBinding.inflate(layoutInflater)
}
@SuppressLint("SetJavaScriptEnabled")
override fun initView() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
binding.root.updatePadding(top = BarUtils.getStatusBarHeight())
binding.idWeb.settings.setJavaScriptEnabled(true)
binding.idWeb.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
view?.loadUrl(url!!)
return true
}
}
binding.idWeb.loadUrl("")
binding.idBack.setOnClickListener {
finishToMain()
}
}
}
\ No newline at end of file
package com.base.superfilemanager.activity.splash
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.text.SpannableString
import android.text.Spanned
import android.text.style.UnderlineSpan
import androidx.core.view.isVisible
import com.base.superfilemanager.databinding.ActivitySplashBinding
import com.base.superfilemanager.helps.ConfigHelper
class PrivacyAgreementManager {
private val binding: ActivitySplashBinding
private val context: Activity
private val listener: onUserPrivacyAggreementListener
constructor(binding: ActivitySplashBinding, context: Activity, listener: onUserPrivacyAggreementListener) {
this.binding = binding
this.context = context
this.listener = listener
initView()
}
private fun initView() {
val spannableString = SpannableString("Privacy Policy")
spannableString.setSpan(
UnderlineSpan(),
0,
spannableString.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
binding.idTvPrivacyPolicy.text = spannableString
binding.idTvPrivacyPolicy.setOnClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://sites.google.com/view/esuper-file-manager/home")
)
context.startActivity(intent)
}
binding.idTvStart.setOnClickListener {
binding.idTvStart.isVisible = false
binding.idLlJindu.isVisible = true
binding.idLlYinsi.isVisible = false
ConfigHelper.ifAgreePrivacy = true
// EventHelper.event("click_start_to_use")
// EventHelper.event("page_${javaClass.simpleName}")
listener.onAgreePrivacy()
}
}
interface onUserPrivacyAggreementListener {
fun onAgreePrivacy()
}
}
\ No newline at end of file
package com.base.smartfilemanager.activity.splash
package com.base.superfilemanager.activity.splash
import android.os.Handler
import com.base.smartfilemanager.databinding.ActivitySplashBinding
import com.base.superfilemanager.databinding.ActivitySplashBinding
import com.blankj.utilcode.util.SPUtils
class ProgressManager {
......
package com.base.smartfilemanager.activity.splash
package com.base.superfilemanager.activity.splash
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.base.smartfilemanager.R
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.databinding.ActivitySplashBinding
import com.base.smartfilemanager.helps.BaseActivity
import androidx.core.view.isVisible
import com.base.superfilemanager.activity.MainActivity
import com.base.superfilemanager.databinding.ActivitySplashBinding
import com.base.superfilemanager.helps.BaseActivity
import com.base.superfilemanager.helps.ConfigHelper
import com.blankj.utilcode.util.BarUtils
@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(), ProgressManager.ProgressListener {
class SplashActivity : BaseActivity<ActivitySplashBinding>(), ProgressManager.ProgressListener,
PrivacyAgreementManager.onUserPrivacyAggreementListener {
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
......@@ -43,7 +40,14 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(), ProgressManager.Pr
return
}
mProgressManager = ProgressManager(binding, this)
mProgressManager?.startProgress()
if (ConfigHelper.ifAgreePrivacy) {
binding.idTvStart.isVisible = false
binding.idLlJindu.isVisible = true
binding.idLlYinsi.isVisible = false
onAgreePrivacy()
} else {
PrivacyAgreementManager(binding, this, this)
}
}
override fun onProgressMax() {
......@@ -54,4 +58,9 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(), ProgressManager.Pr
}
override fun onAgreePrivacy() {
mProgressManager?.startProgress()
}
}
\ No newline at end of file
package com.base.smartfilemanager.adapter
package com.base.superfilemanager.adapter
import android.annotation.SuppressLint
import android.content.Context
......@@ -7,10 +7,10 @@ import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.smartfilemanager.R
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.databinding.ItemFileListBinding
import com.base.smartfilemanager.view.XmlEx.inflate
import com.base.superfilemanager.R
import com.base.superfilemanager.bean.FileBean
import com.base.superfilemanager.databinding.ItemFileListBinding
import com.base.superfilemanager.view.XmlEx.inflate
import com.bumptech.glide.Glide
class FileBrowseAdapter(private val select: (size: Int) -> Unit) : RecyclerView.Adapter<FileBrowseAdapter.FB>() {
......
package com.base.smartfilemanager.adapter
package com.base.superfilemanager.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.smartfilemanager.R
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.databinding.ItemFileCategoryDocBinding
import com.base.smartfilemanager.databinding.ItemFileCategoryMediaBinding
import com.base.smartfilemanager.databinding.ItemRecentMediaBinding
import com.base.smartfilemanager.view.XmlEx.inflate
import com.base.superfilemanager.R
import com.base.superfilemanager.bean.FileCategoryBean
import com.base.superfilemanager.databinding.ItemFileCategoryDocBinding
import com.base.superfilemanager.databinding.ItemFileCategoryMediaBinding
import com.base.superfilemanager.databinding.ItemRecentMediaBinding
import com.base.superfilemanager.view.XmlEx.inflate
import com.bumptech.glide.Glide
class FileCategoryAdapter(val mode: String, val click: (key: String) -> Unit) : RecyclerView.Adapter<FileCategoryAdapter.DC>() {
......
package com.base.smartfilemanager.bean
package com.base.superfilemanager.bean
data class FileBean(
var name: String = "",
......
package com.base.smartfilemanager.bean
package com.base.superfilemanager.bean
data class FileCategoryBean(
val key: String="",
......
package com.base.smartfilemanager.bean
package com.base.superfilemanager.bean
data class ImageDataBean(
val path: String,
......
package com.base.smartfilemanager.fragment
package com.base.superfilemanager.fragment
import android.content.Intent
import android.os.Build
......@@ -6,14 +6,14 @@ import android.os.Environment
import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.base.smartfilemanager.R
import com.base.smartfilemanager.activity.FileCategoryActivity
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.adapter.FileCategoryAdapter
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.databinding.FragmentFileCategoryBinding
import com.base.smartfilemanager.helps.BaseFragment
import com.base.smartfilemanager.helps.file.MediaStoreEx
import com.base.superfilemanager.R
import com.base.superfilemanager.activity.FileCategoryActivity
import com.base.superfilemanager.activity.MainActivity
import com.base.superfilemanager.adapter.FileCategoryAdapter
import com.base.superfilemanager.bean.FileCategoryBean
import com.base.superfilemanager.databinding.FragmentFileCategoryBinding
import com.base.superfilemanager.helps.BaseFragment
import com.base.superfilemanager.helps.file.MediaStoreEx
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.PermissionUtils
import kotlinx.coroutines.Dispatchers
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.util.Base64
import java.security.SecureRandom
......@@ -8,7 +8,7 @@ import javax.crypto.spec.SecretKeySpec
object AESHelper {
private const val aesKey = "nbutdub5lsfdkitt"
private const val aesKey = "tqdd8p5oplj9vj3y"
private val cipher by lazy {
Cipher.getInstance("AES/GCM/NoPadding")
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.content.Context
import android.util.AttributeSet
import com.base.smartfilemanager.helps.KotlinExt.decode
import com.base.superfilemanager.helps.KotlinExt.decode
import com.noober.background.view.BLTextView
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.app.AppOpsManager
import android.app.Dialog
......@@ -12,8 +12,8 @@ import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.view.DialogViews
import com.base.superfilemanager.activity.MainActivity
import com.base.superfilemanager.view.DialogViews
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.BarUtils
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.os.Bundle
import android.view.LayoutInflater
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.SPUtils
......@@ -11,8 +11,8 @@ object ConfigHelper {
// 域名
const val eventUrl = "https://rp.cansole764cansole.xyz"
const val apiUrl = "https://api.cansole764cansole.xyz"
const val eventUrl = "https://rp.denisjodion560.xyz"
const val apiUrl = "https://api.denisjodion560.xyz"
// admob广告id
const val openAdmobId = "/6499/example/app-open"
......@@ -20,7 +20,7 @@ object ConfigHelper {
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696110"
// 正式包名
const val packageName = "com.kk.cleanmaster.file.cleanmaster.master"
const val packageName = "com.kkzxaakk.filemanagerr.abcd"
val noLoadingActivities = listOf(
"full", // 过滤全屏广告
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.os.Build
import android.util.Log
import com.base.smartfilemanager.helps.ConfigHelper.ifAgreePrivacy
import com.base.superfilemanager.helps.ConfigHelper.ifAgreePrivacy
import com.blankj.utilcode.BuildConfig
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
......@@ -39,6 +39,7 @@ object EventHelper {
ext: JSONObject? = null,
isSingleEvent: Boolean = false
) {
return
if (!ifAgreePrivacy) {
Log.e(TAG, "ifAgreePrivacy=$ifAgreePrivacy")
return
......
package com.base.smartfilemanager.helps
package com.base.superfilemanager.helps
import android.view.View
import com.base.smartfilemanager.BaseApplication
import com.base.superfilemanager.BaseApplication
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Locale
......
package com.base.smartfilemanager.helps.file
package com.base.superfilemanager.helps.file
import java.io.File
import java.util.Stack
......
package com.base.smartfilemanager.helps.file
package com.base.superfilemanager.helps.file
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import com.base.smartfilemanager.BaseApplication
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.bean.ImageDataBean
import com.base.superfilemanager.BaseApplication
import com.base.superfilemanager.bean.FileBean
import com.base.superfilemanager.bean.ImageDataBean
import com.blankj.utilcode.util.FileUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
......
package com.base.smartfilemanager.helps.file
package com.base.superfilemanager.helps.file
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.base.smartfilemanager.bean.ImageDataBean
import com.base.superfilemanager.bean.ImageDataBean
import com.blankj.utilcode.util.ScreenUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
......
package com.base.smartfilemanager.helps.file
package com.base.superfilemanager.helps.file
import android.content.ContentUris
import android.content.Context
......
package com.base.smartfilemanager.view
package com.base.superfilemanager.view
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import com.base.smartfilemanager.R
import com.base.smartfilemanager.databinding.DialogPermissonOpenBinding
import com.base.superfilemanager.R
import com.base.superfilemanager.databinding.DialogPermissonOpenBinding
import com.blankj.utilcode.util.SpanUtils
object DialogViews {
......
package com.base.smartfilemanager.view
package com.base.superfilemanager.view
import android.content.Context
import android.text.SpannableString
......@@ -8,8 +8,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.base.smartfilemanager.R
import com.base.smartfilemanager.databinding.DialogFileDeleteBinding
import com.base.superfilemanager.R
import com.base.superfilemanager.databinding.DialogFileDeleteBinding
object FileDeleteDialog {
......
package com.base.smartfilemanager.view
package com.base.superfilemanager.view
import android.view.LayoutInflater
import android.view.View
......
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white"/>
<item android:drawable="@drawable/splash_bg" />
<item android:drawable="@color/white" />
<!-- <item android:drawable="@drawable/splash_bg" />-->
<item
android:top="130dp"
android:gravity="top|center_horizontal">
<bitmap
android:src="@mipmap/icon_text" />
android:gravity="top|center_horizontal"
android:top="130dp">
<bitmap android:src="@drawable/qdylogo" />
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="14dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/id_back"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_centerVertical="true"
android:paddingHorizontal="12dp"
android:src="@drawable/fanhui"
android:tint="#333333" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Privacy Policy"
android:textColor="#333333"
android:textSize="17sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</RelativeLayout>
<WebView
android:id="@+id/id_web"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="14dp"
android:layout_weight="1"
android:padding="10dp" />
</LinearLayout>
\ No newline at end of file
......@@ -3,9 +3,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:background="@drawable/splash_bp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/splash_bp"
android:orientation="vertical"
tools:context=".activity.splash.SplashActivity">
......@@ -26,7 +26,20 @@
android:layout_height="wrap_content"
android:layout_marginBottom="49dp"
android:gravity="center_horizontal"
android:orientation="vertical">
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/tv_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Loading..."
android:textColor="#333333"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<ProgressBar
android:id="@+id/pb"
......@@ -37,17 +50,57 @@
android:progressDrawable="@drawable/shape_splash_s" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/id_ll_yinsi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:text="By continuing you are agreeing to the"
android:textColor="#676767"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:text="Loading..."
android:textColor="#000000"
android:textSize="15sp"
android:text=" &amp; "
android:visibility="gone"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/id_tv_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Privacy Policy"
android:textColor="#676767"
android:textSize="14sp"
tools:ignore="HardcodedText" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.noober.background.view.BLTextView
android:id="@+id/id_tv_start"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="40dp"
android:layout_marginBottom="49dp"
android:gravity="center"
android:text="START TO USE"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="visible"
app:bl_corners_radius="18dp"
app:bl_solid_color="#027CF6"
tools:ignore="HardcodedText" />
</LinearLayout>
\ No newline at end of file
......@@ -2,4 +2,5 @@
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="app_name">Super File Manager</string>
<string name="facebook_app_id" translatable="false">486434960399485</string>
</resources>
\ No newline at end of file
......@@ -24,5 +24,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "smart-file-manager"
rootProject.name = "super-file-manager"
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment