Skip to content

Commit a6e3691

Browse files
committed
兼容Android 5~16
1 parent d1a747c commit a6e3691

File tree

13 files changed

+256
-43
lines changed

13 files changed

+256
-43
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
### SwissArmyKnife
55

6+
兼容Android5~16
7+
68
SwissArmyKnife 是一款方便调试android UI的工具。可以直接在android设备屏幕上显示控件的相关信息。3.x版本对根View没有任何要求,可以是任意类型的view。
79
3.x需要弹窗权限。由于时间有限,暂未兼容Android5.0以下设备
810

@@ -18,10 +20,8 @@ SwissArmyKnife 是一款方便调试android UI的工具。可以直接在android
1820
}
1921
2022
dependencies {
21-
implementation 'com.github.android-notes.SwissArmyKnife:saklib:3.0.1-alpha1'
22-
23-
24-
}
23+
implementation 'com.github.android-notes.SwissArmyKnife:saklib:4.0.5'
24+
}
2525
```
2626

2727
```
@@ -30,7 +30,7 @@ SwissArmyKnife 是一款方便调试android UI的工具。可以直接在android
3030
3131
```
3232

33-
备注:需要使用 me.weishu.reflection.Reflection.unseal(this);
33+
备注:需要使用 implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:6.1'
3434

3535
### 功能界面
3636

build.gradle

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
google()
77
}
88
dependencies {
9-
classpath "com.android.tools.build:gradle:8.5.2"
9+
classpath "com.android.tools.build:gradle:7.4.2"
1010
// classpath 'com.novoda:bintray-release:0.8.0'
1111
// classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
1212

@@ -19,14 +19,6 @@ buildscript {
1919
}
2020
}
2121

22-
allprojects {
23-
repositories {
24-
mavenCentral()
25-
jcenter()
26-
google()
27-
}
28-
}
29-
3022
task clean(type: Delete) {
3123
delete rootProject.buildDir
3224
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Wed Feb 05 13:11:24 CST 2025
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

saklib/build.gradle

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
apply plugin: 'com.android.library'
2+
apply plugin: 'maven-publish'
23
android {
34
namespace('com.wanjian.sak')
45
compileSdkVersion 33
@@ -26,3 +27,17 @@ dependencies {
2627
implementation 'com.android.support:support-v4:23.0.0'
2728
compileOnly project(path: ':systemlib')
2829
}
30+
31+
publishing {
32+
publications {
33+
release(MavenPublication) {
34+
groupId = 'com.github.android-notes'
35+
artifactId = 'SwissArmyKnife'
36+
version = '4.0.5'
37+
38+
afterEvaluate {
39+
from components.release
40+
}
41+
}
42+
}
43+
}

saklib/src/main/java/com/wanjian/sak/Scaffold.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package com.wanjian.sak;
22

33
import android.app.Application;
4+
import android.os.Build;
45
import android.view.InputEvent;
56
import android.view.View;
67
import android.view.ViewRootImpl;
78

89
import com.wanjian.sak.config.Config;
910
import com.wanjian.sak.layer.LayerRoot;
1011
import com.wanjian.sak.system.input.InputEventListener;
12+
import com.wanjian.sak.system.input.InputEventProcessorCompact;
1113
import com.wanjian.sak.system.input.InputEventReceiverCompact;
1214
import com.wanjian.sak.system.traversals.ViewTraversalsCompact;
1315
import com.wanjian.sak.system.traversals.ViewTraversalsListener;
@@ -52,7 +54,7 @@ public void onRemoveWindow(ViewRootImpl viewRootImpl, View view) {
5254

5355

5456
private void observerInputEvent(Config config, final LayerRoot layerRoot, final ViewRootImpl viewRootImpl, final View rootView) {
55-
InputEventReceiverCompact.get(viewRootImpl, new InputEventListener() {
57+
InputEventListener listener = new InputEventListener() {
5658
@Override
5759
public boolean onBeforeInputEvent(InputEvent inputEvent) {
5860
return layerRoot.beforeInputEvent(rootView, inputEvent);
@@ -62,7 +64,12 @@ public boolean onBeforeInputEvent(InputEvent inputEvent) {
6264
public void onAfterInputEvent(InputEvent inputEvent) {
6365
layerRoot.afterInputEvent(rootView, inputEvent);
6466
}
65-
});
67+
};
68+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
69+
InputEventProcessorCompact.get(rootView.getContext(),viewRootImpl, listener);
70+
} else {
71+
InputEventReceiverCompact.get(viewRootImpl, listener);
72+
}
6673
}
6774

6875
private void observerUIChange(Config config, final LayerRoot layerRoot, ViewRootImpl viewRootImpl, View view) {

saklib/src/main/java/com/wanjian/sak/system/canvas/compact/CanvasCompact.java

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,32 @@
66

77
public abstract class CanvasCompact {
88

9-
protected ViewRootImpl viewRootImpl;
10-
11-
CanvasCompact(ViewRootImpl viewRootImpl) {
12-
this.viewRootImpl = viewRootImpl;
13-
}
14-
15-
public static CanvasCompact get(ViewRootImpl viewRootImpl) {
16-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
17-
return new HardwareCanvasV29Impl(viewRootImpl);
18-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
19-
return new HardwareCanvasV26Impl(viewRootImpl);
20-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
21-
return new HardwareCanvasV24Impl(viewRootImpl);
22-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
23-
return new HardwareCanvasV23Impl(viewRootImpl);
24-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
25-
return new HardwareCanvasV21Impl(viewRootImpl);
26-
} else {
27-
throw new RuntimeException("unsupport android version");
9+
protected ViewRootImpl viewRootImpl;
10+
11+
CanvasCompact(ViewRootImpl viewRootImpl) {
12+
this.viewRootImpl = viewRootImpl;
13+
}
14+
15+
public static CanvasCompact get(ViewRootImpl viewRootImpl) {
16+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
17+
return new HardwareCanvasV31Impl(viewRootImpl);
18+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
19+
return new HardwareCanvasV29Impl(viewRootImpl);
20+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
21+
return new HardwareCanvasV26Impl(viewRootImpl);
22+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
23+
return new HardwareCanvasV24Impl(viewRootImpl);
24+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
25+
return new HardwareCanvasV23Impl(viewRootImpl);
26+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
27+
return new HardwareCanvasV21Impl(viewRootImpl);
28+
} else {
29+
throw new RuntimeException("unsupport android version");
30+
}
2831
}
29-
}
3032

31-
public abstract Canvas requireCanvas();
33+
public abstract Canvas requireCanvas();
3234

33-
public abstract void releaseCanvas();
35+
public abstract void releaseCanvas();
3436

3537
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.wanjian.sak.system.canvas.compact;
2+
3+
import android.graphics.Canvas;
4+
import android.graphics.FrameInfo;
5+
import android.graphics.HardwareRenderer;
6+
import android.graphics.RecordingCanvas;
7+
import android.graphics.RenderNode;
8+
import android.view.Choreographer;
9+
import android.view.ThreadedRenderer;
10+
import android.view.View;
11+
import android.view.ViewFrameInfo;
12+
import android.view.ViewRootImpl;
13+
14+
import java.lang.reflect.Field;
15+
import java.lang.reflect.Method;
16+
17+
class HardwareCanvasV31Impl extends HardwareCanvasV29Impl {
18+
HardwareCanvasV31Impl(ViewRootImpl viewRootImpl) {
19+
super(viewRootImpl);
20+
}
21+
22+
@Override
23+
protected void markDrawStart(Choreographer choreographer) {
24+
try {
25+
ViewFrameInfo frameInfo = getViewFrameInfo();
26+
frameInfo.markDrawStart();
27+
} catch (Exception e) {
28+
throw new RuntimeException(e);
29+
}
30+
}
31+
32+
private ViewFrameInfo getViewFrameInfo() {
33+
try {
34+
Field mFrameInfoF = ViewRootImpl.class.getDeclaredField("mViewFrameInfo");
35+
mFrameInfoF.setAccessible(true);
36+
ViewFrameInfo frameInfo = (ViewFrameInfo) mFrameInfoF.get(viewRootImpl);
37+
return frameInfo;
38+
} catch (Exception e) {
39+
throw new RuntimeException(e);
40+
}
41+
}
42+
43+
private FrameInfo getFrameInfo() {
44+
try {
45+
Method mFrameInfoM = ViewRootImpl.class.getDeclaredMethod("getUpdatedFrameInfo");
46+
mFrameInfoM.setAccessible(true);
47+
FrameInfo frameInfo = (FrameInfo) mFrameInfoM.invoke(viewRootImpl);
48+
return frameInfo;
49+
} catch (Exception e) {
50+
throw new RuntimeException(e);
51+
}
52+
}
53+
54+
@Override
55+
protected void nSyncAndDrawFrame() {
56+
try {
57+
ThreadedRenderer renderer = getHardwareRenderer(viewRootImpl);
58+
Method method = HardwareRenderer.class.getDeclaredMethod("syncAndDrawFrame", FrameInfo.class);
59+
method.setAccessible(true);
60+
method.invoke(renderer, getFrameInfo());
61+
} catch (Exception e) {
62+
throw new RuntimeException(e);
63+
}
64+
}
65+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.wanjian.sak.system.input;
2+
3+
import android.content.Context;
4+
import android.view.InputEvent;
5+
import android.view.InputEventCompatProcessor;
6+
import android.view.ViewRootImpl;
7+
8+
import java.lang.reflect.Field;
9+
import java.util.ArrayList;
10+
import java.util.Arrays;
11+
import java.util.List;
12+
13+
public class InputEventCompatProcessorV30 extends InputEventCompatProcessor {
14+
private InputEventCompatProcessor originInputEventCompatProcessor;
15+
private InputEventListener listener;
16+
17+
public InputEventCompatProcessorV30(Context context, ViewRootImpl viewRootImpl, InputEventListener listener) {
18+
super(context);
19+
this.listener = listener;
20+
try {
21+
Field field = viewRootImpl.getClass().getDeclaredField("mInputCompatProcessor");
22+
field.setAccessible(true);
23+
originInputEventCompatProcessor = (InputEventCompatProcessor) field.get(viewRootImpl);
24+
field.set(viewRootImpl, this);
25+
} catch (Exception e) {
26+
throw new RuntimeException(e);
27+
}
28+
}
29+
30+
@Override
31+
public InputEvent processInputEventBeforeFinish(InputEvent e) {
32+
return originInputEventCompatProcessor.processInputEventBeforeFinish(e);
33+
}
34+
35+
@Override
36+
public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
37+
List<InputEvent> list;
38+
if (listener.onBeforeInputEvent(e)) {
39+
list = new ArrayList<>();
40+
} else {
41+
list = originInputEventCompatProcessor.processInputEventForCompatibility(e);
42+
}
43+
listener.onAfterInputEvent(e);
44+
return list;
45+
}
46+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.wanjian.sak.system.input;
2+
3+
import android.content.Context;
4+
import android.os.Build;
5+
import android.os.Looper;
6+
import android.util.SparseIntArray;
7+
import android.view.InputChannel;
8+
import android.view.InputEventReceiver;
9+
import android.view.ViewRootImpl;
10+
11+
import com.wanjian.sak.unsafe.UnsafeProxy;
12+
13+
import java.lang.reflect.Field;
14+
15+
public abstract class InputEventProcessorCompact {
16+
public static void get(Context context, ViewRootImpl viewRootImpl, InputEventListener listener) {
17+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
18+
new InputEventCompatProcessorV30(context, viewRootImpl, listener);
19+
}
20+
}
21+
}

saklib/src/main/java/com/wanjian/sak/system/traversals/MyTraversalRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
package com.wanjian.sak.system.traversals;
22

3+
import android.util.Log;
34
import android.view.View;
5+
import android.view.ViewRootImpl;
46

57
import java.util.ArrayList;
68
import java.util.List;
79

810
public class MyTraversalRunnable implements Runnable {
11+
private ViewRootImpl $$viewRootImpl;
912
private Runnable originTraversals;
1013
private View rootView;
1114
private List<ViewTraversalsListener> listeners = new ArrayList<>();
1215

1316
public MyTraversalRunnable(Runnable originTraversals, View rootView) {
17+
$$viewRootImpl = (ViewRootImpl) rootView.getRootView().getParent();
1418
this.originTraversals = originTraversals;
1519
this.rootView = rootView;
1620
}
1721

1822
@Override
1923
public void run() {
24+
Log.d("TAG", "run: "+$$viewRootImpl);
2025
notifyBeforeRun();
2126
originTraversals.run();
2227
notifyAfterRun();

settings.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
11
include ':app', ':saklib'
22
include ':systemlib'
3+
4+
dependencyResolutionManagement {
5+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
6+
repositories {
7+
google()
8+
mavenCentral()
9+
maven { url 'https://jitpack.io' }
10+
}
11+
}
Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
1+
2+
13
package android.view;
24

5+
import android.content.Context;
6+
import android.os.Build;
7+
8+
import java.util.ArrayList;
39
import java.util.List;
410

511
public class InputEventCompatProcessor {
6-
public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
7-
return null;
8-
}
9-
}
12+
13+
14+
public InputEventCompatProcessor(Context context) {
15+
16+
}
17+
18+
19+
public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
20+
21+
return null;
22+
}
23+
24+
25+
public InputEvent processInputEventBeforeFinish(InputEvent e) {
26+
// No changes needed
27+
return e;
28+
}
29+
}

0 commit comments

Comments
 (0)