Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce SlotTableInspector tool for Compose. #142

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sample-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ tasks.withType<KotlinCompile> {

dependencies {
implementation(project(":radiography"))
implementation(project(":slot-table-inspector"))
implementation(Dependencies.AppCompat)
implementation(Dependencies.Compose(sampleComposeVersion).Activity())
implementation(Dependencies.Compose(sampleComposeVersion).Material)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Checkbox
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TextField
Expand All @@ -31,15 +32,19 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import radiography.ExperimentalRadiographyComposeApi
import radiography.Radiography
import radiography.ScanScopes.FocusedWindowScope
Expand All @@ -50,6 +55,9 @@ import radiography.ViewStateRenderers.DefaultsNoPii
import radiography.ViewStateRenderers.ViewRenderer
import radiography.ViewStateRenderers.androidViewStateRendererFor
import radiography.ViewStateRenderers.textViewRenderer
import radiography.compose.slottable.SlotTableInspectable
import radiography.compose.slottable.SlotTableInspector
import radiography.compose.slottable.SlotTableInspectorState

internal const val TEXT_FIELD_TEST_TAG = "text-field"
internal const val LIVE_HIERARCHY_TEST_TAG = "live-hierarchy"
Expand All @@ -59,84 +67,115 @@ internal const val LIVE_HIERARCHY_TEST_TAG = "live-hierarchy"
ComposeSampleApp()
}

@OptIn(ExperimentalRadiographyComposeApi::class, ExperimentalAnimationApi::class)
@OptIn(
ExperimentalRadiographyComposeApi::class,
ExperimentalAnimationApi::class,
ExperimentalComposeUiApi::class,
)
@Composable fun ComposeSampleApp() {
val context = LocalContext.current
val liveHierarchy = remember { mutableStateOf<String?>(null) }
val slotTableInspectorState = remember { SlotTableInspectorState() }
var showSlotTableInspector by remember { mutableStateOf(false) }

var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var rememberMe by remember { mutableStateOf(false) }

MaterialTheme {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
RadiographyLogo(Modifier.height(128.dp))
SlotTableInspectable(slotTableInspectorState) {
MaterialTheme {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
RadiographyLogo(Modifier.height(128.dp))

TextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") },
colors = TextFieldDefaults.outlinedTextFieldColors(),
modifier = Modifier.testTag(TEXT_FIELD_TEST_TAG)
)
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
colors = TextFieldDefaults.outlinedTextFieldColors(),
visualTransformation = PasswordVisualTransformation()
)
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = rememberMe, onCheckedChange = { rememberMe = it })
Spacer(Modifier.width(8.dp))
Text("Remember me")
}

Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
TextButton(onClick = {}) {
Text("SIGN IN")
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") },
colors = TextFieldDefaults.outlinedTextFieldColors(),
modifier = Modifier.testTag(TEXT_FIELD_TEST_TAG)
)
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
colors = TextFieldDefaults.outlinedTextFieldColors(),
visualTransformation = PasswordVisualTransformation()
)
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = rememberMe, onCheckedChange = { rememberMe = it })
Spacer(Modifier.width(8.dp))
Text("Remember me")
}
TextButton(onClick = {}) {
Text("FORGOT PASSWORD")

Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
TextButton(onClick = {}) {
Text("SIGN IN")
}
TextButton(onClick = {}) {
Text("FORGOT PASSWORD")
}
}
}

// Include a classic Android view in the composition.
AndroidView(::TextView) {
@SuppressLint("SetTextI18n")
it.text = "By signing in, you agree to our Terms and Conditions."
}
// Include a classic Android view in the composition.
AndroidView(::TextView) {
@SuppressLint("SetTextI18n")
it.text = "By signing in, you agree to our Terms and Conditions."
}

liveHierarchy.value?.let {
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.weight(1f)
) {
Column(Modifier.verticalScroll(rememberScrollState())) {
Text(
liveHierarchy.value.orEmpty(),
fontFamily = FontFamily.Monospace,
fontSize = 6.sp,
modifier = Modifier.testTag(LIVE_HIERARCHY_TEST_TAG)
)
liveHierarchy.value?.let {
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.weight(1f)
) {
Column(Modifier.verticalScroll(rememberScrollState())) {
Text(
liveHierarchy.value.orEmpty(),
fontFamily = FontFamily.Monospace,
fontSize = 6.sp,
modifier = Modifier.testTag(LIVE_HIERARCHY_TEST_TAG)
)
}
}
}
Row {
TextButton(
modifier = Modifier.weight(1f),
onClick = { showSelectionDialog(context) }
) {
Text("SHOW STRING RENDERING DIALOG", textAlign = TextAlign.Center)
}
TextButton(
modifier = Modifier.weight(1f),
onClick = {
slotTableInspectorState.captureSlotTables()
showSlotTableInspector = true
}) {
Text("SHOW SLOT TABLE INSPECTOR", textAlign = TextAlign.Center)
}
}
}
TextButton(onClick = { showSelectionDialog(context) }) {
Text("SHOW STRING RENDERING DIALOG")
}

