├── .idea ├── .name ├── compiler.xml ├── .gitignore ├── runConfigurations.xml ├── misc.xml ├── jarRepositories.xml └── codeStyles │ └── Project.xml ├── app ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ └── xposed_init │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── longshihan │ │ │ └── uetooltaichi │ │ │ ├── MainActivity.java │ │ │ └── xposed │ │ │ └── UEToolXposed.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── longshihan │ │ │ └── uetooltaichi │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── longshihan │ │ └── uetooltaichi │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── uetool ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable-xxhdpi │ │ │ ├── uet_add.png │ │ │ ├── uet_menu.png │ │ │ ├── uet_minus.png │ │ │ ├── uet_scalpel.png │ │ │ ├── uet_edit_attr.png │ │ │ ├── uet_tree_arrow.png │ │ │ ├── uet_add_pressed.png │ │ │ ├── uet_minus_pressed.png │ │ │ ├── uet_show_gridding.png │ │ │ ├── uet_sub_menu_bg.9.png │ │ │ └── uet_relative_position.png │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── values.xml │ │ ├── values-zh │ │ │ └── strings.xml │ │ ├── values-en │ │ │ └── strings.xml │ │ ├── drawable │ │ │ ├── uet_selector_add.xml │ │ │ ├── uet_selector_minus.xml │ │ │ └── uet_selector_brief_desc.xml │ │ └── layout │ │ │ ├── uet_cell_tree.xml │ │ │ ├── uet_sub_menu_layout.xml │ │ │ ├── uet_menu_layout.xml │ │ │ └── uet_dialog_fragment_list_tree.xml │ │ ├── java │ │ └── me │ │ │ └── ele │ │ │ └── uetool │ │ │ ├── Provider.java │ │ │ ├── attrdialog │ │ │ ├── AttrsDialogItemViewBinder.java │ │ │ ├── binder │ │ │ │ ├── TextItemBinder.java │ │ │ │ ├── TitleItemBinder.java │ │ │ │ ├── BitmapItemBinder.java │ │ │ │ ├── EditTextItemBinder.java │ │ │ │ ├── SwitchItemBinder.java │ │ │ │ ├── AddMinusEditTextItemBinder.java │ │ │ │ └── BriefDescItemBinder.java │ │ │ └── AttrsDialogMultiTypePool.java │ │ │ ├── BoardTextView.java │ │ │ ├── RegionView.java │ │ │ ├── UETSubMenu.java │ │ │ ├── GriddingLayout.java │ │ │ ├── UETCore.java │ │ │ ├── RelativePositionLayout.java │ │ │ ├── MenuHelper.java │ │ │ ├── UETool.java │ │ │ ├── FragmentListTreeDialog.java │ │ │ ├── EditAttrLayout.java │ │ │ ├── UETMenu.java │ │ │ ├── CollectViewsLayout.java │ │ │ └── Util.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── uetool-base ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── me │ │ └── ele │ │ └── uetool │ │ └── base │ │ ├── item │ │ ├── Item.java │ │ ├── TitleItem.java │ │ ├── AddMinusEditItem.java │ │ ├── ElementItem.java │ │ ├── BriefDescItem.java │ │ ├── BitmapItem.java │ │ ├── TextItem.java │ │ ├── SwitchItem.java │ │ └── EditTextItem.java │ │ ├── IAttrs.java │ │ ├── ItemViewBinder.java │ │ ├── Application.java │ │ ├── ItemArrayList.java │ │ ├── DimenUtil.java │ │ ├── Element.java │ │ └── ReflectionP.java ├── build.gradle └── proguard-rules.pro ├── uetool-fresco ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── me │ │ └── ele │ │ └── uetool │ │ └── fresco │ │ └── UETFresco.java ├── build.gradle └── proguard-rules.pro ├── android.keystor ├── Android123.keystore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── image ├── image-20200524214435404.png ├── image-20200524214726294.png ├── image-20200524214837975.png └── image-20200524214959821.png ├── settings.gradle ├── gradle.properties ├── ReadMe.md ├── .gitignore ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | UEToolTaiChi -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /uetool/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /uetool-base/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /uetool-fresco/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.longshihan.uetooltaichi.xposed.UEToolXposed -------------------------------------------------------------------------------- /uetool-base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android.keystor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/android.keystor -------------------------------------------------------------------------------- /uetool-fresco/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Android123.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/Android123.keystore -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | UEToolTaiChi 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /image/image-20200524214435404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/image/image-20200524214435404.png -------------------------------------------------------------------------------- /image/image-20200524214726294.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/image/image-20200524214726294.png -------------------------------------------------------------------------------- /image/image-20200524214837975.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/image/image-20200524214837975.png -------------------------------------------------------------------------------- /image/image-20200524214959821.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/image/image-20200524214959821.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='UEToolTaiChi' 2 | include ':app' 3 | include ':uetool', ':uetool-fresco', ':uetool-base' 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_add.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_menu.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_minus.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_scalpel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_scalpel.png -------------------------------------------------------------------------------- /uetool/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12sp 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_edit_attr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_edit_attr.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_tree_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_tree_arrow.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_add_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_add_pressed.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_minus_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_minus_pressed.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_show_gridding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_show_gridding.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_sub_menu_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_sub_menu_bg.9.png -------------------------------------------------------------------------------- /uetool/src/main/res/drawable-xxhdpi/uet_relative_position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longshihan1/XposedUETool/HEAD/uetool/src/main/res/drawable-xxhdpi/uet_relative_position.png -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/Item.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | public class Item { 4 | 5 | public boolean isValid() { 6 | return true; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/Provider.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.graphics.RectF; 4 | 5 | public interface Provider { 6 | 7 | void onClickTreeItem(RectF rectF); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/IAttrs.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import java.util.List; 4 | 5 | import me.ele.uetool.base.item.Item; 6 | 7 | public interface IAttrs { 8 | 9 | List getAttrs(Element element); 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 23 19:50:37 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /uetool/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 捕捉控件 4 | 相对位置 5 | 网格栅栏 6 | 手术刀 7 | -------------------------------------------------------------------------------- /uetool/src/main/res/values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | catch 4 | location 5 | gridding 6 | scalpel 7 | -------------------------------------------------------------------------------- /uetool/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uetool/src/main/res/drawable/uet_selector_add.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /uetool/src/main/res/drawable/uet_selector_minus.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/TitleItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | public class TitleItem extends Item { 4 | 5 | private String name; 6 | 7 | public TitleItem(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/AddMinusEditItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | import me.ele.uetool.base.Element; 4 | 5 | public class AddMinusEditItem extends EditTextItem { 6 | 7 | public AddMinusEditItem(String name, Element element, int type, String detail) { 8 | super(name, element, type, detail); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/ElementItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | import me.ele.uetool.base.Element; 4 | 5 | public class ElementItem extends TitleItem { 6 | 7 | private Element element; 8 | 9 | public ElementItem(String name, Element element) { 10 | super(name); 11 | this.element = element; 12 | } 13 | 14 | public Element getElement() { 15 | return element; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /uetool-fresco/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion versions.compileSdk 5 | 6 | defaultConfig { 7 | minSdkVersion versions.minSdk 8 | } 9 | 10 | lintOptions { 11 | abortOnError false 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation project(':uetool-base') 17 | // implementation "me.ele:uetool-base:${versions.release}" 18 | implementation "com.facebook.fresco:fresco:${versions.fresco}" 19 | } 20 | -------------------------------------------------------------------------------- /app/src/test/java/com/longshihan/uetooltaichi/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.longshihan.uetooltaichi; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /uetool/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion versions.compileSdk 5 | 6 | defaultConfig { 7 | minSdkVersion versions.minSdk 8 | } 9 | 10 | lintOptions { 11 | abortOnError false 12 | } 13 | } 14 | 15 | dependencies { 16 | api project(':uetool-base') 17 | // api "me.ele:uetool-base:${versions.release}" 18 | implementation 'com.jakewharton.scalpel:scalpel:1.1.2' 19 | implementation 'com.github.bmelnychuk:atv:1.2.9' 20 | } -------------------------------------------------------------------------------- /uetool/src/main/res/drawable/uet_selector_brief_desc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /uetool-base/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion versions.compileSdk 5 | 6 | defaultConfig { 7 | minSdkVersion versions.minSdk 8 | } 9 | 10 | dependencies { 11 | api "com.android.support:appcompat-v7:${versions.supportLibrary}" 12 | api "com.android.support:recyclerview-v7:${versions.supportLibrary}" 13 | api "com.android.support:support-v4:${versions.supportLibrary}" 14 | } 15 | 16 | lintOptions { 17 | abortOnError false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /uetool/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | UETool 4 | could not found view in (%1$.0f , %2$.0f), please select view again 5 | coming soon 6 | DESABLE_UETOOL 7 | catch 8 | location 9 | gridding 10 | scalpel 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/BriefDescItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | import me.ele.uetool.base.Element; 4 | 5 | public class BriefDescItem extends ElementItem { 6 | 7 | private boolean isSelected; 8 | 9 | public BriefDescItem(Element element) { 10 | this(element, false); 11 | } 12 | 13 | public BriefDescItem(Element element, boolean isSelected) { 14 | super("", element); 15 | this.isSelected = isSelected; 16 | } 17 | 18 | public boolean isSelected() { 19 | return isSelected; 20 | } 21 | 22 | @Override 23 | public boolean isValid() { 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/ItemViewBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | /** 10 | * @author: weishenhong contact me. 11 | * @date: 2019-07-08 22:51 12 | */ 13 | public interface ItemViewBinder { 14 | 15 | @NonNull 16 | VH onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter); 17 | 18 | void onBindViewHolder(@NonNull VH holder, @NonNull T item); 19 | 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/BitmapItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | public class BitmapItem extends Item { 6 | 7 | private String name; 8 | private Bitmap bitmap; 9 | 10 | public BitmapItem(String name, Bitmap bitmap) { 11 | this.name = name; 12 | this.bitmap = bitmap; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public Bitmap getBitmap() { 20 | return bitmap; 21 | } 22 | 23 | @Override 24 | public boolean isValid() { 25 | if (bitmap == null) { 26 | return false; 27 | } 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /uetool/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /uetool-base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /uetool-fresco/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/Application.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import android.content.Context; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public class Application { 8 | 9 | private static Context CONTEXT; 10 | 11 | private Application() { 12 | } 13 | 14 | public static Context getApplicationContext() { 15 | if (CONTEXT != null) { 16 | return CONTEXT; 17 | } else { 18 | try { 19 | Class activityThreadClass = Class.forName("android.app.ActivityThread"); 20 | Method method = activityThreadClass.getMethod("currentApplication"); 21 | CONTEXT = (Context) method.invoke(null); 22 | return CONTEXT; 23 | } catch (Exception e) { 24 | return null; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/res/layout/uet_cell_tree.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/longshihan/uetooltaichi/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.longshihan.uetooltaichi; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.longshihan.uetooltaichi", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/AttrsDialogItemViewBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog; 2 | 3 | 4 | import androidx.recyclerview.widget.RecyclerView; 5 | 6 | import me.ele.uetool.AttrsDialog; 7 | import me.ele.uetool.base.ItemViewBinder; 8 | import me.ele.uetool.base.item.Item; 9 | 10 | /** 11 | * @author: weishenhong contact me. 12 | * @date: 2019-07-08 23:37 13 | */ 14 | public abstract class AttrsDialogItemViewBinder> implements ItemViewBinder { 15 | 16 | protected AttrsDialog.AttrDialogCallback getAttrDialogCallback(RecyclerView.Adapter adapter) { 17 | AttrsDialog.AttrDialogCallback callback = null; 18 | if (adapter instanceof AttrsDialog.Adapter) { 19 | callback = ((AttrsDialog.Adapter) adapter).getAttrDialogCallback(); 20 | } 21 | return callback; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /uetool/src/main/res/layout/uet_sub_menu_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 23 | 24 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 25 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/TextItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.TextItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class TextItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.TextViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.TextViewHolder.newInstance(parent); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.TextViewHolder holder, @NonNull TextItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/TitleItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.TitleItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class TitleItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.TitleViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.TitleViewHolder.newInstance(parent); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.TitleViewHolder holder, @NonNull TitleItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/ItemArrayList.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Iterator; 6 | 7 | import me.ele.uetool.base.item.Item; 8 | 9 | public class ItemArrayList extends ArrayList { 10 | 11 | @Override 12 | public boolean add(T t) { 13 | if (!t.isValid()) { 14 | return false; 15 | } 16 | return super.add(t); 17 | } 18 | 19 | @Override 20 | public boolean addAll(Collection c) { 21 | removeInvalidItem(c); 22 | return super.addAll(c); 23 | } 24 | 25 | @Override 26 | public boolean addAll(int index, Collection c) { 27 | removeInvalidItem(c); 28 | return super.addAll(index, c); 29 | } 30 | 31 | private void removeInvalidItem(Collection c) { 32 | Iterator iterator = (Iterator) c.iterator(); 33 | while (iterator.hasNext()) { 34 | T t = iterator.next(); 35 | if (!t.isValid()) { 36 | iterator.remove(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/BitmapItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.BitmapItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class BitmapItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.BitmapInfoViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.BitmapInfoViewHolder.newInstance(parent); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.BitmapInfoViewHolder holder, @NonNull BitmapItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/EditTextItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.EditTextItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class EditTextItemBinder extends AttrsDialogItemViewBinder> { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.EditTextViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.EditTextViewHolder.newInstance(parent); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.EditTextViewHolder holder, @NonNull EditTextItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/SwitchItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.SwitchItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class SwitchItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.SwitchViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.SwitchViewHolder.newInstance(parent, getAttrDialogCallback(adapter)); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.SwitchViewHolder holder, @NonNull SwitchItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/AddMinusEditTextItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.AddMinusEditItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class AddMinusEditTextItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.AddMinusEditViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | return AttrsDialog.Adapter.AddMinusEditViewHolder.newInstance(parent); 22 | } 23 | 24 | @Override 25 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.AddMinusEditViewHolder holder, @NonNull AddMinusEditItem item) { 26 | holder.bindView(item); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/binder/BriefDescItemBinder.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog.binder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import me.ele.uetool.AttrsDialog; 10 | import me.ele.uetool.attrdialog.AttrsDialogItemViewBinder; 11 | import me.ele.uetool.base.item.BriefDescItem; 12 | 13 | /** 14 | * @author: weishenhong contact me. 15 | * @date: 2019-07-08 23:46 16 | */ 17 | public class BriefDescItemBinder extends AttrsDialogItemViewBinder { 18 | @NonNull 19 | @Override 20 | public AttrsDialog.Adapter.BriefDescViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent, RecyclerView.Adapter adapter) { 21 | 22 | return AttrsDialog.Adapter.BriefDescViewHolder.newInstance(parent, getAttrDialogCallback(adapter)); 23 | } 24 | 25 | @Override 26 | public void onBindViewHolder(@NonNull AttrsDialog.Adapter.BriefDescViewHolder holder, @NonNull BriefDescItem item) { 27 | holder.bindView(item); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /uetool/src/main/res/layout/uet_menu_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 21 | 22 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # UETool-Xposed插件版 2 | ## UETool介绍-饿了么提供 3 | UETool 是一个各方人员(设计师、程序员、测试)都可以使用的调试工具。它可以作用于任何显示在屏幕上的 view,比如 Activity/Fragment/Dialog/PopupWindow 等等。 4 | 详细资料请看:[饿了么UETool](https://github.com/eleme/UETool) 5 | 对于平常的开发者来说项目中集成一个框架太过于复杂,特别是这个的主要适用的人群是设计。所以针对这个问题,将其封装成一个插件,对开发和设计都比较友好。 6 | 7 | ## 进阶版-VirtualUETool 8 | 项目地址:https://github.com/zhangke3016/VirtualUETool 9 | 项目介绍:https://www.jianshu.com/p/20bd558fdaf9 10 | 感谢zhangke3016大佬,我实在他项目的基础上实现的,大佬踩了不少坑。 11 | 他的原理是底层使用VirtualAPP实现的,但由于VirtualApp商业化已经好多年了,普通版对于高版本的支持性并不好,因为这个原因,决定开发以Xposed为底层的UETool,可以在免Root的太极阴中使用,对于普通用户来说,使用成本降低了很多。 12 | 13 | ## 超级进阶版-XposedUETool 14 | 项目底层是使用Xposed框架,适用范围:Xposed,EDXposed,太极等依赖Xposed框架的软件。 15 | 16 | 太极:模块管理->勾选相应的软件->打开APP,点击打开UETool,(PS:第一次会请求悬浮权限),出现UE图标就是正常状态了。 17 | 18 | EDXposed/Xposed:安装软件,打开模块->勾选相应的软件->重启手机->打开软件,点击打开UETool,(PS:第一次会请求悬浮权限),出现UE图标就是正常状态了。 19 | 20 | ![](https://github.com/longshihan1/XposedUETool/blob/master/image/image-20200524214435404.png) 21 | 22 | 捕捉功能: 23 | 24 | ![](https://github.com/longshihan1/XposedUETool/blob/master/image/image-20200524214726294.png) 25 | 26 | 相对位置: 27 | 28 | ![](https://github.com/longshihan1/XposedUETool/blob/master/image/image-20200524214837975.png) 29 | 30 | 手术刀(页面层级): 31 | 32 | ![](https://github.com/longshihan1/XposedUETool/blob/master/image/image-20200524214959821.png) 33 | 34 | 我们使用酷安网演示了一下具体的功能的使用。基本满足于开发和设计的需求。 -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/BoardTextView.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.widget.AppCompatTextView; 8 | 9 | import me.ele.uetool.base.DimenUtil; 10 | 11 | public class BoardTextView extends AppCompatTextView { 12 | 13 | private final String defaultInfo = UETool.getInstance().getTargetActivity().getPackageName() + " / " + UETool.getInstance().getTargetActivity().getClass().getName(); 14 | private final int padding = DimenUtil.dip2px(3); 15 | 16 | public BoardTextView(Context context) { 17 | this(context, null); 18 | } 19 | 20 | public BoardTextView(Context context, @Nullable AttributeSet attrs) { 21 | this(context, attrs, 0); 22 | } 23 | 24 | public BoardTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | initView(); 27 | } 28 | 29 | private void initView() { 30 | setBackgroundColor(0x902395ff); 31 | setPadding(padding, padding, padding, padding); 32 | setTextColor(0xffffffff); 33 | setTextSize(9); 34 | setText(defaultInfo); 35 | } 36 | 37 | public void updateInfo(String info) { 38 | setText(info + "\n" + defaultInfo); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/RegionView.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.RectF; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | import me.ele.uetool.base.DimenUtil; 11 | 12 | public class RegionView extends View { 13 | 14 | private RectF rectF; 15 | private Paint paint = new Paint() { 16 | { 17 | setAntiAlias(true); 18 | setColor(Color.YELLOW); 19 | setStrokeWidth(DimenUtil.dip2px(2)); 20 | setStyle(Style.STROKE); 21 | } 22 | }; 23 | 24 | public RegionView(Context context) { 25 | this(context, null); 26 | } 27 | 28 | public RegionView(Context context, AttributeSet attrs) { 29 | this(context, attrs, 0); 30 | } 31 | 32 | public RegionView(Context context, AttributeSet attrs, int defStyleAttr) { 33 | super(context, attrs, defStyleAttr); 34 | } 35 | 36 | public void drawRegion(RectF rectF) { 37 | this.rectF = rectF; 38 | invalidate(); 39 | } 40 | 41 | @Override 42 | protected void onDraw(Canvas canvas) { 43 | super.onDraw(canvas); 44 | if (rectF != null) { 45 | canvas.drawRect(rectF, paint); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/attrdialog/AttrsDialogMultiTypePool.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.attrdialog; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import me.ele.uetool.base.ItemViewBinder; 7 | import me.ele.uetool.base.item.Item; 8 | 9 | /** 10 | * @author: weishenhong contact me. 11 | * @date: 2019-07-08 22:50 12 | */ 13 | public class AttrsDialogMultiTypePool { 14 | 15 | private List classes = new ArrayList<>(); 16 | private List binders = new ArrayList<>(); 17 | 18 | public void register( 19 | Class clazz, 20 | ItemViewBinder binder) { 21 | if (classes.contains(clazz)) { 22 | return; 23 | } 24 | classes.add(clazz); 25 | binders.add(binder); 26 | } 27 | 28 | 29 | public ItemViewBinder getItemViewBinder(int index) { 30 | if (index < 0 || index > binders.size()) { 31 | throw new RuntimeException("un support view holder type:" + index); 32 | } 33 | return binders.get(index); 34 | } 35 | 36 | public int getItemType(Object item) { 37 | int index = classes.indexOf(item.getClass()); 38 | if (index == -1) { 39 | throw new RuntimeException("un support class type:" + item.getClass().getName()); 40 | } 41 | return index; 42 | } 43 | } -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/TextItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | import android.text.TextUtils; 4 | import android.view.View; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | public class TextItem extends TitleItem { 9 | 10 | private String detail; 11 | private boolean enableCopy; 12 | private View.OnClickListener onClickListener; 13 | 14 | public TextItem(String name, String detail) { 15 | this(name, detail, false, null); 16 | } 17 | 18 | public TextItem(String name, String detail, boolean enableCopy) { 19 | this(name, detail, enableCopy, null); 20 | } 21 | 22 | public TextItem(String name, String detail, View.OnClickListener onClickListener) { 23 | this(name, detail, false, onClickListener); 24 | } 25 | 26 | public TextItem(String name, String detail, boolean enableCopy, View.OnClickListener onClickListener) { 27 | super(name); 28 | this.detail = detail; 29 | this.enableCopy = enableCopy; 30 | this.onClickListener = onClickListener; 31 | } 32 | 33 | public String getDetail() { 34 | return detail; 35 | } 36 | 37 | // 是否可复制文案 38 | public boolean isEnableCopy() { 39 | return enableCopy; 40 | } 41 | 42 | @Nullable 43 | public View.OnClickListener getOnClickListener() { 44 | return onClickListener; 45 | } 46 | 47 | @Override 48 | public boolean isValid() { 49 | if (TextUtils.isEmpty(detail)) { 50 | return false; 51 | } 52 | return true; 53 | } 54 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.longshihan.uetooltaichi" 9 | minSdkVersion 21 10 | targetSdkVersion 29 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | signingConfigs { 18 | //你自己的keystore信息 19 | //你自己的keystore信息 20 | release { 21 | storeFile file('../Android123.keystore') 22 | storePassword '123longhe' 23 | keyAlias 'Android' 24 | keyPassword 'longhe' 25 | } 26 | } 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 31 | } 32 | } 33 | 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(dir: 'libs', include: ['*.jar']) 38 | 39 | implementation 'androidx.appcompat:appcompat:1.1.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 41 | testImplementation 'junit:junit:4.12' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 44 | compileOnly 'de.robv.android.xposed:api:82' 45 | implementation project(path: ':uetool') 46 | implementation project(path: ':uetool-fresco') 47 | implementation project(path: ':uetool-base') 48 | } 49 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/SwitchItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | 4 | import androidx.annotation.IntDef; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | 9 | import me.ele.uetool.base.Element; 10 | 11 | import static me.ele.uetool.base.item.SwitchItem.Type.TYPE_IS_BOLD; 12 | import static me.ele.uetool.base.item.SwitchItem.Type.TYPE_MOVE; 13 | import static me.ele.uetool.base.item.SwitchItem.Type.TYPE_SHOW_VALID_VIEWS; 14 | 15 | public class SwitchItem extends ElementItem { 16 | 17 | @Type 18 | private int type; 19 | private boolean isChecked; 20 | 21 | public SwitchItem(String name, Element element, @Type int type) { 22 | super(name, element); 23 | this.type = type; 24 | } 25 | 26 | public SwitchItem(String name, Element element, @Type int type, boolean isChecked) { 27 | super(name, element); 28 | this.type = type; 29 | this.isChecked = isChecked; 30 | } 31 | 32 | public void setChecked(boolean checked) { 33 | isChecked = checked; 34 | } 35 | 36 | public boolean isChecked() { 37 | return isChecked; 38 | } 39 | 40 | public int getType() { 41 | return type; 42 | } 43 | 44 | @IntDef({ 45 | TYPE_IS_BOLD, 46 | TYPE_MOVE, 47 | TYPE_SHOW_VALID_VIEWS, 48 | }) 49 | @Retention(RetentionPolicy.SOURCE) 50 | public @interface Type { 51 | int TYPE_IS_BOLD = 1; 52 | int TYPE_MOVE = 2; 53 | int TYPE_SHOW_VALID_VIEWS = 3; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/DimenUtil.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.util.TypedValue; 6 | 7 | public class DimenUtil { 8 | 9 | private static final Context CONTEXT = Application.getApplicationContext(); 10 | 11 | private DimenUtil() { 12 | 13 | } 14 | 15 | public static String px2dip(float pxValue) { 16 | return px2dip(pxValue, false); 17 | } 18 | 19 | public static String px2dip(float pxValue, boolean withUnit) { 20 | float scale = CONTEXT.getResources().getDisplayMetrics().density; 21 | return (int) (pxValue / scale + 0.5F) + (withUnit ? "dp" : ""); 22 | } 23 | 24 | public static int dip2px(float dpValue) { 25 | float scale = CONTEXT.getResources().getDisplayMetrics().density; 26 | return (int) (dpValue * scale + 0.5F); 27 | } 28 | 29 | public static int sp2px(float sp) { 30 | return (int) TypedValue.applyDimension(2, sp, CONTEXT.getResources().getDisplayMetrics()); 31 | } 32 | 33 | public static String px2sp(float pxValue) { 34 | final float fontScale = CONTEXT.getResources().getDisplayMetrics().scaledDensity; 35 | return String.valueOf((int) (pxValue / fontScale + 0.5f)); 36 | } 37 | 38 | public static int getScreenWidth() { 39 | return CONTEXT.getResources().getDisplayMetrics().widthPixels; 40 | } 41 | 42 | public static int getScreenHeight() { 43 | return CONTEXT.getResources().getDisplayMetrics().heightPixels; 44 | } 45 | 46 | public static int getStatusBarHeight() { 47 | Resources resources = CONTEXT.getResources(); 48 | int resId = resources.getIdentifier("status_bar_height", "dimen", "android"); 49 | return resId > 0 ? resources.getDimensionPixelSize(resId) : 0; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /uetool/src/main/res/layout/uet_dialog_fragment_list_tree.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 20 | 21 | 30 | 31 | 38 | 39 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/item/EditTextItem.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base.item; 2 | 3 | 4 | import androidx.annotation.IntDef; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | 9 | import me.ele.uetool.base.Element; 10 | 11 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_HEIGHT; 12 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_PADDING_BOTTOM; 13 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_PADDING_LEFT; 14 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_PADDING_RIGHT; 15 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_PADDING_TOP; 16 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_TEXT; 17 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_TEXT_COLOR; 18 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_TEXT_SIZE; 19 | import static me.ele.uetool.base.item.EditTextItem.Type.TYPE_WIDTH; 20 | 21 | public class EditTextItem extends ElementItem { 22 | 23 | private @Type 24 | int type; 25 | private String detail; 26 | 27 | public EditTextItem(String name, Element element, @Type int type, String detail) { 28 | super(name, element); 29 | this.type = type; 30 | this.detail = detail; 31 | } 32 | 33 | public String getDetail() { 34 | return detail; 35 | } 36 | 37 | public int getType() { 38 | return type; 39 | } 40 | 41 | @IntDef({ 42 | TYPE_TEXT, 43 | TYPE_TEXT_SIZE, 44 | TYPE_TEXT_COLOR, 45 | TYPE_WIDTH, 46 | TYPE_HEIGHT, 47 | TYPE_PADDING_LEFT, 48 | TYPE_PADDING_RIGHT, 49 | TYPE_PADDING_TOP, 50 | TYPE_PADDING_BOTTOM, 51 | }) 52 | @Retention(RetentionPolicy.SOURCE) 53 | public @interface Type { 54 | int TYPE_TEXT = 1; 55 | int TYPE_TEXT_SIZE = 2; 56 | int TYPE_TEXT_COLOR = 3; 57 | int TYPE_WIDTH = 4; 58 | int TYPE_HEIGHT = 5; 59 | int TYPE_PADDING_LEFT = 6; 60 | int TYPE_PADDING_RIGHT = 7; 61 | int TYPE_PADDING_TOP = 8; 62 | int TYPE_PADDING_BOTTOM = 9; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/UETSubMenu.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Gravity; 6 | import android.widget.ImageView; 7 | import android.widget.LinearLayout; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.Nullable; 11 | 12 | import static me.ele.uetool.base.DimenUtil.dip2px; 13 | 14 | public class UETSubMenu extends LinearLayout { 15 | 16 | private final int padding = dip2px(5); 17 | 18 | private ImageView vImage; 19 | private TextView vTitle; 20 | 21 | public UETSubMenu(Context context) { 22 | this(context, null); 23 | } 24 | 25 | public UETSubMenu(Context context, @Nullable AttributeSet attrs) { 26 | this(context, attrs, 0); 27 | } 28 | 29 | public UETSubMenu(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | inflate(context, R.layout.uet_sub_menu_layout, this); 32 | setGravity(Gravity.CENTER); 33 | setOrientation(VERTICAL); 34 | setPadding(padding, 0, padding, 0); 35 | setTranslationY(dip2px(2)); 36 | vImage = findViewById(R.id.image); 37 | vTitle = findViewById(R.id.title); 38 | } 39 | 40 | public void update(SubMenu subMenu) { 41 | vImage.setImageResource(subMenu.getImageRes()); 42 | vTitle.setText(subMenu.getTitle()); 43 | setOnClickListener(subMenu.getOnClickListener()); 44 | } 45 | 46 | public static class SubMenu { 47 | private String title; 48 | private int imageRes; 49 | private OnClickListener onClickListener; 50 | 51 | public SubMenu(String title, int imageRes, OnClickListener onClickListener) { 52 | this.title = title; 53 | this.imageRes = imageRes; 54 | this.onClickListener = onClickListener; 55 | } 56 | 57 | public String getTitle() { 58 | return title; 59 | } 60 | 61 | public int getImageRes() { 62 | return imageRes; 63 | } 64 | 65 | public OnClickListener getOnClickListener() { 66 | return onClickListener; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/Element.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import android.graphics.Rect; 4 | import android.os.Build; 5 | import android.view.View; 6 | 7 | public class Element { 8 | 9 | private View view; 10 | private Rect originRect = new Rect(); 11 | private Rect rect = new Rect(); 12 | private int[] location = new int[2]; 13 | private Element parentElement; 14 | 15 | public Element(View view) { 16 | this.view = view; 17 | reset(); 18 | originRect.set(rect.left, rect.top, rect.right, rect.bottom); 19 | } 20 | 21 | public View getView() { 22 | return view; 23 | } 24 | 25 | public Rect getRect() { 26 | return rect; 27 | } 28 | 29 | public Rect getOriginRect() { 30 | return originRect; 31 | } 32 | 33 | public void reset() { 34 | view.getLocationOnScreen(location); 35 | int width = view.getWidth(); 36 | int height = view.getHeight(); 37 | 38 | int left = location[0]; 39 | int right = left + width; 40 | int top = location[1]; 41 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 42 | top -= DimenUtil.getStatusBarHeight(); 43 | } 44 | int bottom = top + height; 45 | 46 | rect.set(left, top, right, bottom); 47 | } 48 | 49 | public Element getParentElement() { 50 | if (parentElement == null) { 51 | Object parentView = view.getParent(); 52 | if (parentView instanceof View) { 53 | parentElement = new Element((View) parentView); 54 | } 55 | } 56 | return parentElement; 57 | } 58 | 59 | // view 的面积 60 | public int getArea() { 61 | return view.getWidth() * view.getHeight(); 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) { 66 | if (this == o) return true; 67 | if (o == null || getClass() != o.getClass()) return false; 68 | 69 | Element element = (Element) o; 70 | 71 | return view != null ? view.equals(element.view) : element.view == null; 72 | 73 | } 74 | 75 | @Override 76 | public int hashCode() { 77 | return view != null ? view.hashCode() : 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/GriddingLayout.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | 11 | import androidx.annotation.Nullable; 12 | 13 | import static me.ele.uetool.base.DimenUtil.dip2px; 14 | import static me.ele.uetool.base.DimenUtil.getScreenHeight; 15 | import static me.ele.uetool.base.DimenUtil.getScreenWidth; 16 | 17 | public class GriddingLayout extends View { 18 | 19 | public static final int LINE_INTERVAL = dip2px(5); 20 | private final int screenWidth = getScreenWidth(); 21 | private final int screenHeight = getScreenHeight(); 22 | 23 | private Paint paint = new Paint() { 24 | { 25 | setAntiAlias(true); 26 | setColor(0x30000000); 27 | setStrokeWidth(1); 28 | } 29 | }; 30 | 31 | private Activity bindActivity = UETool.getInstance().getTargetActivity(); 32 | 33 | public GriddingLayout(Context context) { 34 | super(context); 35 | } 36 | 37 | public GriddingLayout(Context context, @Nullable AttributeSet attrs) { 38 | super(context, attrs); 39 | } 40 | 41 | public GriddingLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 42 | super(context, attrs, defStyleAttr); 43 | } 44 | 45 | @Override 46 | protected void onDraw(Canvas canvas) { 47 | super.onDraw(canvas); 48 | int startX = 0; 49 | while (startX < screenWidth) { 50 | canvas.drawLine(startX, 0, startX, screenHeight, paint); 51 | startX = startX + LINE_INTERVAL; 52 | } 53 | 54 | int startY = 0; 55 | while (startY < screenHeight) { 56 | canvas.drawLine(0, startY, screenWidth, startY, paint); 57 | startY = startY + LINE_INTERVAL; 58 | } 59 | } 60 | 61 | @Override 62 | public boolean dispatchTouchEvent(MotionEvent event) { 63 | bindActivity.dispatchTouchEvent(event); 64 | return super.dispatchTouchEvent(event); 65 | } 66 | 67 | @Override 68 | public boolean onTouchEvent(MotionEvent event) { 69 | return true; 70 | } 71 | 72 | @Override 73 | protected void onDetachedFromWindow() { 74 | super.onDetachedFromWindow(); 75 | bindActivity = null; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /uetool-base/src/main/java/me/ele/uetool/base/ReflectionP.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.base; 2 | 3 | import android.os.Build; 4 | import android.util.Log; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 来自 weishu FreeReflection 10 | * https://github.com/tiann/FreeReflection 11 | */ 12 | public class ReflectionP { 13 | 14 | private static final String TAG = "Reflection"; 15 | 16 | private static Object sVmRuntime; 17 | private static Method setHiddenApiExemptions; 18 | 19 | static { 20 | try { 21 | Method forName = Class.class.getDeclaredMethod("forName", String.class); 22 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class); 23 | 24 | Class vmRuntimeClass = (Class) forName.invoke(null, "dalvik.system.VMRuntime"); 25 | Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null); 26 | setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class}); 27 | sVmRuntime = getRuntime.invoke(null); 28 | } catch (Throwable e) { 29 | Log.e(TAG, "reflect bootstrap failed:", e); 30 | } 31 | } 32 | 33 | public static T breakAndroidP(Func func) { 34 | T result; 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 36 | exemptAll(); 37 | result = func.call(); 38 | } else { 39 | result = func.call(); 40 | } 41 | return result; 42 | } 43 | 44 | /** 45 | * make specific methods exempted from hidden API check. 46 | * 47 | * @param methods the method signature prefix, such as "Ldalvik/system", "Landroid" or even "L" 48 | * @return true if success 49 | */ 50 | private static boolean exempt(String... methods) { 51 | if (sVmRuntime == null || setHiddenApiExemptions == null) { 52 | return false; 53 | } 54 | 55 | try { 56 | setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods}); 57 | return true; 58 | } catch (Throwable e) { 59 | return false; 60 | } 61 | } 62 | 63 | /** 64 | * Make all hidden API exempted. 65 | * 66 | * @return true if success. 67 | */ 68 | private static boolean exemptAll() { 69 | return exempt(new String[]{"L"}); 70 | } 71 | 72 | public interface Func { 73 | T call(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 32 | 33 | 45 | 46 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/longshihan/uetooltaichi/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.longshihan.uetooltaichi; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.TextView; 13 | 14 | import com.longshihan.uetooltaichi.xposed.UEToolXposed; 15 | 16 | import me.ele.uetool.MenuHelper; 17 | import me.ele.uetool.UETMenu; 18 | import me.ele.uetool.UETool; 19 | 20 | public class MainActivity extends AppCompatActivity { 21 | public static final String TAG = "UEToolXposed"; 22 | TextView open,close,openSSL,closeSSL; 23 | public static boolean isOpenSSL=false; 24 | private static final String ReceiveACTION="com.longshihan.uetooltaichi.xposed.receive"; 25 | public static final String ACTION="com.longshihan.uetooltaichi.xposed"; 26 | private static final String ACTIONTYPE_Receive="Receive_type"; 27 | private ReceiveBroadcastReceiver receiveBroadcastReceiver; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_main); 33 | open = findViewById(R.id.open); 34 | close=findViewById(R.id.close); 35 | 36 | open.setOnClickListener(new View.OnClickListener() { 37 | @Override 38 | public void onClick(View v) { 39 | UETool.showUETMenu(); 40 | } 41 | }); 42 | close.setOnClickListener(new View.OnClickListener(){ 43 | @Override 44 | public void onClick(View v) { 45 | UETool.dismissUETMenu(); 46 | } 47 | }); 48 | initSSL(); 49 | } 50 | 51 | private void initSSL() { 52 | try { 53 | stop(); 54 | IntentFilter intentFilter = new IntentFilter(ReceiveACTION); 55 | receiveBroadcastReceiver = new ReceiveBroadcastReceiver(); 56 | registerReceiver(receiveBroadcastReceiver, intentFilter); 57 | }catch (Exception e){ 58 | e.printStackTrace(); 59 | } 60 | 61 | } 62 | 63 | public void stop(){ 64 | try{ 65 | if (receiveBroadcastReceiver!=null){ 66 | unregisterReceiver(receiveBroadcastReceiver); 67 | } 68 | }catch (Exception e){ 69 | e.printStackTrace(); 70 | } 71 | } 72 | 73 | 74 | static class ReceiveBroadcastReceiver extends BroadcastReceiver { 75 | @Override 76 | public void onReceive(Context context, Intent intent1) { 77 | try { 78 | }catch (Exception e){ 79 | Log.d(TAG,":"+e.getMessage()); 80 | e.printStackTrace(); 81 | } 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /app/src/main/java/com/longshihan/uetooltaichi/xposed/UEToolXposed.java: -------------------------------------------------------------------------------- 1 | package com.longshihan.uetooltaichi.xposed; 2 | 3 | import android.app.AndroidAppHelper; 4 | import android.app.Application; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.util.Log; 10 | 11 | import de.robv.android.xposed.IXposedHookLoadPackage; 12 | import de.robv.android.xposed.XC_MethodHook; 13 | import de.robv.android.xposed.XposedBridge; 14 | import de.robv.android.xposed.XposedHelpers; 15 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 16 | import me.ele.uetool.MenuHelper; 17 | import me.ele.uetool.UETMenu; 18 | 19 | public class UEToolXposed implements IXposedHookLoadPackage { 20 | private static final String TAG = "UEToolXposed"; 21 | private static final String ACTION = "com.longshihan.uetooltaichi.xposed"; 22 | private Context context; 23 | private MyBroadcastReceiver myBroadcastReceiver = null; 24 | public static String currentPackageName; 25 | private static XC_LoadPackage.LoadPackageParam loadPackageParam; 26 | private static final String ReceiveACTION="com.longshihan.uetooltaichi.xposed.receive"; 27 | private static final String ACTIONTYPE_Receive="Receive_type"; 28 | @Override 29 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { 30 | currentPackageName = lpparam.packageName; 31 | loadPackageParam = lpparam; 32 | XposedBridge.log(TAG + " >> current package:" + lpparam.packageName); 33 | try { 34 | XposedHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() { 35 | @Override 36 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 37 | XposedBridge.log(TAG + " >> onCreate:" + param.args.length); 38 | context = AndroidAppHelper.currentApplication().getApplicationContext(); 39 | startWatch(); 40 | } 41 | }); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | 48 | 49 | private void startWatch() { 50 | if (myBroadcastReceiver != null) { 51 | stopWatch(); 52 | } 53 | Log.d(TAG, ":" + ":启动UETool广播"); 54 | IntentFilter intentFilter = new IntentFilter(ACTION); // 设置广播接收器的信息过滤器, 55 | myBroadcastReceiver = new MyBroadcastReceiver(); 56 | // 在代码中动态注册广播接收器,intentFilter为这个广播接收器能接收到的广播信息的动作类型,用于过滤广播信息 57 | context.registerReceiver(myBroadcastReceiver, intentFilter); 58 | } 59 | 60 | private void stopWatch() { 61 | try { 62 | if (myBroadcastReceiver != null) { 63 | context.unregisterReceiver(myBroadcastReceiver); 64 | myBroadcastReceiver = null; 65 | } 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | static class MyBroadcastReceiver extends BroadcastReceiver { 72 | @Override 73 | public void onReceive(Context context, Intent intent1) { 74 | try { 75 | int type = intent1.getIntExtra("type", MenuHelper.Type.TYPE_UNKNOWN); 76 | if (type == MenuHelper.Type.TYPE_EDIT_ATTR 77 | || type == MenuHelper.Type.TYPE_LAYOUT_LEVEL 78 | || type == MenuHelper.Type.TYPE_RELATIVE_POSITION 79 | || type == MenuHelper.Type.TYPE_SHOW_GRIDDING 80 | || type == MenuHelper.Type.TYPE_UNKNOWN) { 81 | Log.d(TAG, "type:UETool:" + type); 82 | UETMenu.open(type); 83 | } 84 | } catch (Exception e) { 85 | Log.d(TAG, ":" + e.getMessage()); 86 | e.printStackTrace(); 87 | } 88 | } 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/UETCore.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.util.Pair; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | import me.ele.uetool.base.Element; 11 | import me.ele.uetool.base.IAttrs; 12 | import me.ele.uetool.base.item.*; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import static me.ele.uetool.base.DimenUtil.px2dip; 18 | import static me.ele.uetool.base.DimenUtil.px2sp; 19 | 20 | public class UETCore implements IAttrs { 21 | 22 | @Override 23 | public List getAttrs(Element element) { 24 | List items = new ArrayList<>(); 25 | 26 | View view = element.getView(); 27 | 28 | items.add(new TextItem("Fragment", Util.getCurrentFragmentName(element.getView()), new View.OnClickListener() { 29 | @Override 30 | public void onClick(View v) { 31 | new FragmentListTreeDialog(v.getContext()).show(); 32 | } 33 | })); 34 | items.add(new TextItem("ViewHolder", Util.getViewHolderName(element.getView()))); 35 | items.add(new SwitchItem("Move", element, SwitchItem.Type.TYPE_MOVE)); 36 | items.add(new SwitchItem("ValidViews", element, SwitchItem.Type.TYPE_SHOW_VALID_VIEWS)); 37 | 38 | IAttrs iAttrs = AttrsManager.createAttrs(view); 39 | if (iAttrs != null) { 40 | items.addAll(iAttrs.getAttrs(element)); 41 | } 42 | 43 | items.add(new TitleItem("COMMON")); 44 | items.add(new TextItem("Class", view.getClass().getName())); 45 | items.add(new TextItem("Id", Util.getResId(view))); 46 | items.add(new TextItem("ResName", Util.getResourceName(view.getId()))); 47 | items.add(new TextItem("Clickable", Boolean.toString(view.isClickable()).toUpperCase())); 48 | items.add(new TextItem("OnClickListener", Util.getViewClickListener(view))); 49 | items.add(new TextItem("Focused", Boolean.toString(view.isFocused()).toUpperCase())); 50 | items.add(new AddMinusEditItem("Width(dp)", element, EditTextItem.Type.TYPE_WIDTH, px2dip(view.getWidth()))); 51 | items.add(new AddMinusEditItem("Height(dp)", element, EditTextItem.Type.TYPE_HEIGHT, px2dip(view.getHeight()))); 52 | items.add(new TextItem("Alpha", String.valueOf(view.getAlpha()))); 53 | Object background = Util.getBackground(view); 54 | if (background instanceof String) { 55 | items.add(new TextItem("Background", (String) background)); 56 | } else if (background instanceof Bitmap) { 57 | items.add(new BitmapItem("Background", (Bitmap) background)); 58 | } 59 | items.add(new AddMinusEditItem("PaddingLeft(dp)", element, EditTextItem.Type.TYPE_PADDING_LEFT, px2dip(view.getPaddingLeft()))); 60 | items.add(new AddMinusEditItem("PaddingRight(dp)", element, EditTextItem.Type.TYPE_PADDING_RIGHT, px2dip(view.getPaddingRight()))); 61 | items.add(new AddMinusEditItem("PaddingTop(dp)", element, EditTextItem.Type.TYPE_PADDING_TOP, px2dip(view.getPaddingTop()))); 62 | items.add(new AddMinusEditItem("PaddingBottom(dp)", element, EditTextItem.Type.TYPE_PADDING_BOTTOM, px2dip(view.getPaddingBottom()))); 63 | 64 | return items; 65 | } 66 | 67 | static class AttrsManager { 68 | 69 | public static IAttrs createAttrs(View view) { 70 | if (view instanceof TextView) { 71 | return new UETTextView(); 72 | } else if (view instanceof ImageView) { 73 | return new UETImageView(); 74 | } 75 | return null; 76 | } 77 | } 78 | 79 | static class UETTextView implements IAttrs { 80 | 81 | @Override 82 | public List getAttrs(Element element) { 83 | List items = new ArrayList<>(); 84 | TextView textView = ((TextView) element.getView()); 85 | items.add(new TitleItem("TextView")); 86 | items.add(new EditTextItem("Text", element, EditTextItem.Type.TYPE_TEXT, textView.getText().toString())); 87 | items.add(new AddMinusEditItem("TextSize(sp)", element, EditTextItem.Type.TYPE_TEXT_SIZE, px2sp(textView.getTextSize()))); 88 | items.add(new EditTextItem("TextColor", element, EditTextItem.Type.TYPE_TEXT_COLOR, Util.intToHexColor(textView.getCurrentTextColor()))); 89 | List> pairs = Util.getTextViewBitmap(textView); 90 | for (Pair pair : pairs) { 91 | items.add(new BitmapItem(pair.first, pair.second)); 92 | } 93 | items.add(new SwitchItem("IsBold", element, SwitchItem.Type.TYPE_IS_BOLD, textView.getTypeface() != null ? textView.getTypeface().isBold() : false)); 94 | return items; 95 | } 96 | } 97 | 98 | static class UETImageView implements IAttrs { 99 | 100 | @Override 101 | public List getAttrs(Element element) { 102 | List items = new ArrayList<>(); 103 | ImageView imageView = ((ImageView) element.getView()); 104 | items.add(new TitleItem("ImageView")); 105 | items.add(new BitmapItem("Bitmap", Util.getImageViewBitmap(imageView))); 106 | items.add(new TextItem("ScaleType", Util.getImageViewScaleType(imageView))); 107 | return items; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/RelativePositionLayout.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | 11 | import androidx.annotation.Nullable; 12 | 13 | import me.ele.uetool.base.Element; 14 | 15 | import static me.ele.uetool.base.DimenUtil.dip2px; 16 | 17 | public class RelativePositionLayout extends CollectViewsLayout { 18 | 19 | private final int elementsNum = 2; 20 | private Paint areaPaint = new Paint() { 21 | { 22 | setAntiAlias(true); 23 | setColor(Color.RED); 24 | setStyle(Style.STROKE); 25 | setStrokeWidth(dip2px(1)); 26 | } 27 | }; 28 | 29 | private Element[] relativeElements = new Element[elementsNum]; 30 | private int searchCount = 0; 31 | 32 | public RelativePositionLayout(Context context) { 33 | this(context, null); 34 | } 35 | 36 | public RelativePositionLayout(Context context, @Nullable AttributeSet attrs) { 37 | this(context, attrs, 0); 38 | } 39 | 40 | public RelativePositionLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | setLayerType(LAYER_TYPE_SOFTWARE, null); 43 | } 44 | 45 | @Override 46 | public boolean onTouchEvent(MotionEvent event) { 47 | switch (event.getAction()) { 48 | case MotionEvent.ACTION_DOWN: 49 | break; 50 | case MotionEvent.ACTION_UP: 51 | 52 | final Element element = getTargetElement(event.getX(), event.getY()); 53 | if (element != null) { 54 | relativeElements[searchCount % elementsNum] = element; 55 | searchCount++; 56 | invalidate(); 57 | } 58 | break; 59 | } 60 | return true; 61 | } 62 | 63 | @Override 64 | protected void onDraw(Canvas canvas) { 65 | super.onDraw(canvas); 66 | 67 | if (relativeElements == null) { 68 | return; 69 | } 70 | 71 | boolean doubleNotNull = true; 72 | for (Element element : relativeElements) { 73 | if (element != null) { 74 | Rect rect = element.getRect(); 75 | canvas.drawLine(0, rect.top, screenWidth, rect.top, dashLinePaint); 76 | canvas.drawLine(0, rect.bottom, screenWidth, rect.bottom, dashLinePaint); 77 | canvas.drawLine(rect.left, 0, rect.left, screenHeight, dashLinePaint); 78 | canvas.drawLine(rect.right, 0, rect.right, screenHeight, dashLinePaint); 79 | canvas.drawRect(rect, areaPaint); 80 | } else { 81 | doubleNotNull = false; 82 | } 83 | } 84 | 85 | if (doubleNotNull) { 86 | Rect firstRect = relativeElements[searchCount % elementsNum].getRect(); 87 | Rect secondRect = relativeElements[(searchCount - 1) % elementsNum].getRect(); 88 | 89 | if (secondRect.top > firstRect.bottom) { 90 | int x = secondRect.left + secondRect.width() / 2; 91 | drawLineWithText(canvas, x, firstRect.bottom, x, secondRect.top); 92 | } 93 | 94 | if (firstRect.top > secondRect.bottom) { 95 | int x = secondRect.left + secondRect.width() / 2; 96 | drawLineWithText(canvas, x, secondRect.bottom, x, firstRect.top); 97 | } 98 | 99 | if (secondRect.left > firstRect.right) { 100 | int y = secondRect.top + secondRect.height() / 2; 101 | drawLineWithText(canvas, secondRect.left, y, firstRect.right, y); 102 | } 103 | 104 | if (firstRect.left > secondRect.right) { 105 | int y = secondRect.top + secondRect.height() / 2; 106 | drawLineWithText(canvas, secondRect.right, y, firstRect.left, y); 107 | } 108 | 109 | drawNestedAreaLine(canvas, firstRect, secondRect); 110 | drawNestedAreaLine(canvas, secondRect, firstRect); 111 | } 112 | } 113 | 114 | private void drawNestedAreaLine(Canvas canvas, Rect firstRect, Rect secondRect) { 115 | if (secondRect.left >= firstRect.left && secondRect.right <= firstRect.right && secondRect.top >= firstRect.top && secondRect.bottom <= firstRect.bottom) { 116 | 117 | drawLineWithText(canvas, secondRect.left, secondRect.top + secondRect.height() / 2, 118 | firstRect.left, secondRect.top + secondRect.height() / 2); 119 | 120 | drawLineWithText(canvas, secondRect.right, secondRect.top + secondRect.height() / 2, 121 | firstRect.right, secondRect.top + secondRect.height() / 2); 122 | 123 | drawLineWithText(canvas, secondRect.left + secondRect.width() / 2, secondRect.top, 124 | secondRect.left + secondRect.width() / 2, firstRect.top); 125 | 126 | drawLineWithText(canvas, secondRect.left + secondRect.width() / 2, secondRect.bottom, 127 | secondRect.left + secondRect.width() / 2, firstRect.bottom); 128 | } 129 | } 130 | 131 | @Override 132 | protected void onDetachedFromWindow() { 133 | super.onDetachedFromWindow(); 134 | relativeElements = null; 135 | } 136 | 137 | @Override 138 | protected void drawLineWithText(Canvas canvas, int startX, int startY, int endX, int endY) { 139 | drawLineWithText(canvas, startX, startY, endX, endY, dip2px(2)); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/MenuHelper.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.FrameLayout; 9 | import android.widget.Toast; 10 | 11 | import androidx.annotation.IntDef; 12 | 13 | import com.jakewharton.scalpel.ScalpelFrameLayout; 14 | 15 | import java.lang.annotation.Retention; 16 | import java.lang.annotation.RetentionPolicy; 17 | 18 | import me.ele.uetool.base.DimenUtil; 19 | 20 | import static android.view.Gravity.BOTTOM; 21 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 22 | import static me.ele.uetool.MenuHelper.Type.TYPE_LAYOUT_LEVEL; 23 | import static me.ele.uetool.MenuHelper.Type.TYPE_UNKNOWN; 24 | 25 | public class MenuHelper { 26 | public static final String TAG = "MenuHelper"; 27 | 28 | public static final String EXTRA_TYPE = "x_uetool_extra_type"; 29 | 30 | private static final String EXTRA_TYPE_LEVEL = "x_uetool_extra_type_level"; 31 | 32 | public static void show(Activity activity, Bundle bundle) { 33 | if (bundle == null) { 34 | return; 35 | } 36 | Log.d(TAG, " show "); 37 | FrameLayout frameLayout = new FrameLayout(activity); 38 | ViewGroup vContainer = frameLayout; 39 | final BoardTextView board = new BoardTextView(activity); 40 | int type = bundle.getInt(EXTRA_TYPE, TYPE_UNKNOWN); 41 | 42 | switch (type) { 43 | case Type.TYPE_EDIT_ATTR: 44 | EditAttrLayout editAttrLayout = new EditAttrLayout(activity); 45 | editAttrLayout.setOnDragListener(new EditAttrLayout.OnDragListener() { 46 | @Override 47 | public void showOffset(String offsetContent) { 48 | board.updateInfo(offsetContent); 49 | } 50 | }); 51 | vContainer.addView(editAttrLayout); 52 | break; 53 | case Type.TYPE_RELATIVE_POSITION: 54 | vContainer.addView(new RelativePositionLayout(activity)); 55 | break; 56 | case Type.TYPE_SHOW_GRIDDING: 57 | vContainer.addView(new GriddingLayout(activity)); 58 | board.updateInfo("LINE_INTERVAL: " + DimenUtil.px2dip(GriddingLayout.LINE_INTERVAL, true)); 59 | break; 60 | case TYPE_LAYOUT_LEVEL: 61 | break; 62 | default: 63 | Toast.makeText(activity, "fail ---", Toast.LENGTH_SHORT).show(); 64 | break; 65 | } 66 | 67 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); 68 | params.gravity = BOTTOM; 69 | vContainer.addView(board, params); 70 | 71 | View view = Util.getCurrentView(activity); 72 | ViewGroup viewGroup = null; 73 | if (view instanceof ViewGroup){ 74 | viewGroup = (ViewGroup) view; 75 | } 76 | if (viewGroup != null && viewGroup.getChildCount() > 0){ 77 | if (type == TYPE_LAYOUT_LEVEL){ 78 | ScalpelFrameLayout scalpelFrameLayout = new ScalpelFrameLayout(activity); 79 | View v = viewGroup.getChildAt(0); 80 | viewGroup.removeView(v); 81 | 82 | scalpelFrameLayout.addView(v); 83 | scalpelFrameLayout.setTag(EXTRA_TYPE_LEVEL); 84 | vContainer.addView(scalpelFrameLayout); 85 | scalpelFrameLayout.setLayerInteractionEnabled(true); 86 | scalpelFrameLayout.setDrawViews(true); 87 | scalpelFrameLayout.setDrawIds(true); 88 | } 89 | View viewWithTag = viewGroup.findViewWithTag(EXTRA_TYPE); 90 | View viewWithTagLevel = viewGroup.findViewWithTag(EXTRA_TYPE_LEVEL); 91 | if (viewWithTag != null){ 92 | viewGroup.removeView(viewWithTag); 93 | } 94 | if (viewWithTagLevel != null){ 95 | viewGroup.removeView(viewWithTagLevel); 96 | } 97 | vContainer.setTag(EXTRA_TYPE); 98 | vContainer.setFocusable(false); 99 | vContainer.setFocusableInTouchMode(false); 100 | Log.d(TAG, " addView "); 101 | viewGroup.addView(vContainer,new ViewGroup.LayoutParams(viewGroup.getWidth(),viewGroup.getHeight())); 102 | viewGroup.postInvalidate(); 103 | } 104 | } 105 | 106 | public static boolean dismiss(Activity activity) { 107 | View view = Util.getCurrentView(activity); 108 | ViewGroup viewGroup = null; 109 | if (view instanceof ViewGroup){ 110 | viewGroup = (ViewGroup) view; 111 | } 112 | if (viewGroup != null){ 113 | View viewWithTag = viewGroup.findViewWithTag(EXTRA_TYPE); 114 | ViewGroup viewWithTagLevel = viewGroup.findViewWithTag(EXTRA_TYPE_LEVEL); 115 | if (viewWithTagLevel != null){ 116 | View child = viewWithTagLevel.getChildAt(0); 117 | if (child != null) { 118 | viewWithTagLevel.removeView(child); 119 | ViewGroup vg = (ViewGroup) Util.getCurrentView(activity); 120 | vg.addView(child,0); 121 | } 122 | } 123 | if (viewWithTag != null){ 124 | Log.d(TAG, " removeView "); 125 | viewGroup.removeView(viewWithTag); 126 | return true; 127 | } 128 | } 129 | return false; 130 | } 131 | 132 | @IntDef({ 133 | TYPE_UNKNOWN, 134 | Type.TYPE_EDIT_ATTR, 135 | Type.TYPE_SHOW_GRIDDING, 136 | Type.TYPE_RELATIVE_POSITION, 137 | TYPE_LAYOUT_LEVEL 138 | }) 139 | @Retention(RetentionPolicy.SOURCE) 140 | public @interface Type { 141 | int TYPE_UNKNOWN = -1; 142 | int TYPE_EDIT_ATTR = 1; 143 | int TYPE_SHOW_GRIDDING = 2; 144 | int TYPE_RELATIVE_POSITION = 3; 145 | int TYPE_LAYOUT_LEVEL = 4; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/UETool.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.provider.Settings; 10 | import android.widget.Toast; 11 | 12 | import me.ele.uetool.attrdialog.AttrsDialogMultiTypePool; 13 | import me.ele.uetool.attrdialog.binder.AddMinusEditTextItemBinder; 14 | import me.ele.uetool.attrdialog.binder.BitmapItemBinder; 15 | import me.ele.uetool.attrdialog.binder.BriefDescItemBinder; 16 | import me.ele.uetool.attrdialog.binder.EditTextItemBinder; 17 | import me.ele.uetool.attrdialog.binder.SwitchItemBinder; 18 | import me.ele.uetool.attrdialog.binder.TextItemBinder; 19 | import me.ele.uetool.attrdialog.binder.TitleItemBinder; 20 | import me.ele.uetool.base.Application; 21 | import me.ele.uetool.base.ItemViewBinder; 22 | import me.ele.uetool.base.item.AddMinusEditItem; 23 | import me.ele.uetool.base.item.BitmapItem; 24 | import me.ele.uetool.base.item.BriefDescItem; 25 | import me.ele.uetool.base.item.EditTextItem; 26 | import me.ele.uetool.base.item.Item; 27 | import me.ele.uetool.base.item.SwitchItem; 28 | import me.ele.uetool.base.item.TextItem; 29 | import me.ele.uetool.base.item.TitleItem; 30 | 31 | import java.util.HashSet; 32 | import java.util.LinkedHashSet; 33 | import java.util.Set; 34 | 35 | public class UETool { 36 | 37 | private static volatile UETool instance; 38 | private Set filterClassesSet = new HashSet<>(); 39 | private Set attrsProviderSet = new LinkedHashSet() { 40 | { 41 | add(UETCore.class.getName()); 42 | add("me.ele.uetool.fresco.UETFresco"); 43 | } 44 | }; 45 | public UETMenu uetMenu; 46 | private AttrsDialogMultiTypePool attrsDialogMultiTypePool = new AttrsDialogMultiTypePool(); 47 | 48 | private UETool() { 49 | initAttrsDialogMultiTypePool(); 50 | } 51 | 52 | public static UETool getInstance() { 53 | if (instance == null) { 54 | synchronized (UETool.class) { 55 | if (instance == null) { 56 | instance = new UETool(); 57 | } 58 | } 59 | } 60 | return instance; 61 | } 62 | 63 | public static void putFilterClass(Class clazz) { 64 | putFilterClass(clazz.getName()); 65 | } 66 | 67 | public static void putFilterClass(String className) { 68 | getInstance().putFilterClassName(className); 69 | } 70 | 71 | public static void registerAttrDialogItemViewBinder(Class clazz, ItemViewBinder binder) { 72 | getInstance().attrsDialogMultiTypePool.register(clazz, binder); 73 | } 74 | 75 | public static void putAttrsProviderClass(Class clazz) { 76 | putAttrsProviderClass(clazz.getName()); 77 | } 78 | 79 | public static void putAttrsProviderClass(String className) { 80 | getInstance().putAttrsProviderClassName(className); 81 | } 82 | 83 | public static boolean showUETMenu() { 84 | return getInstance().showMenu(); 85 | } 86 | 87 | public static boolean showUETMenu(int y) { 88 | return getInstance().showMenu(y); 89 | } 90 | 91 | public static int dismissUETMenu() { 92 | return getInstance().dismissMenu(); 93 | } 94 | 95 | private void putFilterClassName(String className) { 96 | filterClassesSet.add(className); 97 | } 98 | 99 | private void putAttrsProviderClassName(String className) { 100 | attrsProviderSet.add(className); 101 | } 102 | 103 | private boolean showMenu() { 104 | return showMenu(10); 105 | } 106 | 107 | private boolean showMenu(int y) { 108 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 109 | if (!Settings.canDrawOverlays(Application.getApplicationContext())) { 110 | requestPermission(Application.getApplicationContext()); 111 | Toast.makeText(Application.getApplicationContext(), "After grant this permission, re-enable UETool", Toast.LENGTH_LONG).show(); 112 | return false; 113 | } 114 | } 115 | if (uetMenu == null) { 116 | uetMenu = new UETMenu(Application.getApplicationContext(), y); 117 | } 118 | if (!uetMenu.isShown()) { 119 | uetMenu.show(); 120 | return true; 121 | } 122 | return false; 123 | } 124 | 125 | private int dismissMenu() { 126 | if (uetMenu != null) { 127 | int y = uetMenu.dismiss(); 128 | uetMenu = null; 129 | return y; 130 | } 131 | return -1; 132 | } 133 | 134 | public Set getFilterClasses() { 135 | return filterClassesSet; 136 | } 137 | 138 | public Activity getTargetActivity() { 139 | return Util.getCurrentActivity(); 140 | } 141 | 142 | public AttrsDialogMultiTypePool getAttrsDialogMultiTypePool() { 143 | return attrsDialogMultiTypePool; 144 | } 145 | 146 | public Set getAttrsProvider() { 147 | return attrsProviderSet; 148 | } 149 | 150 | 151 | private void initAttrsDialogMultiTypePool() { 152 | attrsDialogMultiTypePool.register(AddMinusEditItem.class, new AddMinusEditTextItemBinder()); 153 | attrsDialogMultiTypePool.register(BitmapItem.class, new BitmapItemBinder()); 154 | attrsDialogMultiTypePool.register(BriefDescItem.class, new BriefDescItemBinder()); 155 | attrsDialogMultiTypePool.register(EditTextItem.class, new EditTextItemBinder()); 156 | attrsDialogMultiTypePool.register(SwitchItem.class, new SwitchItemBinder()); 157 | attrsDialogMultiTypePool.register(TextItem.class, new TextItemBinder()); 158 | attrsDialogMultiTypePool.register(TitleItem.class, new TitleItemBinder()); 159 | } 160 | 161 | @TargetApi(Build.VERSION_CODES.M) 162 | private void requestPermission(Context context) { 163 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); 164 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 165 | context.startActivity(intent); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/FragmentListTreeDialog.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.graphics.Color; 7 | import android.graphics.RectF; 8 | import android.graphics.drawable.ColorDrawable; 9 | import android.os.Bundle; 10 | import android.text.Html; 11 | import android.view.*; 12 | import android.widget.CheckBox; 13 | import android.widget.CompoundButton; 14 | import android.widget.ImageView; 15 | import android.widget.TextView; 16 | 17 | import androidx.fragment.app.Fragment; 18 | import androidx.fragment.app.FragmentActivity; 19 | import androidx.fragment.app.FragmentManager; 20 | 21 | import com.unnamed.b.atv.model.TreeNode; 22 | import com.unnamed.b.atv.view.AndroidTreeView; 23 | 24 | public class FragmentListTreeDialog extends Dialog implements Provider { 25 | 26 | private ViewGroup containerView; 27 | private RegionView regionView; 28 | 29 | public FragmentListTreeDialog(Context context) { 30 | super(context); 31 | requestWindowFeature(Window.FEATURE_NO_TITLE); 32 | } 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 37 | 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.uet_dialog_fragment_list_tree); 40 | 41 | containerView = findViewById(R.id.container); 42 | regionView = findViewById(R.id.region); 43 | CheckBox checkBox = findViewById(R.id.checkbox); 44 | 45 | createTree(checkBox.isChecked()); 46 | 47 | checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 48 | @Override 49 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 50 | createTree(isChecked); 51 | } 52 | }); 53 | 54 | } 55 | 56 | // 创建 fragment tree 57 | private void createTree(boolean showPackageName) { 58 | TreeNode root = TreeNode.root(); 59 | 60 | Activity activity = UETool.getInstance().getTargetActivity(); 61 | if (activity instanceof FragmentActivity) { 62 | FragmentManager fragmentManager = ((FragmentActivity) activity).getSupportFragmentManager(); 63 | createTreeNode(root, fragmentManager, showPackageName); 64 | } 65 | 66 | containerView.removeAllViews(); 67 | 68 | AndroidTreeView tView = new AndroidTreeView(getContext(), root); 69 | tView.setDefaultAnimation(true); 70 | tView.setUse2dScroll(true); 71 | tView.setDefaultContainerStyle(R.style.uet_TreeNodeStyleCustom); 72 | containerView.addView(tView.getView()); 73 | 74 | tView.expandAll(); 75 | } 76 | 77 | // 递归创建 fragment tree node 78 | private TreeNode createTreeNode(TreeNode rootNode, FragmentManager fragmentManager, boolean showPackageName) { 79 | for (Fragment fragment : fragmentManager.getFragments()) { 80 | TreeNode node = new TreeNode(new TreeItem(fragment, showPackageName)).setViewHolder(new TreeItemVH(getContext(), this)); 81 | FragmentManager childManager = fragment.getChildFragmentManager(); 82 | rootNode.addChild(createTreeNode(node, childManager, showPackageName)); 83 | } 84 | return rootNode; 85 | } 86 | 87 | @Override 88 | protected void onStart() { 89 | super.onStart(); 90 | getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 91 | getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); 92 | } 93 | 94 | @Override 95 | public void onClickTreeItem(RectF rectF) { 96 | regionView.drawRegion(rectF); 97 | } 98 | 99 | public static class TreeItemVH extends TreeNode.BaseNodeViewHolder { 100 | 101 | private TextView nameView; 102 | private ImageView arrowView; 103 | 104 | private Provider provider; 105 | 106 | public TreeItemVH(Context context, Provider provider) { 107 | super(context); 108 | this.provider = provider; 109 | } 110 | 111 | @Override 112 | public View createNodeView(TreeNode node, final TreeItem value) { 113 | final View view = LayoutInflater.from(context).inflate(R.layout.uet_cell_tree, null, false); 114 | 115 | nameView = view.findViewById(R.id.name); 116 | arrowView = view.findViewById(R.id.arrow); 117 | 118 | nameView.setText(Html.fromHtml(value.name)); 119 | 120 | if (value.rectF != null) { 121 | nameView.setOnClickListener(new View.OnClickListener() { 122 | @Override 123 | public void onClick(View v) { 124 | if (provider != null) { 125 | provider.onClickTreeItem(value.rectF); 126 | } 127 | } 128 | }); 129 | } 130 | 131 | return view; 132 | } 133 | 134 | @Override 135 | public void toggle(boolean active) { 136 | super.toggle(active); 137 | 138 | arrowView.animate().setDuration(200).rotation(active ? 90 : 0).start(); 139 | } 140 | } 141 | 142 | public static class TreeItem { 143 | 144 | public String name; 145 | public RectF rectF; 146 | 147 | public TreeItem(Fragment fragment, boolean showPackageName) { 148 | initName(fragment, showPackageName); 149 | initRect(fragment); 150 | } 151 | 152 | private void initName(Fragment fragment, boolean showPackageName) { 153 | StringBuilder sb = new StringBuilder(); 154 | sb.append(showPackageName ? fragment.getClass().getName() : fragment.getClass().getSimpleName()); 155 | sb.append("[visible=").append(fragment.isVisible()).append(", hashCode=").append(fragment.hashCode()).append("]"); 156 | name = sb.toString(); 157 | if (fragment.isVisible()) { 158 | name = "" + name + ""; 159 | } 160 | } 161 | 162 | private void initRect(Fragment fragment) { 163 | if (fragment.isVisible()) { 164 | View view = fragment.getView(); 165 | int[] location = new int[2]; 166 | view.getLocationOnScreen(location); 167 | rectF = new RectF(location[0], location[1], location[0] + view.getWidth(), location[1] + view.getHeight()); 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /uetool-fresco/src/main/java/me/ele/uetool/fresco/UETFresco.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool.fresco; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.drawable.BitmapDrawable; 5 | import android.graphics.drawable.Drawable; 6 | import android.view.View; 7 | import com.facebook.common.internal.Supplier; 8 | import com.facebook.drawee.backends.pipeline.PipelineDraweeController; 9 | import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder; 10 | import com.facebook.drawee.drawable.FadeDrawable; 11 | import com.facebook.drawee.drawable.ScaleTypeDrawable; 12 | import com.facebook.drawee.generic.GenericDraweeHierarchy; 13 | import com.facebook.drawee.generic.RoundingParams; 14 | import com.facebook.drawee.view.DraweeView; 15 | import com.facebook.drawee.view.GenericDraweeView; 16 | import me.ele.uetool.base.Element; 17 | import me.ele.uetool.base.IAttrs; 18 | import me.ele.uetool.base.item.BitmapItem; 19 | import me.ele.uetool.base.item.Item; 20 | import me.ele.uetool.base.item.TextItem; 21 | import me.ele.uetool.base.item.TitleItem; 22 | 23 | import java.lang.reflect.Field; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import static me.ele.uetool.base.DimenUtil.px2dip; 28 | 29 | public class UETFresco implements IAttrs { 30 | 31 | @Override 32 | public List getAttrs(Element element) { 33 | List items = new ArrayList<>(); 34 | 35 | View view = element.getView(); 36 | 37 | if (view instanceof DraweeView) { 38 | items.add(new TitleItem("DraweeView")); 39 | items.add(new TextItem("CornerRadius", getCornerRadius((DraweeView) view))); 40 | items.add(new TextItem("ImageURI", getImageURI((DraweeView) view), true)); 41 | items.add(new TextItem("ActualScaleType", getScaleType((DraweeView) view), true)); 42 | items.add(new TextItem("IsSupportAnimation", isSupportAnimation((DraweeView) view))); 43 | items.add(new BitmapItem("PlaceHolderImage", getPlaceHolderBitmap((DraweeView) view))); 44 | items.add(new TextItem("FadeDuration", getFadeDuration((DraweeView) view))); 45 | } 46 | return items; 47 | } 48 | 49 | private String getCornerRadius(DraweeView draweeView) { 50 | GenericDraweeHierarchy hierarchy = getGenericDraweeHierarchy(draweeView); 51 | if (hierarchy != null) { 52 | RoundingParams params = hierarchy.getRoundingParams(); 53 | if (params != null) { 54 | float[] cornersRadii = params.getCornersRadii(); 55 | if (cornersRadii != null) { 56 | float firstRadii = cornersRadii[0]; 57 | for (int i = 1; i < 8; i++) { 58 | if (firstRadii != cornersRadii[i]) { 59 | return null; 60 | } 61 | } 62 | return px2dip(firstRadii, true); 63 | } 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | private String getScaleType(DraweeView draweeView) { 70 | GenericDraweeHierarchy hierarchy = getGenericDraweeHierarchy(draweeView); 71 | if (hierarchy != null) { 72 | return hierarchy.getActualImageScaleType().toString().toUpperCase(); 73 | } 74 | return null; 75 | } 76 | 77 | private String getImageURI(DraweeView draweeView) { 78 | PipelineDraweeControllerBuilder builder = getFrescoControllerBuilder(draweeView); 79 | if (builder != null) { 80 | return builder.getImageRequest().getSourceUri().toString(); 81 | } 82 | return ""; 83 | } 84 | 85 | private String isSupportAnimation(DraweeView draweeView) { 86 | PipelineDraweeControllerBuilder builder = getFrescoControllerBuilder(draweeView); 87 | if (builder != null) { 88 | return String.valueOf(builder.getAutoPlayAnimations()).toUpperCase(); 89 | } 90 | return ""; 91 | } 92 | 93 | private Bitmap getPlaceHolderBitmap(DraweeView draweeView) { 94 | GenericDraweeHierarchy hierarchy = getGenericDraweeHierarchy(draweeView); 95 | if (hierarchy != null && hierarchy.hasPlaceholderImage()) { 96 | try { 97 | Field mFadeDrawableField = hierarchy.getClass().getDeclaredField("mFadeDrawable"); 98 | mFadeDrawableField.setAccessible(true); 99 | FadeDrawable fadeDrawable = (FadeDrawable) mFadeDrawableField.get(hierarchy); 100 | Field mLayersField = fadeDrawable.getClass().getDeclaredField("mLayers"); 101 | mLayersField.setAccessible(true); 102 | Drawable[] layers = (Drawable[]) mLayersField.get(fadeDrawable); 103 | // PLACEHOLDER_IMAGE_INDEX == 1 104 | Drawable drawable = layers[1]; 105 | return getFrescoDrawableBitmap(drawable); 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | return null; 111 | } 112 | 113 | private String getFadeDuration(DraweeView draweeView) { 114 | int duration = 0; 115 | GenericDraweeHierarchy hierarchy = getGenericDraweeHierarchy(draweeView); 116 | if (hierarchy != null) { 117 | duration = hierarchy.getFadeDuration(); 118 | } 119 | return duration + "ms"; 120 | } 121 | 122 | private GenericDraweeHierarchy getGenericDraweeHierarchy(DraweeView draweeView) { 123 | if (draweeView instanceof GenericDraweeView) { 124 | return ((GenericDraweeView) draweeView).getHierarchy(); 125 | } 126 | return null; 127 | } 128 | 129 | private PipelineDraweeControllerBuilder getFrescoControllerBuilder(DraweeView draweeView) { 130 | try { 131 | PipelineDraweeController controller = (PipelineDraweeController) draweeView.getController(); 132 | Field mDataSourceSupplierFiled = PipelineDraweeController.class.getDeclaredField("mDataSourceSupplier"); 133 | mDataSourceSupplierFiled.setAccessible(true); 134 | Supplier supplier = (Supplier) mDataSourceSupplierFiled.get(controller); 135 | Field mAutoField = Class.forName("com.facebook.drawee.controller.AbstractDraweeControllerBuilder$2").getDeclaredField("this$0"); 136 | mAutoField.setAccessible(true); 137 | PipelineDraweeControllerBuilder builder = (PipelineDraweeControllerBuilder) mAutoField.get(supplier); 138 | return builder; 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | } 142 | return null; 143 | } 144 | 145 | private Bitmap getFrescoDrawableBitmap(Drawable drawable) { 146 | try { 147 | if (drawable instanceof ScaleTypeDrawable) { 148 | return ((BitmapDrawable) drawable.getCurrent()).getBitmap(); 149 | } else if (drawable instanceof BitmapDrawable) { 150 | return ((BitmapDrawable) drawable).getBitmap(); 151 | } 152 | } catch (Exception e) { 153 | e.printStackTrace(); 154 | } 155 | return null; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/EditAttrLayout.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | 12 | import androidx.annotation.Nullable; 13 | 14 | import me.ele.uetool.base.Element; 15 | 16 | import static me.ele.uetool.base.DimenUtil.dip2px; 17 | import static me.ele.uetool.base.DimenUtil.px2dip; 18 | 19 | public class EditAttrLayout extends CollectViewsLayout { 20 | 21 | private final int moveUnit = dip2px(1); 22 | private final int lineBorderDistance = dip2px(5); 23 | 24 | private Paint areaPaint = new Paint() { 25 | { 26 | setAntiAlias(true); 27 | setColor(0x30000000); 28 | } 29 | }; 30 | 31 | private Element targetElement; 32 | private AttrsDialog dialog; 33 | private IMode mode = new ShowMode(); 34 | private float lastX, lastY; 35 | private OnDragListener onDragListener; 36 | 37 | public EditAttrLayout(Context context) { 38 | super(context); 39 | } 40 | 41 | public EditAttrLayout(Context context, @Nullable AttributeSet attrs) { 42 | super(context, attrs); 43 | } 44 | 45 | public EditAttrLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 46 | super(context, attrs, defStyleAttr); 47 | } 48 | 49 | @Override 50 | protected void onDraw(Canvas canvas) { 51 | super.onDraw(canvas); 52 | if (targetElement != null) { 53 | canvas.drawRect(targetElement.getRect(), areaPaint); 54 | mode.onDraw(canvas); 55 | } 56 | } 57 | 58 | @Override 59 | public boolean onTouchEvent(MotionEvent event) { 60 | switch (event.getAction()) { 61 | case MotionEvent.ACTION_DOWN: 62 | lastX = event.getX(); 63 | lastY = event.getY(); 64 | break; 65 | case MotionEvent.ACTION_UP: 66 | mode.triggerActionUp(event); 67 | break; 68 | case MotionEvent.ACTION_MOVE: 69 | mode.triggerActionMove(event); 70 | break; 71 | } 72 | return true; 73 | } 74 | 75 | @Override 76 | protected void onDetachedFromWindow() { 77 | super.onDetachedFromWindow(); 78 | targetElement = null; 79 | dismissAttrsDialog(); 80 | } 81 | 82 | public void setOnDragListener(OnDragListener onDragListener) { 83 | this.onDragListener = onDragListener; 84 | } 85 | 86 | public void dismissAttrsDialog() { 87 | if (dialog != null) { 88 | dialog.dismiss(); 89 | } 90 | } 91 | 92 | class MoveMode implements IMode { 93 | 94 | @Override 95 | public void onDraw(Canvas canvas) { 96 | Rect rect = targetElement.getRect(); 97 | Rect originRect = targetElement.getOriginRect(); 98 | canvas.drawRect(originRect, dashLinePaint); 99 | Element parentElement = targetElement.getParentElement(); 100 | if (parentElement != null) { 101 | Rect parentRect = parentElement.getRect(); 102 | int x = rect.left + rect.width() / 2; 103 | int y = rect.top + rect.height() / 2; 104 | drawLineWithText(canvas, rect.left, y, parentRect.left, y, dip2px(2)); 105 | drawLineWithText(canvas, x, rect.top, x, parentRect.top, dip2px(2)); 106 | drawLineWithText(canvas, rect.right, y, parentRect.right, y, dip2px(2)); 107 | drawLineWithText(canvas, x, rect.bottom, x, parentRect.bottom, dip2px(2)); 108 | } 109 | if (onDragListener != null) { 110 | onDragListener.showOffset("Offset:\n" + "x -> " + px2dip(rect.left - originRect.left, true) + " y -> " + px2dip(rect.top - originRect.top, true)); 111 | } 112 | } 113 | 114 | @Override 115 | public void triggerActionMove(MotionEvent event) { 116 | if (targetElement != null) { 117 | boolean changed = false; 118 | View view = targetElement.getView(); 119 | float diffX = event.getX() - lastX; 120 | if (Math.abs(diffX) >= moveUnit) { 121 | view.setTranslationX(view.getTranslationX() + diffX); 122 | lastX = event.getX(); 123 | changed = true; 124 | } 125 | float diffY = event.getY() - lastY; 126 | if (Math.abs(diffY) >= moveUnit) { 127 | view.setTranslationY(view.getTranslationY() + diffY); 128 | lastY = event.getY(); 129 | changed = true; 130 | } 131 | if (changed) { 132 | targetElement.reset(); 133 | invalidate(); 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | public void triggerActionUp(MotionEvent event) { 140 | 141 | } 142 | } 143 | 144 | class ShowMode implements IMode { 145 | 146 | @Override 147 | public void onDraw(Canvas canvas) { 148 | Rect rect = targetElement.getRect(); 149 | drawLineWithText(canvas, rect.left, rect.top - lineBorderDistance, rect.right, rect.top - lineBorderDistance); 150 | drawLineWithText(canvas, rect.right + lineBorderDistance, rect.top, rect.right + lineBorderDistance, rect.bottom); 151 | } 152 | 153 | @Override 154 | public void triggerActionMove(MotionEvent event) { 155 | 156 | } 157 | 158 | @Override 159 | public void triggerActionUp(final MotionEvent event) { 160 | final Element element = getTargetElement(event.getX(), event.getY()); 161 | if (element != null) { 162 | targetElement = element; 163 | invalidate(); 164 | if (dialog == null) { 165 | dialog = new AttrsDialog(getContext()); 166 | dialog.setAttrDialogCallback(new AttrsDialog.AttrDialogCallback() { 167 | @Override 168 | public void enableMove() { 169 | mode = new MoveMode(); 170 | dismissAttrsDialog(); 171 | } 172 | 173 | @Override 174 | public void showValidViews(int position, boolean isChecked) { 175 | int positionStart = position + 1; 176 | if (isChecked) { 177 | dialog.notifyValidViewItemInserted(positionStart, getTargetElements(lastX, lastY), targetElement); 178 | } else { 179 | dialog.notifyItemRangeRemoved(positionStart); 180 | } 181 | } 182 | 183 | @Override 184 | public void selectView(Element element) { 185 | targetElement = element; 186 | dismissAttrsDialog(); 187 | dialog.show(targetElement); 188 | } 189 | }); 190 | dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 191 | @Override 192 | public void onDismiss(DialogInterface dialog) { 193 | if (targetElement != null) { 194 | targetElement.reset(); 195 | invalidate(); 196 | } 197 | } 198 | }); 199 | } 200 | dialog.show(targetElement); 201 | } 202 | } 203 | } 204 | 205 | public interface IMode { 206 | void onDraw(Canvas canvas); 207 | 208 | void triggerActionMove(MotionEvent event); 209 | 210 | void triggerActionUp(MotionEvent event); 211 | } 212 | 213 | public interface OnDragListener { 214 | void showOffset(String offsetContent); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/UETMenu.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.TimeInterpolator; 6 | import android.animation.ValueAnimator; 7 | import android.app.Activity; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.res.Resources; 11 | import android.graphics.PixelFormat; 12 | import android.os.Build; 13 | import android.os.Bundle; 14 | import android.util.Log; 15 | import android.view.*; 16 | import android.view.animation.AccelerateDecelerateInterpolator; 17 | import android.view.animation.Interpolator; 18 | import android.widget.FrameLayout; 19 | import android.widget.LinearLayout; 20 | import com.jakewharton.scalpel.ScalpelFrameLayout; 21 | 22 | import java.lang.reflect.Field; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import static me.ele.uetool.MenuHelper.Type.TYPE_LAYOUT_LEVEL; 27 | import static me.ele.uetool.MenuHelper.Type.TYPE_UNKNOWN; 28 | 29 | public class UETMenu extends LinearLayout { 30 | 31 | private View vMenu; 32 | private ViewGroup vSubMenuContainer; 33 | private ValueAnimator animator; 34 | private Interpolator defaultInterpolator = new AccelerateDecelerateInterpolator(); 35 | private List subMenus = new ArrayList<>(); 36 | 37 | private WindowManager windowManager; 38 | private WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 39 | private int touchSlop; 40 | private int y; 41 | public static final String ACTION="com.longshihan.uetooltaichi.xposed"; 42 | 43 | public UETMenu(final Context context, int y) { 44 | super(context); 45 | inflate(context, R.layout.uet_menu_layout, this); 46 | setGravity(Gravity.CENTER_VERTICAL); 47 | 48 | this.y = y; 49 | touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 50 | windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 51 | 52 | vMenu = findViewById(R.id.menu); 53 | vSubMenuContainer = findViewById(R.id.sub_menu_container); 54 | Resources resources = context.getResources(); 55 | subMenus.add(new UETSubMenu.SubMenu(resources.getString(R.string.uet_catch_view), R.drawable.uet_edit_attr, new OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | Intent intent = new Intent(ACTION); 59 | intent.putExtra("type",MenuHelper.Type.TYPE_EDIT_ATTR); 60 | context.sendBroadcast(intent); 61 | } 62 | })); 63 | subMenus.add(new UETSubMenu.SubMenu(resources.getString(R.string.uet_relative_location), R.drawable.uet_relative_position, 64 | new OnClickListener() { 65 | @Override 66 | public void onClick(View v) { 67 | Intent intent = new Intent(ACTION); 68 | intent.putExtra("type", MenuHelper.Type.TYPE_RELATIVE_POSITION); 69 | context.sendBroadcast(intent); 70 | } 71 | })); 72 | subMenus.add(new UETSubMenu.SubMenu(resources.getString(R.string.uet_grid), R.drawable.uet_show_gridding, 73 | new OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | Intent intent = new Intent(ACTION); 77 | intent.putExtra("type", MenuHelper.Type.TYPE_SHOW_GRIDDING); 78 | context.sendBroadcast(intent); 79 | } 80 | })); 81 | 82 | subMenus.add(new UETSubMenu.SubMenu(resources.getString(R.string.uet_scalpel), R.drawable.uet_scalpel, new OnClickListener() { 83 | @Override 84 | public void onClick(View view) { 85 | Intent intent = new Intent(ACTION); 86 | intent.putExtra("type", TYPE_LAYOUT_LEVEL); 87 | context.sendBroadcast(intent); 88 | } 89 | })); 90 | 91 | for (UETSubMenu.SubMenu subMenu : subMenus) { 92 | UETSubMenu uetSubMenu = new UETSubMenu(getContext()); 93 | uetSubMenu.update(subMenu); 94 | vSubMenuContainer.addView(uetSubMenu); 95 | } 96 | 97 | vMenu.setOnClickListener(new OnClickListener() { 98 | @Override 99 | public void onClick(View v) { 100 | startAnim(); 101 | } 102 | }); 103 | 104 | vMenu.setOnTouchListener(new View.OnTouchListener() { 105 | private float downX, downY; 106 | private float lastY; 107 | 108 | @Override 109 | public boolean onTouch(View v, MotionEvent event) { 110 | switch (event.getAction()) { 111 | case MotionEvent.ACTION_DOWN: 112 | downX = event.getRawX(); 113 | downY = event.getRawY(); 114 | lastY = downY; 115 | break; 116 | case MotionEvent.ACTION_MOVE: 117 | params.y += event.getRawY() - lastY; 118 | params.y = Math.max(0, params.y); 119 | windowManager.updateViewLayout(UETMenu.this, params); 120 | lastY = event.getRawY(); 121 | break; 122 | case MotionEvent.ACTION_UP: 123 | if (Math.abs(event.getRawX() - downX) < touchSlop && Math.abs(event.getRawY() - downY) < touchSlop) { 124 | try { 125 | Field field = View.class.getDeclaredField("mListenerInfo"); 126 | field.setAccessible(true); 127 | Object object = field.get(vMenu); 128 | field = object.getClass().getDeclaredField("mOnClickListener"); 129 | field.setAccessible(true); 130 | object = field.get(object); 131 | if (object != null && object instanceof View.OnClickListener) { 132 | ((View.OnClickListener) object).onClick(vMenu); 133 | } 134 | } catch (Exception e) { 135 | e.printStackTrace(); 136 | } 137 | } 138 | break; 139 | } 140 | return true; 141 | } 142 | }); 143 | } 144 | 145 | private void startAnim() { 146 | ensureAnim(); 147 | final boolean isOpen = vSubMenuContainer.getTranslationX() <= -vSubMenuContainer.getWidth(); 148 | animator.setInterpolator(isOpen ? defaultInterpolator : new ReverseInterpolator(defaultInterpolator)); 149 | animator.removeAllListeners(); 150 | animator.addListener(new AnimatorListenerAdapter() { 151 | @Override 152 | public void onAnimationStart(Animator animation) { 153 | vSubMenuContainer.setVisibility(VISIBLE); 154 | } 155 | 156 | @Override 157 | public void onAnimationEnd(Animator animation) { 158 | if (!isOpen) { 159 | vSubMenuContainer.setVisibility(GONE); 160 | } 161 | } 162 | }); 163 | animator.start(); 164 | } 165 | 166 | private void ensureAnim() { 167 | if (animator == null) { 168 | animator = ValueAnimator.ofInt(-vSubMenuContainer.getWidth(), 0); 169 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 170 | @Override 171 | public void onAnimationUpdate(ValueAnimator animation) { 172 | vSubMenuContainer.setTranslationX((int) animation.getAnimatedValue()); 173 | } 174 | }); 175 | animator.setDuration(400); 176 | } 177 | } 178 | 179 | private void open() { 180 | open(TYPE_UNKNOWN); 181 | } 182 | 183 | public static void open(int type) { 184 | if (type==TYPE_LAYOUT_LEVEL){ 185 | showLayoutLevel(); 186 | return; 187 | } 188 | Activity currentTopActivity = UETool.getInstance().getTargetActivity(); 189 | Log.d("UETMenu", "currentTopActivity: " + (currentTopActivity == null)); 190 | if (currentTopActivity == null) { 191 | Log.d("UETMenu","currentTopActivity 为空"); 192 | return; 193 | }else if (UETMenu.dismiss(currentTopActivity)) { 194 | return; 195 | } 196 | Bundle bundle = new Bundle(); 197 | bundle.putInt(MenuHelper.EXTRA_TYPE, type); 198 | MenuHelper.show(currentTopActivity, bundle); 199 | } 200 | 201 | private static void showLayoutLevel() { 202 | try { 203 | Activity activity=Util.getCurrentActivity(); 204 | if (activity==null){ 205 | Log.d("TTTTTTT","activity 为空"); 206 | return; 207 | } 208 | Log.d("TTTTTTT",activity.getLocalClassName()); 209 | Window window=activity.getWindow(); 210 | if (window==null){ 211 | Log.d("TTTTTTT","window 为空"); 212 | return; 213 | } 214 | ViewGroup decorView = (ViewGroup) window.getDecorView(); 215 | ViewGroup content = decorView.findViewById(android.R.id.content); 216 | View contentChild = content.getChildAt(0); 217 | if (contentChild != null) { 218 | if (contentChild instanceof ScalpelFrameLayout) { 219 | content.removeAllViews(); 220 | View originContent = ((ScalpelFrameLayout) contentChild).getChildAt(0); 221 | ((ScalpelFrameLayout) contentChild).removeAllViews(); 222 | content.addView(originContent); 223 | } else { 224 | content.removeAllViews(); 225 | ScalpelFrameLayout frameLayout = new ScalpelFrameLayout(activity); 226 | frameLayout.setLayerInteractionEnabled(true); 227 | frameLayout.setDrawIds(true); 228 | frameLayout.addView(contentChild); 229 | content.addView(frameLayout); 230 | } 231 | } 232 | }catch (Exception e){ 233 | e.printStackTrace(); 234 | } 235 | } 236 | 237 | public void show() { 238 | try { 239 | windowManager.addView(this, getWindowLayoutParams()); 240 | } catch (Exception e) { 241 | e.printStackTrace(); 242 | } 243 | } 244 | public int dismiss() { 245 | try { 246 | windowManager.removeView(this); 247 | } catch (Exception e) { 248 | e.printStackTrace(); 249 | } 250 | return params.y; 251 | } 252 | 253 | public static boolean dismiss(Activity currentTopActivity) { 254 | return MenuHelper.dismiss(currentTopActivity); 255 | } 256 | 257 | private WindowManager.LayoutParams getWindowLayoutParams() { 258 | params.width = FrameLayout.LayoutParams.WRAP_CONTENT; 259 | params.height = FrameLayout.LayoutParams.WRAP_CONTENT; 260 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 261 | params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 262 | } else { 263 | params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 264 | } 265 | params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 266 | params.format = PixelFormat.TRANSLUCENT; 267 | params.gravity = Gravity.TOP | Gravity.LEFT; 268 | params.x = 10; 269 | params.y = y; 270 | return params; 271 | } 272 | 273 | private static class ReverseInterpolator implements TimeInterpolator { 274 | 275 | private TimeInterpolator mWrappedInterpolator; 276 | 277 | ReverseInterpolator(TimeInterpolator interpolator) { 278 | mWrappedInterpolator = interpolator; 279 | } 280 | 281 | @Override 282 | public float getInterpolation(float input) { 283 | return mWrappedInterpolator.getInterpolation(Math.abs(input - 1f)); 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/CollectViewsLayout.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.ContextWrapper; 6 | import android.graphics.*; 7 | import android.os.Build; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.WindowManager; 12 | import android.widget.Toast; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | import me.ele.uetool.base.DimenUtil; 17 | import me.ele.uetool.base.Element; 18 | import me.ele.uetool.base.ReflectionP; 19 | 20 | import java.lang.reflect.Field; 21 | import java.util.*; 22 | import me.ele.uetool.base.ReflectionP.Func; 23 | 24 | import static me.ele.uetool.base.DimenUtil.*; 25 | 26 | public class CollectViewsLayout extends View { 27 | 28 | private final int halfEndPointWidth = dip2px(2.5f); 29 | private final int textBgFillingSpace = dip2px(2); 30 | private final int textLineDistance = dip2px(5); 31 | protected final int screenWidth = getScreenWidth(); 32 | protected final int screenHeight = getScreenHeight(); 33 | 34 | protected List elements = new ArrayList<>(); 35 | protected Element childElement, parentElement; 36 | protected Paint textPaint = new Paint() { 37 | { 38 | setAntiAlias(true); 39 | setTextSize(sp2px(10)); 40 | setColor(Color.RED); 41 | setStrokeWidth(dip2px(1)); 42 | } 43 | }; 44 | 45 | private Paint textBgPaint = new Paint() { 46 | { 47 | setAntiAlias(true); 48 | setColor(Color.WHITE); 49 | setStrokeJoin(Join.ROUND); 50 | } 51 | }; 52 | 53 | protected Paint dashLinePaint = new Paint() { 54 | { 55 | setAntiAlias(true); 56 | setColor(0x90FF0000); 57 | setStyle(Style.STROKE); 58 | setPathEffect(new DashPathEffect(new float[]{dip2px(4), dip2px(8)}, 0)); 59 | } 60 | }; 61 | 62 | public CollectViewsLayout(Context context) { 63 | super(context); 64 | } 65 | 66 | public CollectViewsLayout(Context context, @Nullable AttributeSet attrs) { 67 | super(context, attrs); 68 | } 69 | 70 | public CollectViewsLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 71 | super(context, attrs, defStyleAttr); 72 | } 73 | 74 | @Override 75 | protected void onAttachedToWindow() { 76 | super.onAttachedToWindow(); 77 | try { 78 | final Activity targetActivity = UETool.getInstance().getTargetActivity(); 79 | final WindowManager windowManager = targetActivity.getWindowManager(); 80 | 81 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { 82 | final Field mGlobalField = Class.forName("android.view.WindowManagerImpl").getDeclaredField("mGlobal"); 83 | mGlobalField.setAccessible(true); 84 | 85 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { 86 | Field mViewsField = Class.forName("android.view.WindowManagerGlobal").getDeclaredField("mViews"); 87 | mViewsField.setAccessible(true); 88 | List views; 89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 90 | views = (List) mViewsField.get(mGlobalField.get(windowManager)); 91 | } else { 92 | views = Arrays.asList((View[]) mViewsField.get(mGlobalField.get(windowManager))); 93 | } 94 | 95 | for (int i = views.size() - 1; i >= 0; i--) { 96 | View targetView = getTargetDecorView(targetActivity, views.get(i)); 97 | if (targetView != null) { 98 | createElements(targetView); 99 | break; 100 | } 101 | } 102 | } else { 103 | ReflectionP.breakAndroidP(new Func() { 104 | @Override 105 | public Void call() { 106 | try { 107 | Field mRootsField = Class.forName("android.view.WindowManagerGlobal").getDeclaredField("mRoots"); 108 | mRootsField.setAccessible(true); 109 | List viewRootImpls; 110 | viewRootImpls = (List) mRootsField.get(mGlobalField.get(windowManager)); 111 | for (int i = viewRootImpls.size() - 1; i >= 0; i--) { 112 | Class clazz = Class.forName("android.view.ViewRootImpl"); 113 | Object object = viewRootImpls.get(i); 114 | Field mWindowAttributesField = clazz.getDeclaredField("mWindowAttributes"); 115 | mWindowAttributesField.setAccessible(true); 116 | Field mViewField = clazz.getDeclaredField("mView"); 117 | mViewField.setAccessible(true); 118 | View decorView = (View) mViewField.get(object); 119 | WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mWindowAttributesField.get(object); 120 | if (layoutParams.getTitle().toString().contains(targetActivity.getClass().getName()) 121 | || getTargetDecorView(targetActivity, decorView) != null) { 122 | createElements(decorView); 123 | break; 124 | } 125 | } 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | } 129 | return null; 130 | } 131 | }); 132 | } 133 | } else { 134 | // http://androidxref.com/4.1.1/xref/frameworks/base/core/java/android/view/WindowManagerImpl.java 135 | Field mWindowManagerField = Class.forName("android.view.WindowManagerImpl$CompatModeWrapper").getDeclaredField("mWindowManager"); 136 | mWindowManagerField.setAccessible(true); 137 | Field mViewsField = Class.forName("android.view.WindowManagerImpl").getDeclaredField("mViews"); 138 | mViewsField.setAccessible(true); 139 | List views = Arrays.asList((View[]) mViewsField.get(mWindowManagerField.get(windowManager))); 140 | for (int i = views.size() - 1; i >= 0; i--) { 141 | View targetView = getTargetDecorView(targetActivity, views.get(i)); 142 | if (targetView != null) { 143 | createElements(targetView); 144 | break; 145 | } 146 | } 147 | } 148 | } catch (Exception e) { 149 | e.printStackTrace(); 150 | } 151 | } 152 | 153 | @Override 154 | protected void onDetachedFromWindow() { 155 | super.onDetachedFromWindow(); 156 | elements.clear(); 157 | childElement = null; 158 | parentElement = null; 159 | } 160 | 161 | private void createElements(View view) { 162 | 163 | List elements = new ArrayList<>(); 164 | traverse(view, elements); 165 | 166 | // 面积从大到小排序 167 | Collections.sort(elements, new Comparator() { 168 | @Override 169 | public int compare(Element o1, Element o2) { 170 | return o2.getArea() - o1.getArea(); 171 | } 172 | }); 173 | 174 | this.elements.addAll(elements); 175 | 176 | } 177 | 178 | private void traverse(View view, List elements) { 179 | if (UETool.getInstance().getFilterClasses().contains(view.getClass().getName())) return; 180 | if (view.getAlpha() == 0 || view.getVisibility() != View.VISIBLE) return; 181 | if ("DESABLE_UETOOL".equals(view.getTag())) return; 182 | elements.add(new Element(view)); 183 | if (view instanceof ViewGroup) { 184 | ViewGroup parent = (ViewGroup) view; 185 | for (int i = 0; i < parent.getChildCount(); i++) { 186 | traverse(parent.getChildAt(i), elements); 187 | } 188 | } 189 | } 190 | 191 | private View getTargetDecorView(Activity targetActivity, View decorView) { 192 | Context context = null; 193 | if (decorView instanceof ViewGroup && ((ViewGroup) decorView).getChildCount() > 0) { 194 | context = ((ViewGroup) decorView).getChildAt(0).getContext(); 195 | } 196 | 197 | while (context != null) { 198 | if (context == targetActivity) { 199 | return decorView; 200 | } else if (context instanceof ContextWrapper) { 201 | context = ((ContextWrapper) context).getBaseContext(); 202 | } else { 203 | return null; 204 | } 205 | } 206 | return null; 207 | } 208 | 209 | protected Element getTargetElement(float x, float y) { 210 | Element target = null; 211 | for (int i = elements.size() - 1; i >= 0; i--) { 212 | final Element element = elements.get(i); 213 | if (element.getRect().contains((int) x, (int) y)) { 214 | if (isParentNotVisible(element.getParentElement())) { 215 | continue; 216 | } 217 | if (element != childElement) { 218 | childElement = element; 219 | parentElement = element; 220 | } else if (parentElement != null) { 221 | parentElement = parentElement.getParentElement(); 222 | } 223 | target = parentElement == null ? element : parentElement; 224 | break; 225 | } 226 | } 227 | if (target == null) { 228 | Toast.makeText(getContext(), String.format("could not found view in (%1$.0f , %2$.0f), please select view again", x, y), Toast.LENGTH_SHORT).show(); 229 | } 230 | return target; 231 | } 232 | 233 | private boolean isParentNotVisible(Element parent) { 234 | if (parent == null) { 235 | return false; 236 | } 237 | if (parent.getRect().left >= DimenUtil.getScreenWidth() 238 | || parent.getRect().top >= DimenUtil.getScreenHeight()) { 239 | return true; 240 | } else { 241 | return isParentNotVisible(parent.getParentElement()); 242 | } 243 | } 244 | 245 | protected List getTargetElements(float x, float y) { 246 | List validList = new ArrayList<>(); 247 | for (int i = elements.size() - 1; i >= 0; i--) { 248 | final Element element = elements.get(i); 249 | if (element.getRect().contains((int) x, (int) y)) { 250 | validList.add(element); 251 | } 252 | } 253 | return validList; 254 | } 255 | 256 | 257 | protected void drawText(Canvas canvas, String text, float x, float y) { 258 | float left = x - textBgFillingSpace; 259 | float top = y - getTextHeight(text); 260 | float right = x + getTextWidth(text) + textBgFillingSpace; 261 | float bottom = y + textBgFillingSpace; 262 | // ensure text in screen bound 263 | if (left < 0) { 264 | right -= left; 265 | left = 0; 266 | } 267 | if (top < 0) { 268 | bottom -= top; 269 | top = 0; 270 | } 271 | if (bottom > screenHeight) { 272 | float diff = top - bottom; 273 | bottom = screenHeight; 274 | top = bottom + diff; 275 | } 276 | if (right > screenWidth) { 277 | float diff = left - right; 278 | right = screenWidth; 279 | left = right + diff; 280 | } 281 | canvas.drawRect(left, top, right, bottom, textBgPaint); 282 | canvas.drawText(text, left + textBgFillingSpace, bottom - textBgFillingSpace, textPaint); 283 | } 284 | 285 | private void drawLineWithEndPoint(Canvas canvas, int startX, int startY, int endX, int endY) { 286 | canvas.drawLine(startX, startY, endX, endY, textPaint); 287 | if (startX == endX) { 288 | canvas.drawLine(startX - halfEndPointWidth, startY, endX + halfEndPointWidth, startY, textPaint); 289 | canvas.drawLine(startX - halfEndPointWidth, endY, endX + halfEndPointWidth, endY, textPaint); 290 | } else if (startY == endY) { 291 | canvas.drawLine(startX, startY - halfEndPointWidth, startX, endY + halfEndPointWidth, textPaint); 292 | canvas.drawLine(endX, startY - halfEndPointWidth, endX, endY + halfEndPointWidth, textPaint); 293 | } 294 | } 295 | 296 | protected void drawLineWithText(Canvas canvas, int startX, int startY, int endX, int endY) { 297 | drawLineWithText(canvas, startX, startY, endX, endY, 0); 298 | } 299 | 300 | protected void drawLineWithText(Canvas canvas, int startX, int startY, int endX, int endY, int endPointSpace) { 301 | 302 | if (startX == endX && startY == endY) { 303 | return; 304 | } 305 | 306 | if (startX > endX) { 307 | int tempX = startX; 308 | startX = endX; 309 | endX = tempX; 310 | } 311 | if (startY > endY) { 312 | int tempY = startY; 313 | startY = endY; 314 | endY = tempY; 315 | } 316 | 317 | if (startX == endX) { 318 | drawLineWithEndPoint(canvas, startX, startY + endPointSpace, endX, endY - endPointSpace); 319 | String text = px2dip(endY - startY, true); 320 | drawText(canvas, text, startX + textLineDistance, startY + (endY - startY) / 2 + getTextHeight(text) / 2); 321 | } else if (startY == endY) { 322 | drawLineWithEndPoint(canvas, startX + endPointSpace, startY, endX - endPointSpace, endY); 323 | String text = px2dip(endX - startX, true); 324 | drawText(canvas, text, startX + (endX - startX) / 2 - getTextWidth(text) / 2, startY - textLineDistance); 325 | } 326 | } 327 | 328 | protected float getTextHeight(String text) { 329 | Rect rect = new Rect(); 330 | textPaint.getTextBounds(text, 0, text.length(), rect); 331 | return rect.height(); 332 | } 333 | 334 | protected float getTextWidth(String text) { 335 | return textPaint.measureText(text); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /uetool/src/main/java/me/ele/uetool/Util.java: -------------------------------------------------------------------------------- 1 | package me.ele.uetool; 2 | 3 | import android.app.Activity; 4 | import android.content.ClipData; 5 | import android.content.ClipboardManager; 6 | import android.content.Context; 7 | import android.content.res.Resources; 8 | import android.graphics.*; 9 | import android.graphics.drawable.*; 10 | import android.os.Build; 11 | import android.text.SpannedString; 12 | import android.text.style.ImageSpan; 13 | import android.util.Log; 14 | import android.util.Pair; 15 | import android.view.ContextThemeWrapper; 16 | import android.view.View; 17 | import android.view.View.OnClickListener; 18 | import android.view.ViewGroup; 19 | import android.view.ViewParent; 20 | import android.view.Window; 21 | import android.view.WindowManager; 22 | import android.widget.ImageView; 23 | import android.widget.TextView; 24 | import android.widget.Toast; 25 | 26 | import androidx.annotation.NonNull; 27 | import androidx.annotation.Nullable; 28 | import androidx.fragment.app.Fragment; 29 | import androidx.fragment.app.FragmentActivity; 30 | import androidx.fragment.app.FragmentManager; 31 | import androidx.recyclerview.widget.RecyclerView; 32 | import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; 33 | 34 | import me.ele.uetool.base.Application; 35 | 36 | import java.lang.reflect.Field; 37 | import java.lang.reflect.Method; 38 | import java.util.ArrayList; 39 | import java.util.Arrays; 40 | import java.util.List; 41 | import java.util.Map; 42 | import me.ele.uetool.base.ReflectionP; 43 | import me.ele.uetool.base.ReflectionP.Func; 44 | 45 | import static android.view.View.NO_ID; 46 | 47 | public class Util { 48 | 49 | public static void enableFullscreen(@NonNull Window window) { 50 | if (Build.VERSION.SDK_INT >= 21) { 51 | addSystemUiFlag(window, 1280); 52 | } 53 | } 54 | 55 | private static void addSystemUiFlag(Window window, int flag) { 56 | View view = window.getDecorView(); 57 | if (view != null) { 58 | view.setSystemUiVisibility(view.getSystemUiVisibility() | flag); 59 | } 60 | } 61 | 62 | public static void setStatusBarColor(@NonNull Window window, int color) { 63 | if (Build.VERSION.SDK_INT >= 21) { 64 | window.setStatusBarColor(color); 65 | } 66 | } 67 | 68 | public static String getViewClickListener(final View view) { 69 | return ReflectionP.breakAndroidP(new Func() { 70 | @Override public String call() { 71 | try { 72 | final Field mListenerInfoField = View.class.getDeclaredField("mListenerInfo"); 73 | mListenerInfoField.setAccessible(true); 74 | final Field mClickListenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener"); 75 | mClickListenerField.setAccessible(true); 76 | OnClickListener listener = (OnClickListener) mClickListenerField.get(mListenerInfoField.get(view)); 77 | return listener.getClass().getName(); 78 | } catch (Exception e) { 79 | return null; 80 | } 81 | } 82 | }); 83 | } 84 | 85 | public static String getResourceName(int id) { 86 | Resources resources = Application.getApplicationContext().getResources(); 87 | try { 88 | if (id == NO_ID || id == 0) { 89 | return ""; 90 | } else { 91 | return resources.getResourceEntryName(id); 92 | } 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | return ""; 97 | } 98 | 99 | public static String getResId(View view) { 100 | try { 101 | int id = view.getId(); 102 | if (id == NO_ID) { 103 | return ""; 104 | } else { 105 | return "0x" + Integer.toHexString(id); 106 | } 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | } 110 | return ""; 111 | } 112 | 113 | public static String intToHexColor(int color) { 114 | return "#" + Integer.toHexString(color).toUpperCase(); 115 | } 116 | 117 | public static Object getBackground(View view) { 118 | Drawable drawable = view.getBackground(); 119 | if (drawable instanceof ColorDrawable) { 120 | return intToHexColor(((ColorDrawable) drawable).getColor()); 121 | } else if (drawable instanceof GradientDrawable) { 122 | try { 123 | Field mFillPaintField = GradientDrawable.class.getDeclaredField("mFillPaint"); 124 | mFillPaintField.setAccessible(true); 125 | Paint mFillPaint = (Paint) mFillPaintField.get(drawable); 126 | Shader shader = mFillPaint.getShader(); 127 | if (shader instanceof LinearGradient) { 128 | Field mColorsField = LinearGradient.class.getDeclaredField("mColors"); 129 | mColorsField.setAccessible(true); 130 | int[] mColors = (int[]) mColorsField.get(shader); 131 | StringBuilder sb = new StringBuilder(); 132 | for (int i = 0, N = mColors.length; i < N; i++) { 133 | sb.append(intToHexColor(mColors[i])); 134 | if (i < N - 1) { 135 | sb.append(" -> "); 136 | } 137 | } 138 | return sb.toString(); 139 | } 140 | } catch (NoSuchFieldException e) { 141 | e.printStackTrace(); 142 | } catch (IllegalAccessException e) { 143 | e.printStackTrace(); 144 | } 145 | } else { 146 | return getDrawableBitmap(drawable); 147 | } 148 | return null; 149 | } 150 | 151 | public static List> getTextViewBitmap(TextView textView) { 152 | List> bitmaps = new ArrayList<>(); 153 | bitmaps.addAll(getTextViewDrawableBitmap(textView)); 154 | bitmaps.addAll(getTextViewImageSpanBitmap(textView)); 155 | return bitmaps; 156 | } 157 | 158 | private static List> getTextViewDrawableBitmap(TextView textView) { 159 | List> bitmaps = new ArrayList<>(); 160 | try { 161 | Drawable[] drawables = textView.getCompoundDrawables(); 162 | bitmaps.add(new Pair<>("DrawableLeft", getDrawableBitmap(drawables[0]))); 163 | bitmaps.add(new Pair<>("DrawableTop", getDrawableBitmap(drawables[1]))); 164 | bitmaps.add(new Pair<>("DrawableRight", getDrawableBitmap(drawables[2]))); 165 | bitmaps.add(new Pair<>("DrawableBottom", getDrawableBitmap(drawables[3]))); 166 | } catch (Exception e) { 167 | e.printStackTrace(); 168 | } 169 | return bitmaps; 170 | } 171 | 172 | private static List> getTextViewImageSpanBitmap(TextView textView) { 173 | List> bitmaps = new ArrayList<>(); 174 | try { 175 | CharSequence text = textView.getText(); 176 | if (text instanceof SpannedString) { 177 | Field mSpansField = Class.forName("android.text.SpannableStringInternal").getDeclaredField("mSpans"); 178 | mSpansField.setAccessible(true); 179 | Object[] spans = (Object[]) mSpansField.get(text); 180 | for (Object span : spans) { 181 | if (span instanceof ImageSpan) { 182 | bitmaps.add(new Pair<>("SpanBitmap", getDrawableBitmap(((ImageSpan) span).getDrawable()))); 183 | } 184 | } 185 | } 186 | } catch (Exception e) { 187 | e.printStackTrace(); 188 | } 189 | return bitmaps; 190 | } 191 | 192 | public static Bitmap getImageViewBitmap(ImageView imageView) { 193 | return getDrawableBitmap(imageView.getDrawable()); 194 | } 195 | 196 | private static Bitmap getDrawableBitmap(Drawable drawable) { 197 | try { 198 | if (drawable instanceof BitmapDrawable) { 199 | return ((BitmapDrawable) drawable).getBitmap(); 200 | } else if (drawable instanceof NinePatchDrawable) { 201 | NinePatch ninePatch = null; 202 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 203 | Field mNinePatchStateFiled = NinePatchDrawable.class.getDeclaredField("mNinePatchState"); 204 | mNinePatchStateFiled.setAccessible(true); 205 | Object mNinePatchState = mNinePatchStateFiled.get(drawable); 206 | Field mNinePatchFiled = mNinePatchState.getClass().getDeclaredField("mNinePatch"); 207 | mNinePatchFiled.setAccessible(true); 208 | ninePatch = (NinePatch) mNinePatchFiled.get(mNinePatchState); 209 | return ninePatch.getBitmap(); 210 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 211 | Field mNinePatchFiled = NinePatchDrawable.class.getDeclaredField("mNinePatch"); 212 | mNinePatchFiled.setAccessible(true); 213 | ninePatch = (NinePatch) mNinePatchFiled.get(drawable); 214 | return ninePatch.getBitmap(); 215 | } 216 | } else if (drawable instanceof ClipDrawable) { 217 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 218 | return ((BitmapDrawable) ((ClipDrawable) drawable).getDrawable()).getBitmap(); 219 | } 220 | } else if (drawable instanceof StateListDrawable) { 221 | return ((BitmapDrawable) drawable.getCurrent()).getBitmap(); 222 | } else if (drawable instanceof VectorDrawableCompat) { 223 | Field mVectorStateField = VectorDrawableCompat.class.getDeclaredField("mVectorState"); 224 | mVectorStateField.setAccessible(true); 225 | Field mCachedBitmapField = Class.forName("android.support.graphics.drawable.VectorDrawableCompat$VectorDrawableCompatState").getDeclaredField("mCachedBitmap"); 226 | mCachedBitmapField.setAccessible(true); 227 | return (Bitmap) mCachedBitmapField.get(mVectorStateField.get(drawable)); 228 | } 229 | } catch (Exception e) { 230 | e.printStackTrace(); 231 | } 232 | return null; 233 | } 234 | 235 | public static String getImageViewScaleType(ImageView imageView) { 236 | return imageView.getScaleType().name(); 237 | } 238 | 239 | public static void clipText(String clipText) { 240 | Context context = Application.getApplicationContext(); 241 | ClipData clipData = ClipData.newPlainText("", clipText); 242 | ((ClipboardManager) (context.getSystemService(Context.CLIPBOARD_SERVICE))).setPrimaryClip(clipData); 243 | Toast.makeText(context, "copied", Toast.LENGTH_SHORT).show(); 244 | } 245 | 246 | // 获取当前 view 所在的最上层 fragment 247 | @Nullable 248 | public static Fragment getCurrentFragment(View targetView) { 249 | 250 | Activity activity = UETool.getInstance().getTargetActivity(); 251 | if (activity instanceof FragmentActivity) { 252 | List fragments = collectVisibleFragment(((FragmentActivity) activity).getSupportFragmentManager()); 253 | for (int i = fragments.size() - 1; i >= 0; i--) { 254 | Fragment fragment = fragments.get(i); 255 | if (findTargetView(fragment.getView(), targetView)) { 256 | return fragment; 257 | } 258 | } 259 | } 260 | 261 | return null; 262 | } 263 | 264 | // 收集所有可见 fragment 265 | private static List collectVisibleFragment(FragmentManager fragmentManager) { 266 | List fragments = new ArrayList<>(); 267 | 268 | for (Fragment fragment : fragmentManager.getFragments()) { 269 | if (fragment.isVisible()) { 270 | fragments.add(fragment); 271 | fragments.addAll(collectVisibleFragment(fragment.getChildFragmentManager())); 272 | } 273 | } 274 | 275 | return fragments; 276 | } 277 | 278 | // 获取当前 fragment 类名 279 | @Nullable 280 | public static String getCurrentFragmentName(View targetView) { 281 | 282 | Fragment fragment = getCurrentFragment(targetView); 283 | 284 | if (fragment != null) { 285 | return fragment.getClass().getName(); 286 | } 287 | 288 | return null; 289 | } 290 | 291 | // 获取当前 view 的 view holder 类名 292 | public static String getViewHolderName(View targetView) { 293 | View currentView = targetView; 294 | while (currentView != null) { 295 | ViewParent parent = currentView.getParent(); 296 | if (parent instanceof RecyclerView) { 297 | return ((RecyclerView) parent).getChildViewHolder(currentView).getClass().getName(); 298 | } 299 | currentView = parent instanceof View ? (View) parent : null; 300 | } 301 | return null; 302 | } 303 | 304 | // 遍历目标 view 是否在指定 view 内 305 | private static boolean findTargetView(View view, View targetView) { 306 | if (view == targetView) { 307 | return true; 308 | } 309 | if (view instanceof ViewGroup) { 310 | ViewGroup parent = (ViewGroup) view; 311 | for (int i = 0; i < parent.getChildCount(); i++) { 312 | if (findTargetView(parent.getChildAt(i), targetView)) { 313 | return true; 314 | } 315 | } 316 | } 317 | return false; 318 | } 319 | 320 | 321 | public static View getCurrentView(Activity activity){ 322 | try { 323 | Activity targetActivity = UETool.getInstance().getTargetActivity(); 324 | WindowManager windowManager = targetActivity.getWindowManager(); 325 | Field mGlobalField = activity.getClass().forName("android.view.WindowManagerImpl").getDeclaredField("mGlobal"); 326 | mGlobalField.setAccessible(true); 327 | 328 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { 329 | Field mViewsField = activity.getClass().forName("android.view.WindowManagerGlobal").getDeclaredField("mViews"); 330 | mViewsField.setAccessible(true); 331 | List views = (List) mViewsField.get(mGlobalField.get(windowManager)); 332 | for (int i = views.size() - 1; i >= 0; i--) { 333 | View targetView = getTargetDecorView(targetActivity, views.get(i)); 334 | if (targetView != null) { 335 | return targetView; 336 | } 337 | } 338 | } else { 339 | Field mRootsField = activity.getClass().forName("android.view.WindowManagerGlobal").getDeclaredField("mRoots"); 340 | mRootsField.setAccessible(true); 341 | List viewRootImpls; 342 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 343 | viewRootImpls = (List) mRootsField.get(mGlobalField.get(windowManager)); 344 | } else { 345 | viewRootImpls = Arrays.asList((Object[]) mRootsField.get(mGlobalField.get(windowManager))); 346 | } 347 | for (int i = viewRootImpls.size() - 1; i >= 0; i--) { 348 | Class clazz = activity.getClass().forName("android.view.ViewRootImpl"); 349 | Object object = viewRootImpls.get(i); 350 | Field mWindowAttributesField = clazz.getDeclaredField("mWindowAttributes"); 351 | mWindowAttributesField.setAccessible(true); 352 | Field mViewField = clazz.getDeclaredField("mView"); 353 | mViewField.setAccessible(true); 354 | View decorView = (View) mViewField.get(object); 355 | WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mWindowAttributesField.get(object); 356 | if (layoutParams.getTitle().toString().contains(targetActivity.getClass().getName()) 357 | || getTargetDecorView(targetActivity, decorView) != null) { 358 | return decorView; 359 | } 360 | } 361 | } 362 | } catch (Exception e) { 363 | e.printStackTrace(); 364 | } 365 | return null; 366 | } 367 | 368 | private static View getTargetDecorView(Activity targetActivity, View decorView) { 369 | View targetView = null; 370 | Context context = decorView.getContext(); 371 | if (context == targetActivity) { 372 | targetView = decorView; 373 | } else { 374 | while (context instanceof ContextThemeWrapper) { 375 | Context baseContext = ((ContextThemeWrapper) context).getBaseContext(); 376 | if (baseContext == targetActivity) { 377 | targetView = decorView; 378 | break; 379 | } 380 | context = baseContext; 381 | } 382 | } 383 | return targetView; 384 | } 385 | public static Activity getCurrentActivity() { 386 | try { 387 | Class activityThreadClass = Class.forName("android.app.ActivityThread"); 388 | Method currentActivityThreadMethod = activityThreadClass.getMethod("currentActivityThread"); 389 | Object currentActivityThread = currentActivityThreadMethod.invoke(null); 390 | Field mActivitiesField = activityThreadClass.getDeclaredField("mActivities"); 391 | mActivitiesField.setAccessible(true); 392 | Map activities = (Map) mActivitiesField.get(currentActivityThread); 393 | for (Object record : activities.values()) { 394 | Class recordClass = record.getClass(); 395 | Log.d("UETMenu","currentTopActivity1234:"+recordClass.getSimpleName()); 396 | Field pausedField = recordClass.getDeclaredField("paused"); 397 | pausedField.setAccessible(true); 398 | if (!(boolean) pausedField.get(record)) { 399 | Field activityField = recordClass.getDeclaredField("activity"); 400 | activityField.setAccessible(true); 401 | Activity activity=(Activity) activityField.get(record); 402 | Log.d("UETMenu","currentTopActivity123:"+activity.getLocalClassName()); 403 | return activity; 404 | } 405 | } 406 | } catch (Exception e) { 407 | Log.d("UETMenu","currentTopActivity123::"+e.getMessage()); 408 | e.printStackTrace(); 409 | } 410 | Log.d("UETMenu","currentTopActivity123 为空"); 411 | return null; 412 | } 413 | } --------------------------------------------------------------------------------