init
|
@ -0,0 +1,16 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/.idea
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,43 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.bjlx.armcloudaf'
|
||||
compileSdk 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.bjlx.armcloudaf"
|
||||
minSdk 26
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation libs.appcompat
|
||||
implementation libs.material
|
||||
implementation libs.activity
|
||||
implementation libs.constraintlayout
|
||||
testImplementation libs.junit
|
||||
androidTestImplementation libs.ext.junit
|
||||
androidTestImplementation libs.espresso.core
|
||||
implementation("net.armcloud.xscore:xscore:1.0.0")
|
||||
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
|
||||
implementation 'com.alibaba:fastjson:1.2.68'
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.bjlx.armcloudaf",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 26
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.bjlx.armcloudaf", appContext.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".App"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.ArmCloudAF">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,32 @@
|
|||
package androidx.app;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Encode {
|
||||
public static String md5(byte[] data) {
|
||||
try {
|
||||
// 创建 MessageDigest 实例,指定算法为 MD5
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
// 更新 MessageDigest 以传入要加密的数据
|
||||
messageDigest.update(data);
|
||||
// 获取加密后的字节数组
|
||||
byte[] digest = messageDigest.digest();
|
||||
// 将字节数组转换为十六进制字符串
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : digest) {
|
||||
// 将每个字节转换为两位十六进制
|
||||
String hex = Integer.toHexString(0xFF & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append("0"); // 添加前导零
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,514 @@
|
|||
package androidx.app;
|
||||
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.android.core.XC_MethodHook;
|
||||
import com.android.core.XSHelpers;
|
||||
import com.bjlx.armcloudaf.ModifyData;
|
||||
import com.bjlx.armcloudaf.MyLogger;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
import java.security.Permission;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Entry {
|
||||
public static final String TAG = "HKAPP(Appsflyer)";
|
||||
private static final ExecutorService executor = Executors.newFixedThreadPool(5);
|
||||
private static final Map<String,String> tmpMap=new HashMap<>();
|
||||
|
||||
|
||||
/* public static void systemMain(ClassLoader classLoader, String pkg, String processName) {
|
||||
Log.e(TAG,"systemMain pkg="+pkg);
|
||||
doJavaHook(pkg, classLoader);
|
||||
}
|
||||
*/
|
||||
|
||||
public static void appMain(ClassLoader loader, Context context, String appClass, String pkg, String process) {
|
||||
Log.d(TAG, "appClass=" + appClass + ", filesDir=" + context.getFilesDir() + ", pid=" + Process.myPid() + ", process=" + process);
|
||||
if(process.contains("sandboxed_process")){
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
doJavaHook(pkg, loader);
|
||||
}catch (Exception ex){
|
||||
Log.d(TAG, Log.getStackTraceString(ex));
|
||||
}
|
||||
}
|
||||
|
||||
static String currentPackageName;
|
||||
static String currentAppVersion;
|
||||
|
||||
private static void doJavaHook(String pkg, ClassLoader loader) {
|
||||
|
||||
try {
|
||||
initData();
|
||||
/* final AppsFlyerCollector collector = new AppsFlyerCollector(context);
|
||||
currentPackageName = context.getPackageName();
|
||||
currentAppVersion = context.getPackageManager().getPackageInfo(currentPackageName, 0).versionName;
|
||||
Log.d(TAG,"currentPackageName="+currentPackageName+" currentAppVersion="+currentAppVersion);
|
||||
*/
|
||||
/* XSHelpers.findAndHookConstructor(JSONObject.class, Map.class, new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
super.beforeHookedMethod(param);
|
||||
Map indata=(Map) param.args[0];
|
||||
if (indata.containsKey("xdp")){
|
||||
Log.i(TAG,"JSONObject map 找到xdp 修改前: xdp="+indata.get("xdp"));
|
||||
indata.put("xdp",900);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Log.i(TAG, "#------- JSONObject Found -------#");
|
||||
Log.i(TAG, "修改前:"+((JSONObject)param.thisObject).toString());
|
||||
JSONObject jsonObject=(JSONObject)param.thisObject;
|
||||
if (jsonObject.has("xdp")){
|
||||
Log.i(TAG,"hook 找到xdp");
|
||||
jsonObject.put("xdp","800");
|
||||
}
|
||||
if (jsonObject.has("ydp")){
|
||||
Log.i(TAG,"hook 找到ydp");
|
||||
}
|
||||
if (jsonObject.has("x_px")){
|
||||
Log.i(TAG,"hook 找到x_px");
|
||||
}
|
||||
if (jsonObject.has("y_px")){
|
||||
Log.i(TAG,"hook 找到y_px");
|
||||
}
|
||||
if (jsonObject.has("d_dpi")){
|
||||
Log.i(TAG,"hook 找到d_dpi");
|
||||
}
|
||||
if (jsonObject.has("size")){
|
||||
Log.i(TAG,"hook 找到size");
|
||||
}
|
||||
if (jsonObject.has("arch")){
|
||||
Log.i(TAG,"hook 找到arch");
|
||||
}
|
||||
if (jsonObject.has("cpu_abi")){
|
||||
Log.i(TAG,"hook 找到cpu_abi");
|
||||
}
|
||||
if (jsonObject.has("cpu_abi2")){
|
||||
Log.i(TAG,"hook 找到cpu_abi2");
|
||||
}
|
||||
if (jsonObject.has("btl")){
|
||||
Log.i(TAG,"hook 找到btl");
|
||||
}
|
||||
if (jsonObject.has("btch")){
|
||||
Log.i(TAG,"hook 找到btch");
|
||||
}
|
||||
if (jsonObject.has("disk")){
|
||||
Log.i(TAG,"hook 找到disk");
|
||||
}
|
||||
if (jsonObject.has("sdk")){
|
||||
Log.i(TAG,"hook 找到sdk");
|
||||
}
|
||||
if (jsonObject.has("network")){
|
||||
Log.i(TAG,"hook 找到network");
|
||||
}
|
||||
if (jsonObject.has("api_ver")){
|
||||
Log.i(TAG,"hook 找到api_ver");
|
||||
}
|
||||
if (jsonObject.has("api_ver_name")){
|
||||
Log.i(TAG,"hook 找到api_ver_name");
|
||||
}
|
||||
if (jsonObject.has("carrier")){
|
||||
Log.i(TAG,"hook 找到carrier");
|
||||
}
|
||||
if (jsonObject.has("product")){
|
||||
Log.i(TAG,"hook 找到product");
|
||||
}
|
||||
if (jsonObject.has("last_boot_time")){
|
||||
Log.i(TAG,"hook 找到last_boot_time");
|
||||
}
|
||||
if (jsonObject.has("initiating_package")){
|
||||
Log.i(TAG,"hook 找到initiating_package");
|
||||
}
|
||||
if (jsonObject.has("advertiserId")){
|
||||
Log.i(TAG,"hook 找到advertiserId");
|
||||
}
|
||||
logLong(TAG, "修改后:"+((JSONObject)param.thisObject).toString());
|
||||
//collector.checkAndParse(param);
|
||||
}
|
||||
});*/
|
||||
|
||||
XSHelpers.findAndHookMethod(URL.class, "openConnection", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
java.net.URL url = (java.net.URL) param.thisObject;
|
||||
URLConnection origConn = (URLConnection) param.getResult();
|
||||
String urlStr = url.toString();
|
||||
Log.d(TAG, "afterHookedMethod: "+urlStr);
|
||||
if (urlStr.contains("appsflyer") ) {
|
||||
//Log.i(TAG, "#------- Found AF -------#");
|
||||
MyLogger.i(TAG, "openConnection urlStr="+urlStr);
|
||||
param.setResult(new XHttpURLConnection(url, (HttpURLConnection) origConn, new HttpRequestListener() {
|
||||
@Override
|
||||
public void onAfterRequest(XHttpURLConnection conn, byte[] b, int off, int len) {
|
||||
try {
|
||||
byte[] bytes = new byte[len-8-16];
|
||||
System.arraycopy(b, off, bytes, 0, len-8-16);
|
||||
String md5 = Encode.md5(bytes);
|
||||
String afdata = tmpMap.get(md5);
|
||||
Uri uri=Uri.parse(urlStr);
|
||||
String app_id = uri.getQueryParameter("app_id");
|
||||
String buildnumber=uri.getQueryParameter("buildnumber");
|
||||
// if (urlStr.contains("conversions") || urlStr.contains("gcd") || urlStr.contains("inapps")){
|
||||
//
|
||||
// }
|
||||
logLong(TAG,"发送数据:afdata="+afdata);
|
||||
MyLogger.e(TAG,"发送数据:afdata="+afdata);
|
||||
MyLogger.e(TAG,"发送数据:app_id="+app_id);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Log.e(TAG,"发送数据异常:"+e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
XSHelpers.findAndHookMethod(Cipher.class, "doFinal", byte[].class, new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
super.beforeHookedMethod(param);
|
||||
|
||||
byte[] bytes = (byte[]) param.args[0];
|
||||
String dataStr=new String(bytes);
|
||||
if (!TextUtils.isEmpty(dataStr)){
|
||||
com.alibaba.fastjson.JSONObject jsonObj=convertToJson(dataStr);
|
||||
if (jsonObj!=null&&jsonObj.containsKey("appsflyerKey")&&jsonObj.containsKey("af_timestamp")){
|
||||
logLong(TAG,"doFinal 修改前="+jsonObj.toJSONString());
|
||||
jsonObj=ModifyData.modifyData(jsonObj);
|
||||
logLong(TAG,"doFinal 修改后="+jsonObj.toJSONString());
|
||||
param.args[0]=jsonObj.toJSONString().getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
|
||||
byte[] bytes = (byte[]) param.args[0];
|
||||
String dataStr=new String(bytes);
|
||||
//Log.e(AFHookTools,"Cipher doFinal data="+dataStr);
|
||||
if (!TextUtils.isEmpty(dataStr)){
|
||||
com.alibaba.fastjson.JSONObject jsonObj=convertToJson(dataStr);
|
||||
if (jsonObj!=null&&jsonObj.containsKey("appsflyerKey")&&jsonObj.containsKey("af_timestamp")){
|
||||
byte[] result = (byte[])param.getResult();
|
||||
String md5 = Encode.md5(result);
|
||||
//Log.e(AFHookTools,"md5="+md5+" encodeData="+dataStr);
|
||||
tmpMap.put(md5,dataStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void broadcast(Context context, JSONObject collectedData)
|
||||
{
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Intent intent = new Intent("com.titan.HOOK_BROADCAST");
|
||||
intent.setClassName("com.titan.taskpolling", "com.titan.taskpolling.HookBroadcastReceiver");
|
||||
|
||||
intent.putExtra("extractType", "AF");
|
||||
intent.putExtra("packageName", context.getPackageName());
|
||||
intent.putExtra("collectedData", collectedData.toString());
|
||||
context.sendBroadcast(intent);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] data, String key) throws Exception {
|
||||
final byte[] salt = new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
|
||||
if (data != null && data.length > 24) {
|
||||
byte[] iv = new byte[16];
|
||||
System.arraycopy(data, data.length - 8 - 16, iv, 0, 16);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec seckey = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec(key.toCharArray(), salt, 10000, 0x80)).getEncoded(), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, seckey, new IvParameterSpec(iv));
|
||||
return cipher.doFinal(data, 0, data.length - 8 - 16);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class HttpRequestListener extends Object {
|
||||
public XHttpURLConnection Owner;
|
||||
public void onBeforeRequest(XHttpURLConnection conn, byte[] b, int off, int len) { }
|
||||
public void onAfterRequest(XHttpURLConnection conn, byte[] b, int off, int len) { }
|
||||
}
|
||||
public final static class XOutputStream extends OutputStream {
|
||||
private HttpRequestListener _listener;
|
||||
public OutputStream Out;
|
||||
|
||||
protected XOutputStream(HttpRequestListener listener){ super(); _listener = listener; }
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (_listener != null) { _listener.onBeforeRequest(_listener.Owner, b, off, len); }
|
||||
Out.write(b, off, len);
|
||||
if (_listener != null) { _listener.onAfterRequest(_listener.Owner, b, off, len); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException { Out.write(b); }
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException { Out.write(b); }
|
||||
@Override
|
||||
public void flush() throws IOException { Out.flush(); }
|
||||
@Override
|
||||
public void close() throws IOException { Out.close(); }
|
||||
@Override
|
||||
public int hashCode() { return Out.hashCode(); }
|
||||
@Override
|
||||
public boolean equals(Object obj) {return Out.equals(obj); }
|
||||
@Override
|
||||
public String toString() { return Out.toString(); }
|
||||
}
|
||||
public final static class XHttpURLConnection extends HttpURLConnection {
|
||||
private final URL _orgUrl;
|
||||
private final HttpURLConnection _orgConn;
|
||||
private final HttpRequestListener _listener;
|
||||
private XOutputStream _outStream;
|
||||
|
||||
protected XHttpURLConnection(URL url, HttpURLConnection conn, HttpRequestListener listener) {
|
||||
super(url);
|
||||
_orgUrl = url;
|
||||
_orgConn = conn;
|
||||
_listener = listener;
|
||||
_listener.Owner = this;
|
||||
_outStream = new XOutputStream(_listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
_outStream.Out = _orgConn.getOutputStream();
|
||||
return _outStream;
|
||||
}
|
||||
@Override
|
||||
public void connect() throws IOException {
|
||||
if (_listener != null){ _listener.onBeforeRequest(this, new byte[0], 0, 0); }
|
||||
_orgConn.connect();
|
||||
if (_listener != null){ _listener.onAfterRequest(this, new byte[0], 0, 0); }
|
||||
}
|
||||
@Override
|
||||
public String getHeaderFieldKey(int n) { return _orgConn.getHeaderFieldKey(n); }
|
||||
@Override
|
||||
public void setFixedLengthStreamingMode(int contentLength) { _orgConn.setFixedLengthStreamingMode(contentLength); }
|
||||
@Override
|
||||
public void setFixedLengthStreamingMode(long contentLength) { _orgConn.setFixedLengthStreamingMode(contentLength); }
|
||||
@Override
|
||||
public void setChunkedStreamingMode(int chunklen) { _orgConn.setChunkedStreamingMode(chunklen); }
|
||||
@Override
|
||||
public String getHeaderField(int n) { return _orgConn.getHeaderField(n); }
|
||||
@Override
|
||||
public void setInstanceFollowRedirects(boolean followRedirects) { _orgConn.setInstanceFollowRedirects(followRedirects); }
|
||||
@Override
|
||||
public boolean getInstanceFollowRedirects() { return _orgConn.getInstanceFollowRedirects(); }
|
||||
@Override
|
||||
public void setRequestMethod(String method) throws ProtocolException { _orgConn.setRequestMethod(method); }
|
||||
@Override
|
||||
public String getRequestMethod() { return _orgConn.getRequestMethod(); }
|
||||
@Override
|
||||
public int getResponseCode() throws IOException { return _orgConn.getResponseCode(); }
|
||||
@Override
|
||||
public String getResponseMessage() throws IOException { return _orgConn.getResponseMessage(); }
|
||||
@Override
|
||||
public long getHeaderFieldDate(String name, long Default) { return _orgConn.getHeaderFieldDate(name, Default); }
|
||||
@Override
|
||||
public Permission getPermission() throws IOException { return _orgConn.getPermission(); }
|
||||
@Override
|
||||
public InputStream getErrorStream() { return _orgConn.getErrorStream(); }
|
||||
@Override
|
||||
public void setConnectTimeout(int timeout) { _orgConn.setConnectTimeout(timeout); }
|
||||
@Override
|
||||
public int getConnectTimeout() { return _orgConn.getConnectTimeout(); }
|
||||
@Override
|
||||
public void setReadTimeout(int timeout) { _orgConn.setReadTimeout(timeout); }
|
||||
@Override
|
||||
public int getReadTimeout() { return _orgConn.getReadTimeout(); }
|
||||
@Override
|
||||
public URL getURL() { return _orgConn.getURL(); }
|
||||
@Override
|
||||
public int getContentLength() { return _orgConn.getContentLength(); }
|
||||
@Override
|
||||
public long getContentLengthLong() { return _orgConn.getContentLengthLong(); }
|
||||
@Override
|
||||
public String getContentType() { return _orgConn.getContentType(); }
|
||||
@Override
|
||||
public String getContentEncoding() { return _orgConn.getContentEncoding(); }
|
||||
@Override
|
||||
public long getExpiration() { return _orgConn.getExpiration(); }
|
||||
@Override
|
||||
public long getDate() { return _orgConn.getDate(); }
|
||||
@Override
|
||||
public long getLastModified() { return _orgConn.getLastModified(); }
|
||||
@Override
|
||||
public String getHeaderField(String name) { return _orgConn.getHeaderField(name); }
|
||||
@Override
|
||||
public Map<String, List<String>> getHeaderFields() { return _orgConn.getHeaderFields(); }
|
||||
@Override
|
||||
public int getHeaderFieldInt(String name, int Default) { return _orgConn.getHeaderFieldInt(name, Default); }
|
||||
@Override
|
||||
public long getHeaderFieldLong(String name, long Default) { return _orgConn.getHeaderFieldLong(name, Default); }
|
||||
@Override
|
||||
public Object getContent() throws IOException { return _orgConn.getContent(); }
|
||||
@Override
|
||||
public Object getContent(Class[] classes) throws IOException { return _orgConn.getContent(classes); }
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException { return _orgConn.getInputStream(); }
|
||||
@Override
|
||||
public String toString() { return _orgConn.toString(); }
|
||||
@Override
|
||||
public void setDoInput(boolean doinput) { _orgConn.setDoInput(doinput); }
|
||||
@Override
|
||||
public boolean getDoInput() { return _orgConn.getDoInput(); }
|
||||
@Override
|
||||
public void setDoOutput(boolean dooutput) { _orgConn.setDoOutput(dooutput); }
|
||||
@Override
|
||||
public boolean getDoOutput() { return _orgConn.getDoOutput(); }
|
||||
@Override
|
||||
public void setAllowUserInteraction(boolean allowuserinteraction) { _orgConn.setAllowUserInteraction(allowuserinteraction); }
|
||||
@Override
|
||||
public boolean getAllowUserInteraction() { return _orgConn.getAllowUserInteraction(); }
|
||||
@Override
|
||||
public void setUseCaches(boolean usecaches) { _orgConn.setUseCaches(usecaches); }
|
||||
@Override
|
||||
public boolean getUseCaches() { return _orgConn.getUseCaches(); }
|
||||
@Override
|
||||
public void setIfModifiedSince(long ifmodifiedsince) { _orgConn.setIfModifiedSince(ifmodifiedsince); }
|
||||
@Override
|
||||
public long getIfModifiedSince() { return _orgConn.getIfModifiedSince(); }
|
||||
@Override
|
||||
public boolean getDefaultUseCaches() { return _orgConn.getDefaultUseCaches(); }
|
||||
@Override
|
||||
public void setDefaultUseCaches(boolean defaultusecaches) { _orgConn.setDefaultUseCaches(defaultusecaches); }
|
||||
@Override
|
||||
public void setRequestProperty(String key, String value) { _orgConn.setRequestProperty(key, value); }
|
||||
@Override
|
||||
public void addRequestProperty(String key, String value) { _orgConn.addRequestProperty(key, value); }
|
||||
@Override
|
||||
public String getRequestProperty(String key) { return _orgConn.getRequestProperty(key); }
|
||||
@Override
|
||||
public Map<String, List<String>> getRequestProperties() { return _orgConn.getRequestProperties(); }
|
||||
@Override
|
||||
public void disconnect() { _orgConn.disconnect(); }
|
||||
@Override
|
||||
public int hashCode() { return _orgConn.hashCode(); }
|
||||
@Override
|
||||
public boolean equals(Object obj) { return _orgConn.equals(obj); }
|
||||
@Override
|
||||
public boolean usingProxy() { return _orgConn.usingProxy(); }
|
||||
}
|
||||
public static com.alibaba.fastjson.JSONObject convertToJson(String str)
|
||||
{
|
||||
try {
|
||||
|
||||
return JSON.parseObject(str);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static void initData(){
|
||||
/*XSHelpers.findAndHookMethod(Application.class, "attach",
|
||||
Context.class, new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
super.beforeHookedMethod(param);
|
||||
Log.e(TAG,"Application attach call============");
|
||||
SocketClient socketClient=new SocketClient();
|
||||
socketClient.connect(new SocketClient.MessageListener() {
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
Log.e(TAG,"socket 收到消息:"+message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});*/
|
||||
XSHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
super.beforeHookedMethod(param);
|
||||
Application application=(Application) param.thisObject;
|
||||
String dataDir=application.getDataDir().getAbsolutePath();
|
||||
MyLogger.init(application.getDataDir());
|
||||
Log.e(TAG,"Application onCreate call dataDir="+dataDir);
|
||||
ModifyData.initNewData(application.getDataDir());
|
||||
}
|
||||
});
|
||||
}
|
||||
public static void logLong(String tag, String message) {
|
||||
int maxLogSize = 4000;
|
||||
if (message == null) return;
|
||||
|
||||
int length = message.length();
|
||||
int chunkCount = (int) Math.ceil((double) length / maxLogSize);
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
int start = i * maxLogSize;
|
||||
int end = Math.min(start + maxLogSize, length);
|
||||
Log.e(tag, "参数:"+message.substring(start, end) + " _" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package androidx.app;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/* loaded from: classes5.dex */
|
||||
public class HttpClient {
|
||||
private static final String TAG = "HttpClient";
|
||||
|
||||
public interface IDownloadListener {
|
||||
void onError(Exception exc);
|
||||
|
||||
void onFinished(String str);
|
||||
|
||||
void onProgress(float f);
|
||||
}
|
||||
|
||||
public static void requestPost(String urlString, JSONObject params) throws IOException {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty("Content-Type", "application/json");
|
||||
con.setDoOutput(true);
|
||||
con.setDoInput(true);
|
||||
con.setConnectTimeout(30000);
|
||||
con.setReadTimeout(30000);
|
||||
OutputStream os = con.getOutputStream();
|
||||
try {
|
||||
byte[] input = params.toString().getBytes("utf-8");
|
||||
os.write(input, 0, input.length);
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
|
||||
try {
|
||||
StringBuilder response = new StringBuilder();
|
||||
while (true) {
|
||||
String responseLine = br.readLine();
|
||||
if (responseLine != null) {
|
||||
response.append(responseLine.trim());
|
||||
} else {
|
||||
br.close();
|
||||
Log.i(TAG, "send finish");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (Throwable th2) {
|
||||
th.addSuppressed(th2);
|
||||
}
|
||||
throw th;
|
||||
}
|
||||
} catch (Throwable th3) {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (Throwable th4) {
|
||||
th3.addSuppressed(th4);
|
||||
}
|
||||
}
|
||||
throw th3;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject requestGet(String urlString) throws IOException, JSONException {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("Content-Type", "application/json");
|
||||
con.setDoOutput(true);
|
||||
con.setDoInput(true);
|
||||
con.setConnectTimeout(30000);
|
||||
con.setReadTimeout(30000);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
|
||||
try {
|
||||
StringBuilder response = new StringBuilder();
|
||||
while (true) {
|
||||
String responseLine = br.readLine();
|
||||
if (responseLine != null) {
|
||||
response.append(responseLine.trim());
|
||||
} else {
|
||||
JSONObject jSONObject = new JSONObject(response.toString());
|
||||
br.close();
|
||||
return jSONObject;
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (Throwable th2) {
|
||||
th.addSuppressed(th2);
|
||||
}
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject convertToJson(String str) {
|
||||
try {
|
||||
return new JSONObject(str);
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package androidx.app;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
public class SocketClient {
|
||||
|
||||
private String serverIp = "127.0.0.1";
|
||||
private int port = 8888;
|
||||
|
||||
private Socket socket;
|
||||
private PrintWriter writer;
|
||||
private boolean connected = false;
|
||||
private MessageListener messageListener;
|
||||
|
||||
public interface MessageListener {
|
||||
void onMessage(String message);
|
||||
}
|
||||
|
||||
public SocketClient() {
|
||||
}
|
||||
|
||||
public SocketClient(String serverIp, int port) {
|
||||
this.serverIp = serverIp;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void connect(MessageListener listener) {
|
||||
this.messageListener = listener;
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
socket = new Socket(serverIp, port);
|
||||
writer = new PrintWriter(socket.getOutputStream(), true);
|
||||
connected = true;
|
||||
Log.d("SocketClient", "已连接到服务器");
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage("已连接到服务器");
|
||||
}
|
||||
startReceiving();
|
||||
} catch (Exception e) {
|
||||
Log.e("SocketClient", "连接失败", e);
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage("连接失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void startReceiving() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
while (connected) {
|
||||
String message = reader.readLine();
|
||||
if (message == null) break;
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage(message);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("SocketClient", "接收消息失败", e);
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage("接收失败: " + e.getMessage());
|
||||
}
|
||||
} finally {
|
||||
disconnect();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void sendMessage(String message) {
|
||||
if (!connected) return;
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (writer != null) {
|
||||
writer.println(message);
|
||||
Log.d("SocketClient", "已发送消息: " + message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("SocketClient", "发送消息失败", e);
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage("发送失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
connected = false;
|
||||
try {
|
||||
if (writer != null) writer.close();
|
||||
if (socket != null) socket.close();
|
||||
} catch (Exception e) {
|
||||
Log.e("SocketClient", "断开连接错误", e);
|
||||
}
|
||||
if (messageListener != null) {
|
||||
messageListener.onMessage("已断开连接");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class AESPatchHelper {
|
||||
private static final byte[] SALT = new byte[8]; // 全零 salt
|
||||
private static final int PADDING_LEN = 8;
|
||||
private static final int IV_LEN = 16;
|
||||
|
||||
public static byte[] extractIV(byte[] data) {
|
||||
int ivPos = data.length - PADDING_LEN - IV_LEN;
|
||||
return Arrays.copyOfRange(data, ivPos, ivPos + IV_LEN);
|
||||
}
|
||||
|
||||
public static byte[] extractPadding(byte[] data) {
|
||||
int padPos = data.length - PADDING_LEN - IV_LEN - PADDING_LEN;
|
||||
if (padPos < 0) padPos = data.length - PADDING_LEN - IV_LEN;
|
||||
return Arrays.copyOfRange(data, data.length - PADDING_LEN - IV_LEN, data.length - IV_LEN);
|
||||
}
|
||||
|
||||
public static byte[] extractCiphertext(byte[] data) {
|
||||
int ctLen = data.length - PADDING_LEN - IV_LEN;
|
||||
return Arrays.copyOfRange(data, 0, ctLen);
|
||||
}
|
||||
|
||||
private static SecretKeySpec deriveKey(String password) throws Exception {
|
||||
return new SecretKeySpec(
|
||||
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
|
||||
.generateSecret(new PBEKeySpec(password.toCharArray(), SALT, 10000, 128))
|
||||
.getEncoded(),
|
||||
"AES"
|
||||
);
|
||||
}
|
||||
|
||||
public static byte[] decryptRaw(byte[] ciphertext, byte[] iv, String password) throws Exception {
|
||||
SecretKeySpec key = deriveKey(password);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
|
||||
return cipher.doFinal(ciphertext);
|
||||
}
|
||||
|
||||
public static byte[] encryptRaw(byte[] plaintext, byte[] iv, String password) throws Exception {
|
||||
SecretKeySpec key = deriveKey(password);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
|
||||
return cipher.doFinal(plaintext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 入口函数:
|
||||
* 提取 data 中 IV + padding + 密文 → 解密 → 加密 → 再拼装成结构化数据
|
||||
*/
|
||||
public static byte[] patchAndReEncrypt(byte[] data, String password) throws Exception {
|
||||
if (data == null || data.length <= (PADDING_LEN + IV_LEN)) return null;
|
||||
|
||||
byte[] iv = extractIV(data);
|
||||
byte[] padding = extractPadding(data);
|
||||
byte[] ciphertext = extractCiphertext(data);
|
||||
// 解密
|
||||
byte[] plaintext = decryptRaw(ciphertext, iv, password);
|
||||
// 可在此修改 plaintext(如篡改某些字段)
|
||||
// 再加密
|
||||
byte[] newCiphertext = encryptRaw(plaintext, iv, password);
|
||||
|
||||
// 拼装为结构:[newCiphertext] + [padding] + [iv]
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(newCiphertext);
|
||||
out.write(padding);
|
||||
out.write(iv);
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class App extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
test2();
|
||||
}
|
||||
|
||||
private void test2(){
|
||||
ClipboardUtils.pasteText(this);
|
||||
/* ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
if (clipboard.hasPrimaryClip()) {
|
||||
ClipData clipData = clipboard.getPrimaryClip();
|
||||
if (clipData != null && clipData.getItemCount() > 0) {
|
||||
CharSequence text = clipData.getItemAt(0).coerceToText(this);
|
||||
Log.d("Clipboard", "剪贴板内容是:" + text);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class ClipboardUtils {
|
||||
private static final String TAG = "ClipboardUtils";
|
||||
|
||||
public static String pasteText(Context context) {
|
||||
try {
|
||||
// 获取剪贴板服务
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
if (clipboard == null) {
|
||||
Log.w(TAG, "Clipboard service not available");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查剪贴板是否有内容
|
||||
if (!clipboard.hasPrimaryClip()) {
|
||||
Log.d(TAG, "Clipboard is empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取剪贴板内容
|
||||
ClipData clipData = clipboard.getPrimaryClip();
|
||||
if (clipData == null) {
|
||||
Log.w(TAG, "ClipData is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查项目数量
|
||||
if (clipData.getItemCount() == 0) {
|
||||
Log.d(TAG, "ClipData has no items");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取第一项内容
|
||||
ClipData.Item item = clipData.getItemAt(0);
|
||||
|
||||
// 尝试获取文本内容
|
||||
CharSequence text = item.getText();
|
||||
if (text != null) {
|
||||
String result = text.toString();
|
||||
if (!TextUtils.isEmpty(result)) {
|
||||
Log.d(TAG, "Successfully pasted text: " + result.substring(0, Math.min(20, result.length())) + "...");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Clipboard item has no text content");
|
||||
return null;
|
||||
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Security exception when accessing clipboard", e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error pasting text from clipboard", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.app.Entry;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_main);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
Entry.appMain(getClassLoader(), this, "", getPackageName(), "");
|
||||
test2();
|
||||
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
test2();
|
||||
}
|
||||
});
|
||||
}
|
||||
private void test2(){
|
||||
ClipboardUtils.pasteText(this);
|
||||
/* ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
if (clipboard.hasPrimaryClip()) {
|
||||
ClipData clipData = clipboard.getPrimaryClip();
|
||||
if (clipData != null && clipData.getItemCount() > 0) {
|
||||
CharSequence text = clipData.getItemAt(0).coerceToText(this);
|
||||
Log.d("Clipboard", "剪贴板内容是:" + text);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
private void test(){
|
||||
Uri uri = Uri.parse("content://com.android.grape.deviceinfo.provider/device_info");
|
||||
Uri path=Uri.withAppendedPath(
|
||||
uri,
|
||||
"device/d1b3e7f8c9a04b8e9f2c1d5e6f7a8b9c"
|
||||
);
|
||||
//new String[]{"data"}
|
||||
Cursor cursor = getContentResolver().query(path, null, null, null, null);
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
// 获取字段名(列名)数组
|
||||
String[] columnNames = cursor.getColumnNames();
|
||||
|
||||
// 遍历每一列,按名字读取
|
||||
for (String columnName : columnNames) {
|
||||
int columnIndex = cursor.getColumnIndex(columnName);
|
||||
String columnVale=cursor.getString(columnIndex);
|
||||
Log.d("Cursor", columnName + ": " + columnVale);
|
||||
}
|
||||
|
||||
Log.d("Cursor", "——— 一行结束 ———");
|
||||
}
|
||||
cursor.close();
|
||||
}else {
|
||||
Log.e("Cursor","cursor ======== is null");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.app.Entry;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class ModifyData {
|
||||
private static JSONObject newData;
|
||||
private final static String fileName="/device.txt";
|
||||
public static JSONObject modifyData(JSONObject jsonObj){
|
||||
if (newData == null) return jsonObj;
|
||||
if (jsonObj.containsKey("deviceData")){
|
||||
com.alibaba.fastjson.JSONObject deviceData =jsonObj.getJSONObject("deviceData");
|
||||
if (deviceData.containsKey("sensors")&&newData.containsKey("sensors")){
|
||||
deviceData.put("sensors",newData.getJSONArray("sensors"));
|
||||
}
|
||||
if (deviceData.containsKey("arch")&&newData.containsKey("arch")){
|
||||
deviceData.put("arch",newData.getString("arch"));
|
||||
}
|
||||
if (deviceData.containsKey("cpu_abi")&&newData.containsKey("cpu_abi")){
|
||||
deviceData.put("cpu_abi",newData.getString("cpu_abi"));
|
||||
}
|
||||
if (deviceData.containsKey("cpu_abi2")&&newData.containsKey("cpu_abi2")){
|
||||
deviceData.put("cpu_abi2",newData.getString("cpu_abi2"));
|
||||
}
|
||||
if (deviceData.containsKey("cpu_abi2")&&newData.containsKey("cpu_abi2")){
|
||||
deviceData.put("cpu_abi2",newData.getString("cpu_abi2"));
|
||||
}
|
||||
if (deviceData!=null&&deviceData.containsKey("dim")){
|
||||
com.alibaba.fastjson.JSONObject dim =deviceData.getJSONObject("dim");
|
||||
if (dim!=null){
|
||||
if (dim.containsKey("xdp")&&newData.containsKey("xdp")){
|
||||
dim.put("xdp",newData.getString("xdp"));
|
||||
}
|
||||
if (dim.containsKey("ydp")&&newData.containsKey("ydp")){
|
||||
dim.put("ydp",newData.getString("ydp"));
|
||||
}
|
||||
if (dim.containsKey("x_px")&&newData.containsKey("x_px")){
|
||||
dim.put("x_px",newData.getString("x_px"));
|
||||
}
|
||||
if (dim.containsKey("y_px")&&newData.containsKey("y_px")){
|
||||
dim.put("y_px",newData.getString("y_px"));
|
||||
}
|
||||
if (dim.containsKey("d_dpi")&&newData.containsKey("d_dpi")){
|
||||
dim.put("d_dpi",newData.getString("d_dpi"));
|
||||
}
|
||||
if (dim.containsKey("size")&&newData.containsKey("size")){
|
||||
dim.put("size",newData.getString("size"));
|
||||
}
|
||||
}
|
||||
deviceData.put("dim",dim);
|
||||
}
|
||||
if (deviceData.containsKey("btch")&&newData.containsKey("btch")){
|
||||
deviceData.put("btch",newData.getString("btch"));
|
||||
}
|
||||
if (deviceData.containsKey("btl")&&newData.containsKey("btl")){
|
||||
deviceData.put("btl",newData.getString("btl"));
|
||||
}
|
||||
jsonObj.put("deviceData",deviceData);
|
||||
}
|
||||
if (jsonObj.containsKey("disk")&&newData.containsKey("disk")){
|
||||
jsonObj.put("disk",newData.getString("disk"));
|
||||
}
|
||||
if (jsonObj.containsKey("sdk")&&newData.containsKey("sdk")){
|
||||
jsonObj.put("sdk",newData.getString("sdk"));
|
||||
}
|
||||
if (jsonObj.containsKey("network")&&newData.containsKey("network")){
|
||||
jsonObj.put("network",newData.getString("network"));
|
||||
}
|
||||
if (jsonObj.containsKey("referrers")){
|
||||
JSONArray referrers =jsonObj.getJSONArray("referrers");
|
||||
JSONArray newRefs=new JSONArray();
|
||||
if (referrers!=null){
|
||||
for (int i = 0; i < referrers.size(); i++) {
|
||||
JSONObject objitem = referrers.getJSONObject(i);
|
||||
if (objitem.containsKey("api_ver")&&newData.containsKey("api_ver")){
|
||||
objitem.put("api_ver",newData.getIntValue("api_ver"));
|
||||
}
|
||||
if (objitem.containsKey("api_ver_name")&&newData.containsKey("api_ver_name")){
|
||||
objitem.put("api_ver_name",newData.getString("api_ver_name"));
|
||||
}
|
||||
newRefs.add(i,objitem);
|
||||
}
|
||||
jsonObj.put("referrers",newRefs);
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("carrier")&&newData.containsKey("carrier")){
|
||||
jsonObj.put("carrier",newData.getString("carrier"));
|
||||
}
|
||||
if (jsonObj.containsKey("product")&&newData.containsKey("product")){
|
||||
jsonObj.put("product",newData.getString("product"));
|
||||
}
|
||||
if (jsonObj.containsKey("last_boot_time")&&newData.containsKey("last_boot_time")){
|
||||
jsonObj.put("last_boot_time",newData.getLong("last_boot_time"));
|
||||
}
|
||||
if (jsonObj.containsKey("advertiserId")&&newData.containsKey("advertiserId")){
|
||||
jsonObj.put("advertiserId",newData.getString("advertiserId"));
|
||||
}
|
||||
if (jsonObj.containsKey("install_source_info")&&newData.containsKey("install_source_info")){
|
||||
JSONObject install_source_info=jsonObj.getJSONObject("install_source_info");
|
||||
if (install_source_info!=null){
|
||||
install_source_info.put("initiating_package",newData.getString("install_source_info"));
|
||||
install_source_info.put("installing_package",newData.getString("install_source_info"));
|
||||
}
|
||||
|
||||
jsonObj.put("install_source_info",install_source_info);
|
||||
}
|
||||
return jsonObj;
|
||||
}
|
||||
public static void initNewData(File fileDir){
|
||||
|
||||
try {
|
||||
byte[] content = readFile(new File(fileDir,fileName));
|
||||
byte[] msgByte=Base64.decode(content,Base64.DEFAULT);
|
||||
Log.e(Entry.TAG,"initNewData msgByte="+new String(msgByte));
|
||||
newData=JSONObject.parseObject(new String(msgByte));
|
||||
Log.e(Entry.TAG,"数据初始化完成==========");
|
||||
} catch (Exception e) {
|
||||
Log.d(Entry.TAG, "initNewData: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
public static byte[] readFile(File file) {
|
||||
try {
|
||||
return Files.readAllBytes(file.toPath());
|
||||
} catch (IOException e) {
|
||||
Log.e(Entry.TAG,"读文件异常:"+e.toString());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MyLogger {
|
||||
private static final String LOG_TAG = "MyLogger";
|
||||
private static final String LOG_FILE_NAME = "/log.txt";
|
||||
private static File logFile;
|
||||
|
||||
public static void init(File filesDir) {
|
||||
logFile = new File(filesDir, LOG_FILE_NAME);
|
||||
}
|
||||
|
||||
private static void writeToFile(String level, String tag, String msg) {
|
||||
if (logFile == null){
|
||||
Log.e(LOG_TAG, "日志文件未初始化");
|
||||
return;
|
||||
}
|
||||
try (FileWriter writer = new FileWriter(logFile, true)) {
|
||||
String logLine = String.format("%s/%s: %s\n", level, tag, msg);
|
||||
writer.write(logLine);
|
||||
writer.flush();
|
||||
Log.d(LOG_TAG, "写入日志成功");
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, "写入日志失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
Log.d(tag, msg);
|
||||
writeToFile("D", tag, msg);
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
Log.i(tag, msg);
|
||||
writeToFile("I", tag, msg);
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
Log.w(tag, msg);
|
||||
writeToFile("W", tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
Log.e(tag, msg);
|
||||
writeToFile("E", tag, msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<Button
|
||||
android:text="测试"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_test"
|
||||
tools:ignore="MissingConstraints" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.6 KiB |
|
@ -0,0 +1,7 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.ArmCloudAF" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Customize your dark theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||
</style>
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">ArmCloudAF</string>
|
||||
</resources>
|
|
@ -0,0 +1,9 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.ArmCloudAF" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Customize your light theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.ArmCloudAF" parent="Base.Theme.ArmCloudAF" />
|
||||
</resources>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older than API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
|
@ -0,0 +1,17 @@
|
|||
package com.bjlx.armcloudaf;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# 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=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
|
@ -0,0 +1,22 @@
|
|||
[versions]
|
||||
agp = "8.10.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
appcompat = "1.6.1"
|
||||
material = "1.10.0"
|
||||
activity = "1.8.0"
|
||||
constraintlayout = "2.1.4"
|
||||
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#Fri Jul 11 09:30:14 GMT+08:00 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# 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
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# 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
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
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" -a "$nonstop" = "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 or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@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
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@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="-Xmx64m" "-Xms64m"
|
||||
|
||||
@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 execute
|
||||
|
||||
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 execute
|
||||
|
||||
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
|
||||
|
||||
: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 %*
|
||||
|
||||
: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
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.bjlx.armcloudaf",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 26
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
||||
maven { url "https://maven.vmos.cn" }
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "ArmCloudAF"
|
||||
include ':app'
|