SideEffect {
liveHierarchy.value = Radiography.scan(
viewStateRenderers = DefaultsIncludingPii,
// Don't trigger infinite recursion.
viewFilter = skipComposeTestTagsFilter(LIVE_HIERARCHY_TEST_TAG)
)
SideEffect {
liveHierarchy.value = Radiography.scan(
viewStateRenderers = DefaultsIncludingPii,
// Don't trigger infinite recursion.
viewFilter = skipComposeTestTagsFilter(LIVE_HIERARCHY_TEST_TAG)
)
}
if (showSlotTableInspector) {
Dialog(
onDismissRequest = { showSlotTableInspector = false },
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Surface {
SlotTableInspector(slotTableInspectorState)
}
}
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ include(
":compose-unsupported-tests",
":radiography",
":sample",
":sample-compose"
":sample-compose",
":slot-table-inspector",
)
11 changes: 11 additions & 0 deletions slot-table-inspector/api/slot-table-inspector.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public final class radiography/compose/slottable/SlotTableInspectorKt {
public static final fun SlotTableInspectable (Lradiography/compose/slottable/SlotTableInspectorState;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
public static final fun SlotTableInspector (Lradiography/compose/slottable/SlotTableInspectorState;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V
}

public final class radiography/compose/slottable/SlotTableInspectorState {
public static final field $stable I
public fun <init> ()V
public final fun captureSlotTables ()V
}

71 changes: 71 additions & 0 deletions slot-table-inspector/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("com.android.library")
kotlin("android")
id("com.vanniktech.maven.publish")
}

android {
compileSdk = 30

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

defaultConfig {
// Compose minSdk is also 21.
minSdk = 21
targetSdk = 30
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
buildConfig = false
compose = true
}

composeOptions {
kotlinCompilerExtensionVersion = Versions.Compose
}

testOptions {
execution = "ANDROIDX_TEST_ORCHESTRATOR"
}
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += listOfNotNull(
"-Xopt-in=kotlin.RequiresOptIn",

// Require explicit public modifiers and types.
// TODO this should be moved to a top-level `kotlin { explicitApi() }` once that's working
// for android projects, see https://youtrack.jetbrains.com/issue/KT-37652.
"-Xexplicit-api=strict".takeUnless {
// Tests aren't part of the public API, don't turn explicit API mode on for them.
name.contains("test", ignoreCase = true)
}
)
}
}

dependencies {
implementation(Dependencies.Compose().Material)
// implementation(Dependencies.Compose().ToolingData)
implementation(Dependencies.Compose().Tooling)

testImplementation(Dependencies.JUnit)
testImplementation(Dependencies.Mockito)
testImplementation(Dependencies.Robolectric)
testImplementation(Dependencies.Truth)

androidTestImplementation(Dependencies.Compose().Testing)
androidTestImplementation(Dependencies.InstrumentationTests.Core)
androidTestImplementation(Dependencies.InstrumentationTests.Espresso)
androidTestImplementation(Dependencies.InstrumentationTests.Rules)
androidTestImplementation(Dependencies.InstrumentationTests.Runner)
androidTestImplementation(Dependencies.Truth)
androidTestUtil(Dependencies.InstrumentationTests.Orchestrator)
}
3 changes: 3 additions & 0 deletions slot-table-inspector/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=slot-table-inspector
POM_NAME=Radiography slot table inspector
POM_PACKAGING=aar
15 changes: 15 additions & 0 deletions slot-table-inspector/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.radiography.compose.slottable.test">

<!--
Workaround required for running tests on API 30 devices.
See https://github.com/android/android-test/issues/743.
Version 1.3.1 of the AndroidX Test libraries remove the need for this workaround.
-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

<application>
<activity android:name="androidx.activity.ComponentActivity" />
</application>
</manifest>

1 change: 1 addition & 0 deletions slot-table-inspector/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="com.squareup.radiography.compose.slottable"/>
Loading