├── .gitignore ├── .run ├── layout-inspector-v2 [buildPlugin].run.xml └── layout-inspector-v2 [runIde].run.xml ├── LICENSE ├── README.md ├── benchmark ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── eric │ │ └── benchmark │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── eric │ │ │ └── benchmark │ │ │ ├── DumpSpeedActivity.kt │ │ │ ├── EasierSelectionActivity.kt │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_dump_speed.xml │ │ ├── activity_easier_selection.xml │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-anydpi-v33 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── eric │ └── benchmark │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── plugin ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── android │ │ ├── layoutinspectorv2 │ │ ├── Icons.kt │ │ ├── LIV2EditorProvider.kt │ │ ├── LayoutInspectorBridge.kt │ │ ├── LayoutInspectorCaptureOptions.kt │ │ ├── LayoutInspectorResult.kt │ │ ├── model │ │ │ ├── ClientWindow.kt │ │ │ ├── DisplayInfo.kt │ │ │ ├── LayoutFileData.kt │ │ │ ├── ViewNode.kt │ │ │ ├── ViewNodeTableModel.kt │ │ │ └── ViewProperty.kt │ │ └── parser │ │ │ ├── DisplayInfoFactory.kt │ │ │ ├── LayoutFileDataParser.kt │ │ │ ├── ViewNodeParser.kt │ │ │ ├── ViewNodeV2Decoder.kt │ │ │ ├── ViewNodeV2Parser.kt │ │ │ └── ViewPropertyParser.kt │ │ └── tools │ │ ├── idea │ │ └── editors │ │ │ └── layoutInspectorv2 │ │ │ ├── AndroidLayoutInspectorService.kt │ │ │ ├── EditHandler.kt │ │ │ ├── LayoutInspectorCaptureTask.java │ │ │ ├── LayoutInspectorContext.java │ │ │ ├── LayoutInspectorEditHandler.kt │ │ │ ├── LayoutInspectorEditor.java │ │ │ ├── LayoutInspectorEditorPanel.java │ │ │ ├── LayoutInspectorFileHelper.kt │ │ │ ├── LayoutInspectorFileType.java │ │ │ ├── LayoutInspectorModel.kt │ │ │ ├── NotificationFixHelper.kt │ │ │ ├── WindowPickerDialog.java │ │ │ ├── actions │ │ │ ├── AndroidRunLayoutInspectorAction.java │ │ │ ├── GetClientWindowsTask.java │ │ │ ├── LoadOverlayAction.kt │ │ │ └── SetOverlayAlphaAction.kt │ │ │ ├── impl │ │ │ └── AndroidLayoutInspectorServiceImpl.kt │ │ │ ├── ptable │ │ │ ├── LIComponentEditor.kt │ │ │ ├── LIEditableItemCellRenderer.kt │ │ │ ├── LITableCellEditor.kt │ │ │ ├── LITableGroupItem.kt │ │ │ ├── LITableItem.kt │ │ │ ├── LITableNameRenderer.java │ │ │ └── LITableRendererProvider.java │ │ │ └── ui │ │ │ ├── LayoutInspectorPanel.java │ │ │ ├── LayoutTreeDefinition.java │ │ │ ├── LayoutTreePanel.java │ │ │ ├── PropertiesDefinition.java │ │ │ ├── PropertiesTablePanel.java │ │ │ ├── RollOverTree.java │ │ │ ├── ViewNodeActiveDisplay.java │ │ │ └── ViewNodeTreeRenderer.java │ │ └── property │ │ └── ptablev2 │ │ ├── PNameRenderer.java │ │ ├── PTable.java │ │ ├── PTableCellEditor.java │ │ ├── PTableCellEditorProvider.java │ │ ├── PTableCellRenderer.java │ │ ├── PTableCellRendererProvider.java │ │ ├── PTableDefaultCellRendererProvider.java │ │ ├── PTableGroupItem.java │ │ ├── PTableItem.java │ │ ├── PTableModel.java │ │ └── StarState.java │ └── resources │ ├── META-INF │ ├── plugin.xml │ └── pluginIcon.svg │ └── icons │ ├── clear-overlay.svg │ ├── icon.svg │ └── load-overlay.svg ├── screenshots ├── img.png ├── img_1.png ├── img_2.png ├── img_3.png └── img_4.png └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | .idea/* 4 | local.properties 5 | captures/ -------------------------------------------------------------------------------- /.run/layout-inspector-v2 [buildPlugin].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | -------------------------------------------------------------------------------- /.run/layout-inspector-v2 [runIde].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LayoutInspectorV2-Pro 2 | [![Version][badge:version]][plugin-versions] 3 | [![Downloads][badge:downloads]][plugin-homepage] 4 | 5 | 6 | [plugin-homepage]: https://plugins.jetbrains.com/plugin/21047-layoutinspectorv2-pro 7 | [plugin-versions]: https://plugins.jetbrains.com/plugin/21047-layoutinspectorv2-pro/versions 8 | [badge:version]: https://img.shields.io/jetbrains/plugin/v/21047.svg?style=flat-square&colorB=2196F3 9 | [badge:downloads]: https://img.shields.io/jetbrains/plugin/d/21047.svg?style=flat-square&colorB=5C6BC0 10 | 11 | LayoutInspectorV2-Pro is an Android Studio/Intellij plugin that allows Android developers to dump the view hierarchy. It is similar to the Legacy-Layout-LayoutInspector in some ways. 12 | 13 | > For convenience, LegacyLayoutInspector is abbreviated as LLI. 14 | 15 | ## Installation 16 | Preferences(Settings) > Plugins > Marketplace > Search and find "LayoutInspectorV2-Pro" > Install Plugin 17 | 18 | Restart the **IDE** after installation. 19 | 20 | your-image 21 | 22 | 23 | ## Usage 24 | 25 | You can find the plugin in the Tools menu.After the dump, LayoutInspectorV2-Pro generates a file with the `.liv2` extension, just like the `.li` file. 26 | 27 | your-image 28 | 29 | ## Why Use LayoutInspectorV2-Pro? 30 | 31 | The Google Android team removed the Legacy-LayoutInspector from Android Studio Bumblebee. If you are a fan of LLI, this plugin will be useful for you. Based on LLI, LayoutInspectorV2-Pro has made some improvements, including: 32 | 33 | 1. Faster dump speed 34 | 2. Easier selection of the view you want 35 | 36 | The following section explains these improvements in detail. 37 | 38 | ### Dump Speed 39 | 40 | LLI provides two ProtocolVersions: `ProtocolVersions.V1` and `ProtocolVersions.V2`. According to the [source code](https://cs.android.com/android/_/android/platform/frameworks/base/+/0d857b9028f2702ce439e13feccde8182d40e1e5), this feature was added in 2015. 41 | 42 | ![img.png](screenshots/img.png) 43 | 44 | LLI uses the V1 version by default to dump, but the V1 version is very slow. You may have seen a pop-up window warning of timeout. 45 | 46 | your-image 47 | 48 | Unfortunately, only internal Google engineers can use the V2 version. Therefore, LayoutInspectorV2-Pro uses the V2 version by default. I tested the dump speed of these two versions on my OnePlus 7 device with various numbers of views, and here are the results: 49 | 50 | | | 3000 views | 4000 views | 5000 views | 51 | |--------------------------|------------|------------|------------| 52 | | ProtocolVersions.V1 (ms) | 4101 | 5352 | 6577 | 53 | | ProtocolVersions.V2 (ms) | 625 | 724 | 837 | 54 | 55 | ### Easier selection 56 | 57 | Many Android developers who use LLI may be confused by its default selection algorithm, which prioritizes the topmost view. However, in most cases, the sizes of areas should carry more weight when selecting a view. 58 | 59 | Consider the following scenario: there are two views, A and B, and B is on top of A. If you click the A area in the image panel, LLI may not select A because B is larger and covers A. 60 | 61 | your-image 62 | 63 | With LayoutInspectorV2-Pro, the sizes of areas between A and B are compared, and A is selected because it is smaller than B. This approach ensures that the most appropriate view is selected. 64 | 65 | ## Supported Versions 66 | 67 | LayoutInspectorV2-Pro should work with most Android Studio versions. The following Android Studio versions have been tested: 68 | 69 | | Version | Build Number | 70 | |-------------------------------|-----------------------------| 71 | | Bumblebee 2021.1.1 | 211.7628.21.2111.8092744 | 72 | | Chipmunk 2021.2.1 Patch 2 | 212.5712.43.2112.8815526 | 73 | | Dolphin 2021.3.1 | 213.7172.25.2113.9014738 | 74 | | Electric Eel 2022.1.1 Patch 1 | 221.6008.13.2211.9514443 | 75 | | Flamingo 2022.2.1 Canary 10 | 222.4459.24.2221.9409768 | 76 | | Giraffe 2022.3.1 Canary 8 | 223.8617.56.2231.9687552 | 77 | | Hedgehog 2023.1.1 Beta 5 | 231.9392.1.2311.10809438 | 78 | | Iguana 2023.2.1 Canary 5 | 232.9921.47.2321.10840167 | 79 | | Android Studio Jellyfish | 233.13135.103.2331.11360849 | 80 | 81 | If you encounter any issues in any version, please report them. 82 | 83 | ## Reporting Issues 84 | 85 | If you encounter any issues while using LayoutInspectorV2-Pro, please report them by creating an issue in the project's issue tracker. To create an issue, please follow these steps: 86 | 87 | 1. Go to the [issue tracker](https://github.com/CoXier/LayoutInspectorV2-Pro/issues). 88 | 2. Click on the "New Issue" button. 89 | 3. Fill out the form with as much detail as possible. 90 | 4. Click on the "Submit new issue" button. 91 | 92 | ## Contributing 93 | 94 | If you would like to contribute to this project, please follow these steps: 95 | 96 | 1. Fork the project. 97 | 2. Create a new branch. 98 | 3. Make your changes. 99 | 4. Submit a pull request. 100 | 101 | ## Show Your Support 102 | 103 | If you find this project helpful, please consider giving it a star. Your support is greatly appreciated! 104 | 105 | 106 | ## License 107 | 108 | This project is licensed under the Apache License 2.0. See the `LICENSE` file for more information. 109 | 110 | -------------------------------------------------------------------------------- /benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /benchmark/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") version "7.4.1" 3 | id("org.jetbrains.kotlin.android") version "1.8.0" 4 | } 5 | 6 | android { 7 | namespace = "com.eric.benchmark" 8 | compileSdk = 33 9 | 10 | defaultConfig { 11 | applicationId = "com.eric.benchmark" 12 | minSdk = 24 13 | targetSdk = 33 14 | versionCode = 1 15 | versionName = "1.0" 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | isMinifyEnabled = false 23 | proguardFiles( 24 | getDefaultProguardFile("proguard-android-optimize.txt"), 25 | "proguard-rules.pro" 26 | ) 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility = JavaVersion.VERSION_1_8 31 | targetCompatibility = JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = "1.8" 35 | } 36 | } 37 | 38 | dependencies { 39 | implementation("androidx.appcompat:appcompat:1.4.1") 40 | implementation("androidx.constraintlayout:constraintlayout:2.1.3") 41 | implementation("com.google.android.material:material:1.4.+") 42 | testImplementation("junit:junit:4.13.2") 43 | androidTestImplementation("androidx.test.ext:junit:1.1.1") 44 | androidTestImplementation("androidx.test.espresso:espresso-core:3.1.0") 45 | } -------------------------------------------------------------------------------- /benchmark/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 -------------------------------------------------------------------------------- /benchmark/src/androidTest/java/com/eric/benchmark/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.eric.benchmark 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.eric.benchmark", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /benchmark/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /benchmark/src/main/java/com/eric/benchmark/DumpSpeedActivity.kt: -------------------------------------------------------------------------------- 1 | package com.eric.benchmark 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.Button 8 | import android.widget.LinearLayout 9 | 10 | class DumpSpeedActivity : AppCompatActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(R.layout.activity_dump_speed) 14 | 15 | val array = intArrayOf(3000, 4000, 5000, 6000) 16 | findViewById(R.id.root).apply { 17 | 18 | array.forEach {count-> 19 | val button = Button(this@DumpSpeedActivity) 20 | addView(button, LinearLayout.LayoutParams(720, 120)) 21 | button.text = "add $count views" 22 | button.setOnClickListener { 23 | addView(count) 24 | } 25 | } 26 | 27 | } 28 | } 29 | 30 | private fun addView(count: Int) { 31 | findViewById(R.id.container).apply { 32 | this.removeAllViews() 33 | for (i in 0 until count) { 34 | addView(View(this@DumpSpeedActivity)) 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /benchmark/src/main/java/com/eric/benchmark/EasierSelectionActivity.kt: -------------------------------------------------------------------------------- 1 | package com.eric.benchmark 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | 6 | class EasierSelectionActivity : AppCompatActivity() { 7 | override fun onCreate(savedInstanceState: Bundle?) { 8 | super.onCreate(savedInstanceState) 9 | setContentView(R.layout.activity_easier_selection) 10 | } 11 | } -------------------------------------------------------------------------------- /benchmark/src/main/java/com/eric/benchmark/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.eric.benchmark 2 | 3 | import android.content.Intent 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.view.View 7 | import android.view.ViewGroup 8 | 9 | class MainActivity : AppCompatActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_main) 13 | findViewById(R.id.dump_speed).setOnClickListener { 14 | startActivity(Intent(this, DumpSpeedActivity::class.java)) 15 | } 16 | findViewById(R.id.easier_selection).setOnClickListener { 17 | startActivity(Intent(this, EasierSelectionActivity::class.java)) 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /benchmark/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /benchmark/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 | -------------------------------------------------------------------------------- /benchmark/src/main/res/layout/activity_dump_speed.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /benchmark/src/main/res/layout/activity_easier_selection.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | -------------------------------------------------------------------------------- /benchmark/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |