🔦 Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements

Overview

Showkase

Showkase Version Compatible with Compose

Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements. With minimal configuration it generates a UI browser that helps you easily find your components, colors & typography. It also renders your components in common situations like dark mode, right-to-left layouts, and scaled fonts which help in finding issues early.

Why should you use Showkase?

  • When using component based UI toolkits (like React, Compose, Flutter, etc), our codebase often ends up with hundreds of components that are hard to discover, visualize, search and organize.
  • Showkase eliminates the manual work of maintaining a UI preview/browser app that each company is forced to build in order to maintain their design system.
  • Since all the available UI elements are now easily searchable and discoverable, there is better reuse of UI elements in your repo. This makes it useful for maintaining consistency across your app. The biggest problem for enforcing a design system is discoverability and Showkase hopefully solves that problem for your team.
  • It allows you to quickly visualize @Composable components, Color properties and TextStyle (Typography) as you are building them. The goal is to improve the turnaround time in creating production-ready UI elements.
  • Showkase aids in catching common UI issues early with the help of auto-generated permutations of your components.

Features

  • Super simple setup
  • Support for visualizing composables(@ShowkaseComposable), colors(@ShowkaseColor) & typography(@ShowkaseTypography).
  • First class support for @Preview annotation. If you are already using @Preview for previews in Android Studio, using Showkase is even easier as all those components are included in the Showkase browser.
  • Support for top level, class, object & companion object functions and properties to be annotated with the Showkase annotations.
  • 5 Permutations are auto created for each composable (Basic Example, Dark Mode, RTL, Font Scaled, Display Scaled. Look in the gif above for examples)'. More to be added in the future!
  • Support for searching a Showkase UI element by name or group.
  • KDoc support that shows the documentation that you added for a component in the Showkase browser.
  • Multi-module support for showcasing UI elements across multiple modules.
  • Support for constraining a component with a custom height/width using additional parameters in the annotation.
  • Descriptive error messages so that the users of the library can fix any incorrect setup.
  • Incremental annotation processor that makes the code-gen more performant.

Installation

Using Showkase is straightforward and takes just a couple of minutes to get started.

Step 1: Add the dependency to your module's build.gradle file. If you have a multi-module setup, add this dependency to all the modules with UI elements that should be displayed inside the Showkase browser.

implementation "com.airbnb.android:showkase:1.0.0-beta03"
kapt "com.airbnb.android:showkase-processor:1.0.0-beta03"

Step 2: Add the relevant annotations for every UI element that should be a part of the Showkase browser.

For @Composable components, you can either use the @Preview annotation that Compose comes with or use the @ShowkaseComposable annotation:

@Preview(name = "Custom name for component", group = "Custom group name")
@Composable
fun MyComponent() { ... }

// or

@ShowkaseComposable(name = "Name of component", group = "Group Name")
@Composable
fun MyComponent() { ... }

For Color properties, you can add the @ShowkaseColor annotation to the field:

@ShowkaseColor(name = "Primary Color", group = "Material Design")
val primaryColor = Color(0xFF6200EE)

For TextStyle properties that are useful for typography, you can add the @ShowkaseTypography annotation to the field:

@ShowkaseTypography(name = "Custom name for style", group = "Custom group name")
val h1 = TextStyle(
    fontWeight = FontWeight.Light,
    fontSize = 96.sp,
    letterSpacing = (-1.5).sp
)

Step 3: Define an implementation of the ShowkaseRootModule interface in your root module. If your setup involves only a single module, add this implementation in that module. Ensure that this implementation is also annotated with the @ShowkaseRoot annotation.

@ShowkaseRoot
class MyRootModule: ShowkaseRootModule

Step 4: Showkase is now ready for use! Showkase comes with an Activity that you need to start for accessing the UI browser. Typically you would start this activity from the debug menu of your app but you are free to start this from any place you like! A nice helper extension function getBrowserIntent is generated for you so you might have to build the app once before it's available for use. Just start the intent and that's all you need to do for accessing Showkase!

startActivity(Showkase.getBrowserIntent(context))

Documentation

1. @ShowkaseComposable

Used to annotate @Composable functions that should be displayed inside the Showkase browser. If you are using the @Preview annotation with your @Composable function already then you don't need to use this annotation. Showkase has first class support for @Preview.

Here's how you would use it with your @Composable function:

@ShowkaseComposable(name = "Name", group = "Group")
@Composable
fun MyComposable() {
    .......
    .......
}

Name and group are optional. Look at the properties section to understand the behavior when you don't pass any properties.

Note: Make sure that you add this annotation to only those functions that meet the following criteria:

  • Functions that don't have any parameters
  • If it does have a parameter, it has to be annotated with @PreviewParameter that is provided a PreviewParameterProvider implementation.

This is identical to how @Preview works in Compose as well so Showkase just adheres to the same rules.

{ override val values: Sequence get() = sequenceOf( Person("John", 12), Person("Doe", 20) ) override val count: Int get() = super.count } // Since we return 2 objects through our ParameterProvider, Showkase will create 2 previews // for this single composable function. Each preview will have it's own parameter that's passed // to it - Person("John", 12) for the first preview and Person("Doe", 20) for the second one. // This is an effective way to preview your composables against different sets of data. ">
// Consider a simple data class
data class Person(
    val name: String,
    val age: Int
)

// In order to pass a person object as a parameter to our composable function, we will annotate 
// our parameter with `@PreviewParameter` and pass a `PreviewParameterProvider` implementation.
@ShowkaseComposable(name = "Name", group = "Group") or @Preview(name = "Name", group = "Group")
@Composable
fun MyComposable(
    @PreviewParameter(provider = ParameterProvider::class) person: Person
) {
    ...
}

class ParameterProvider : PreviewParameterProvider<Person> {
    override val values: Sequence<Person>
        get() = sequenceOf(
            Person("John", 12),
            Person("Doe", 20)
        )

    override val count: Int
        get() = super.count
}

// Since we return 2 objects through our ParameterProvider, Showkase will create 2 previews 
// for this single composable function. Each preview will have it's own parameter that's passed 
// to it - Person("John", 12) for the first preview and Person("Doe", 20) for the second one. 
// This is an effective way to preview your composables against different sets of data.

Alternatively, you could simply wrap your function inside another function that doesn't accept any parameters -

@Composable
fun MyComposable(name: String) {
    .......
    .......
}

In order to make this function compatible with Showkase, you could further wrap this function inside a method that doesn't accept parameters in the following way:

@ShowkaseComposable(name = "Name", group = "Group")
@Composable
fun MyComposablePreview() {
    MyComposable("Name")
}
@ShowkaseComposable currently supports the following properties:
Property Name Description
name The name that should be used to describe your @Composable function. If you don't pass any value, the name of the composable function is used as the name.
group The grouping key that will be used to group it with other @Composable functions. This is useful for better organization and discoverability of your components. If you don't pass any value for the group, the name of the class that wraps this function is used as the group name. If the function is a top level function, the composable is added to a "Default Group".
widthDp The width that your component will be rendered inside the Showkase browser. Use this to restrict the size of your preview inside the Showkase browser.
heightDp The height that your component will be rendered inside the Showkase browser. Use this to restrict the size of your preview inside the Showkase browser.
skip Setting this to true will skip this composable from rendering in the Showkase browser. A good use case for this would be when you want to have composable with @Preview but want to stop Showkase from picking it up and rendering it in its browser
2. @ShowkaseColor

Used to annotate Color properties that should be presented inside the Showkase browser. Here's how you would use it with your Color fields:

@ShowkaseColor(name = "Name", group = "Group")
val redColor = Color.Red

@ShowkaseColor("Primary", "Light Colors")
val primaryColor = Color(0xFF6200EE)

Name and group are optional. Look at the properties section below to understand the behavior when you don't pass any properties.

@ShowkaseColor currently supports the following properties:
Property Name Description
name The name that should be used to describe your Color fields. If you don't pass any value, the name of the color field is used as the name.
group The grouping key that will be used to group it with other Color fields. This is useful for better organization and discoverability of your colors. If you don't pass any value for the group, the name of the class that wraps this field is used as the group name. If the field is a top level field, the color is added to a "Default Group".
3. @ShowkaseTypography

Used to annotate TextStyle properties that should be presented inside the Showkase browser. Here's how you would use it with your TextStyle fields:

@ShowkaseTypography(name = "Name", group = "Group")
val h1 = TextStyle(
    fontWeight = FontWeight.Light,
    fontSize = 96.sp,
    letterSpacing = (-1.5).sp
)

Name and group are optional. Look at the properties section below to understand the behavior when you don't pass any properties.

@ShowkaseTypography currently supports the following properties:
Property Name Description
name The name that should be used to describe your TextStyle fields. If you don't pass any value, the name of the textStyle field is used as the name.
group The grouping key that will be used to group it with other TextStyle fields. This is useful for better organization and discoverability of your typography. If you don't pass any value for the group, the name of the class that wraps this field is used as the group name. If the field is a top level field, the textStyle is added to a "Default Group".
4. @ShowkaseRoot

Used to annotate the ShowkaseRootModule implementation class. This is needed to let Showkase know more about the module that is going to be the root module for aggregating all the Showkase supported UI elements across all the different modules(if you are using a multi-module project). If you are only using a single module in your project, add it to that module. You are allowed to have only one @ShowkaseRoot per module.

Here's an example of how you would use it:

@ShowkaseRoot
fun MyRootModule: ShowkaseRootModule

Note: The root module is the main module of your app that has a dependency on all other modules in the app. This is relevant because we generate the Showkase related classes in the package of the root module and we need to be able to access the UI elements across all the sub modules. This is only possible from the root module as it typically has a dependency on all the sub-modules.

5. Showkase Object

The Showkase object is the receiver for all the helper methods that this library generates. Currently there are a few extension functions that are generated with the Showkase object as the receiver. In order to get access to these functions, you need to build the app once so that the methods can be generated for your use.

Extension function Description
getBrowserIntent Helper function that return an intent to start the ShowkaseBrowser activity.
getMetadata Helper function that's give's you access to Showkase metadata. This contains data about all the composables, colors and typography in your codebase that's rendered in the Showkase Browser.
// Example Usage

val intent = Showkase.getBrowserIntent(context)
startActivity(intent)

val metadata = Showkase.getMetadata()
val components = metadata.componentList
val colors= metadata.colorList
val typography = metadata.typographyList

Frequently Asked Questions

Is Airbnb using Jetpack Compose in their main app? Since Jetpack Compose is still super early, we haven't started using Compose just yet. However, given our history with declarative UI(we created Epoxy), we are super excited about Compose and are hoping to be able to use it once the API's are more stable.
Why did you create this library if you aren't using Compose in production? One of the biggest barriers to adopting new technology is the lack of tooling that you are otherwise used to having. We currently have an internal tool that works exactly like Showkase but for classic Android. We created Showkase to ensure that we have the tooling available to be able to move to Compose in the future. Moreover, we think that this tool would benefit everyone who's using Compose so we decided to open source it. Hopefully we can learn along with the community and add features that would benefit everyone.
Can I contribute to this library? Pull requests are welcome! We'd love help improving this library. Feel free to browse through open issues to look for things that need work. If you have a feature request or bug, please open a new issue so we can track it.
How do I provide feedback? The issues tab is the best place to do that.
Why can't we have a single annotation like `Showkase` for all the UI elements? Why did you create a different annotation for each UI element(@ShowkaseComposable for composables, @ShowkaseColor for colors & @ShowkaseTypography for text styles)? This was done mostly for future proofing. Even though these annotations have the same properties right now, it's possible that they will diverge as we add more features. Once more people start using this library, we will get a more clear idea about whether that needs to happen or not. If we find that it didn't evolve the way we expected, we will consider consildating these annotations.
I would like to customize my component browser. Can this library still be useful? We provide a nice helper function that gives you access to the metadata of all your UI elements that are configured to work with Showkase. You can use `Showkase.getMetadata()` to get access to it and then use it in whatever way you see fit.

Coming Soon!

Here are some ideas that we are thinking about. We are also not limited to these and would love to learn more about your use cases.

  • Support for passing a @PreviewParameter parameter to @Preview/@ShowkaseComposable components. (See https://github.com/airbnb/Showkase#1-showkasecomposable)
  • Hooks for screenshot testing. Since all your components are a part of the Showkase browser, this would be a good opportunity to make this a part of your CI and detect diffs in components. (The getMetadata method can be useful to accomplish this. More info here - https://github.com/airbnb/Showkase#5-showkase-object)
  • Support for other UI elements that are a part of your design system (like icons, spacing, etc)
  • Generating a web version of the Showkase browser with documentation, search and screenshots.

Contributing

Pull requests are welcome! We'd love help improving this library. Feel free to browse through open issues to look for things that need work. If you have a feature request or bug, please open a new issue so we can track it.

License

Copyright 2021 Airbnb, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Dark mode demo not working

    Dark mode demo not working

    Captura de pantalla 2021-03-08 a las 9 26 04

    Hi.

    I'm using this code to change (and animate) dark mode from a debug drawer:

    @Composable
    fun RestaurantMenuNumbersTheme(
        darkTheme: Boolean = isSystemInDarkTheme(),
        darkThemeState: MutableState<Boolean> = remember { mutableStateOf(darkTheme) },
        content: @Composable (MutableState<Boolean>) -> Unit,
    ) {
    
        val transition = updateTransition(targetState = darkThemeState.value)
    
        val colors = if (darkThemeState.value) {
            DarkColorPalette
        } else {
            LightColorPalette
        }
    
        val animateColors = Colors(
            primary = transition.animateColor { colors.primary }.value,
            primaryVariant = transition.animateColor { colors.primaryVariant }.value,
            secondary = transition.animateColor { colors.secondary }.value,
            secondaryVariant = transition.animateColor { colors.secondaryVariant }.value,
            background = transition.animateColor { colors.background }.value,
            surface = transition.animateColor { colors.surface }.value,
            error = transition.animateColor { colors.error }.value,
            onPrimary = transition.animateColor { colors.onPrimary }.value,
            onSecondary = transition.animateColor { colors.onSecondary }.value,
            onBackground = transition.animateColor { colors.onBackground }.value,
            onSurface = transition.animateColor { colors.onSurface }.value,
            onError = transition.animateColor { colors.onError }.value,
            isLight = colors.isLight,
        )
    
        MaterialTheme(
            colors = animateColors,
            typography = Typography,
            shapes = Shapes,
            content = { content(darkThemeState) }
        )
    }
    

    So, showkase not shows the dark mode / l right mode correctly

    opened by alorma 18
  • Can't update from beta08 to beta09

    Can't update from beta08 to beta09

    Hi, when I try to update from beta08 to beta09 (or beta10), the build fails with:

    Unresolved reference: getBrowserIntent

    (the extension function which does this is not generated)

    I can't reproduce it on my open-source project, only in my work project.

    I tried updating Kotlin to 1.5.31 and Compose to 1.0.4, but it didn't help. Any ideas what I can try to debug this?

    opened by davidvavra 17
  • doesn't support @Composables with default params

    doesn't support @Composables with default params

    Hopefully this is enough of a stacktrace with beta12:

    error: com.airbnb.android.showkase.processor.exceptions.ShowkaseProcessorException: Make sure that the @Composable functions that you annotate with the Preview annotation only have a single parameter that is annotated with @PreviewParameter.
      	at com.airbnb.android.showkase.processor.logging.ShowkaseValidator.validateComponentElement$showkase_processor(ShowkaseValidator.kt:59)
    Supported source version 'RELEASE_8' from annotation processor 'org.jetbrains.kotlin.kapt3.base.ProcessorWrapper' less than -source '11'
    
      	at com.airbnb.android.showkase.processor.ShowkaseProcessor.processPreviewAnnotation(ShowkaseProcessor.kt:101)
      	at com.airbnb.android.showkase.processor.ShowkaseProcessor.processComponentAnnotation(ShowkaseProcessor.kt:75)
    com.airbnb.android.showkase.processor.exceptions.ShowkaseProcessorException: Make sure that the @Composable functions that you annotate with the Preview annotation only have a single parameter that is annotated with @PreviewParameter.
    
      	at com.airbnb.android.showkase.processor.ShowkaseProcessor.process(ShowkaseProcessor.kt:57)
      	at com.airbnb.android.showkase.processor.BaseProcessor.internalProcess(BaseProcessor.kt:96)
      	at com.airbnb.android.showkase.processor.BaseProcessor.process(BaseProcessor.kt:64)
    
    
    opened by kenyee 16
  • Previewing a component that is scrollable

    Previewing a component that is scrollable

    Hi!

    I have a component that uses the verticalScroll modifier. Previewing it in AS works fine, but when opening the group which contains this composable in the Showkase activity, I get an exception saying:

    java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().
    

    How should I go about resolving this, or isn't this functionality taken into account yet?

    bug enhancement 
    opened by Dambakk 14
  • Can't build the project if app is a root module

    Can't build the project if app is a root module

    Hi, we have a multi-module project. Previously I had one module 'main' with some submodules containing previews and it worked. But we also have some modules which are not dependencies of 'main' module. Only the top-level 'app' module has a dependency to all modules. I tried to move @ShowkaseRoot to the 'app' module.

    Now building project fails with this exception:

    Caused by: java.lang.NullPointerException
    	at com.airbnb.android.showkase.processor.ShowkaseProcessor.init(ShowkaseProcessor.kt:61)
    	at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.init(incrementalProcessors.kt:41)
    	at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.init(annotationProcessing.kt:197)
    

    Any ideas what might help?

    opened by davidvavra 13
  • NullPointerException build error for 'showkase-processor-testing' module

    NullPointerException build error for 'showkase-processor-testing' module

    Steps taken

    • Fork the project. • Build the project.

    Expected

    • Project build successful.

    Actual

    • Build error thrown NullPointerException, with the following crashlog:

    Build file '/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle' line: 82
    
    A problem occurred evaluating project ':showkase-processor-testing'.
    > java.lang.NullPointerException (no error message)
    
    * Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    
    * Exception is:
    org.gradle.api.GradleScriptException: A problem occurred evaluating project ':showkase-processor-testing'.
    	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93)
    	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.lambda$apply$0(DefaultScriptPluginFactory.java:133)
    	at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77)
    	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:136)
    	at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.configuration.BuildOperationScriptPlugin.lambda$apply$0(BuildOperationScriptPlugin.java:62)
    	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:43)
    	at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:62)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:248)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:275)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:247)
    	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:42)
    	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
    	at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:35)
    	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:100)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:248)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$withProjectLock$3(DefaultProjectStateRegistry.java:308)
    	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:178)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:308)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:289)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:247)
    	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:91)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:63)
    	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:710)
    	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:145)
    	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:36)
    	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:64)
    	at org.gradle.configuration.DefaultProjectsPreparer.prepareProjects(DefaultProjectsPreparer.java:46)
    	at org.gradle.configuration.BuildTreePreparingProjectsPreparer.prepareProjects(BuildTreePreparingProjectsPreparer.java:57)
    	at org.gradle.configuration.BuildOperationFiringProjectsPreparer$ConfigureBuild.run(BuildOperationFiringProjectsPreparer.java:52)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.configuration.BuildOperationFiringProjectsPreparer.prepareProjects(BuildOperationFiringProjectsPreparer.java:40)
    	at org.gradle.initialization.DefaultGradleLauncher.prepareProjects(DefaultGradleLauncher.java:228)
    	at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:165)
    	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:150)
    	at org.gradle.initialization.DefaultGradleLauncher.executeTasks(DefaultGradleLauncher.java:126)
    	at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72)
    	at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:67)
    	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:178)
    	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67)
    	at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56)
    	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:60)
    	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:63)
    	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
    	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:67)
    	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:54)
    	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:87)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:53)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:29)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:33)
    	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:49)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:32)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:27)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55)
    	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64)
    	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54)
    	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36)
    	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36)
    	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25)
    	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59)
    	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
    	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
    	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
    	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
    	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
    	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
    	at org.gradle.util.Swapper.swap(Swapper.java:38)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
    	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    Caused by: java.lang.NullPointerException
    	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.from(DefaultConfigurableFileCollection.java:190)
    	at org.gradle.api.internal.file.DefaultFileOperations.configurableFiles(DefaultFileOperations.java:126)
    	at org.gradle.groovy.scripts.DefaultScript.files(DefaultScript.java:158)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:484)
    	at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:196)
    	at org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject.tryInvokeMethod(BasicScript.java:130)
    	at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:77)
    	at build_atncjkjkhxk6as55q39rna32o$_run_closure2.doCall(/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle:82)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at org.gradle.util.ClosureBackedAction.execute(ClosureBackedAction.java:71)
    	at org.gradle.util.ConfigureUtil.configureTarget(ConfigureUtil.java:154)
    	at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:105)
    	at org.gradle.api.internal.project.DefaultProject.dependencies(DefaultProject.java:1230)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:484)
    	at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:196)
    	at org.gradle.internal.metaobject.CompositeDynamicObject.tryInvokeMethod(CompositeDynamicObject.java:98)
    	at org.gradle.internal.extensibility.MixInClosurePropertiesAsMethodsDynamicObject.tryInvokeMethod(MixInClosurePropertiesAsMethodsDynamicObject.java:34)
    	at org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject.tryInvokeMethod(BasicScript.java:134)
    	at org.gradle.internal.metaobject.AbstractDynamicObject.invokeMethod(AbstractDynamicObject.java:163)
    	at org.gradle.groovy.scripts.BasicScript.invokeMethod(BasicScript.java:83)
    	at build_atncjkjkhxk6as55q39rna32o.run(/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle:50)
    	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:91)
    	... 140 more
    

    Android Studio also throws a lint error as shown below: ss

    bug 
    opened by karanDhillon 12
  • Handle private @Preview annotations gracefully

    Handle private @Preview annotations gracefully

    When trying to build a project with private Previews, Showkase complains:

    The methods annotated with Preview can't be private as Showkase won't be able to access them otherwise
    

    Does it have to be an error? Not all previews are Showkase-worthy, some of them can be used just in ide. Maybe it could be a warning or there could be a gradle flag for this? Wdyt? This should be relatively easy, I could contribute if you decide that it's a good idea to add it.

    opened by micHar 11
  • showkase browser is not showing component from submodule

    showkase browser is not showing component from submodule

    In multi module project structure, showkase browser is showing components only from the module where I have @ShowkaseRoot it's ignoring all compassable from submodules.

    opened by vivart 11
  • java.lang.NoSuchMethodError: No static method setContent

    java.lang.NoSuchMethodError: No static method setContent

    I'm using 1.0.0-alpha08 version which is the same compose version as the library. Any ideas on how to solve it?

    java.lang.NoSuchMethodError: No static method setContent$default(Landroidx/activity/ComponentActivity;Landroidx/compose/runtime/Recomposer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Landroidx/compose/runtime/Composition; in class Landroidx/compose/ui/platform/WrapperKt; or its super classes (declaration of 'androidx.compose.ui.platform.WrapperKt' appears in /data/app/com.yektasarioglu.testapp.debug-ZwvsZzo9Jrsy4DLjhICVeg==/base.apk)
            at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity.onCreate(ShowkaseBrowserActivity.kt:26)
            at android.app.Activity.performCreate(Activity.java:8085)
            at android.app.Activity.performCreate(Activity.java:8073)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1320)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3870)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4076)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2473)
            at android.os.Handler.dispatchMessage(Handler.java:110)
            at android.os.Looper.loop(Looper.java:219)
            at android.app.ActivityThread.main(ActivityThread.java:8349)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)
    
    version-compatibility 
    opened by yektasarioglu 11
  • Make stacked preview annotations generate showkaseMetadata components for each annotation

    Make stacked preview annotations generate showkaseMetadata components for each annotation

    This is the next step in supporting Multipreview annotations. Referencing #255 as the first step.

    Here I have added functionality to make a ShowkaseMetadata object for each Preview annotation. Eg.

    @Preview(
        name = "small font",
        group = "font scales",
        fontScale = 0.5f
    )
    @Preview(
        name = "large font",
        group = "font scales",
        fontScale = 1.5f
    )
    @Composable
    fun ComposablePreviewFont() {
    
    }
    
    

    Before, this would only make one component in Showkase. Now it should make one for each preview and place them in their respective group. Here they would both be placed in the group font scales

    Furthermore, I would advice to review this by commit as I had to update the tests because of some arrangement due to a check in the processor. I placed the test update in 8371ec2efd81e6317c85029e377dca3ae8cd0aee.

    I'm leaving this as a draft PR because I'm having some issues with getting this to work with KAPT. With KSP, the method roundEnvironment.getElementsAnnotatedWith(PREVIEW_CLASS_NAME) returns all the elements for multi stacked preview. However, with KAPT, it does not seem to work the same way. Is there some other function I should use here? Any suggestions are more than welcome 😄 Added a slack post for anyone interested 😄

    opened by oas004 10
  • No preview found in a minified and shrinked build (Proguard)

    No preview found in a minified and shrinked build (Proguard)

    👋 Thanks guys for the amazing work on Showkase, we have implemented it in our apps: we use it to quickly see if we missed the implementation of our components.

    Problem

    When we launch the showkase activity, it does not find any preview.

    Capture d’écran 2021-08-17 à 17 33 12

    Expected behavior

    When we launch the showkase activity, it finds all composables annotated with @Preview.

    Hypothesis

    It is ok in debug but ko in release. Do you think we should specify something in proguard settings ?

    opened by VincentJouanne 9
  • Let the IDE recognize generated code when using KSP

    Let the IDE recognize generated code when using KSP

    Fix #278, #265, #275 See also: https://kotlinlang.org/docs/ksp-quickstart.html#make-ide-aware-of-generated-code

    Extra note for someone using android variants, you should use androidComponents.onVariants() instead.

    opened by Omico 0
  • Start using the October BOM release of Jetpack Compose

    Start using the October BOM release of Jetpack Compose

    Since they have released a BOM for Jetpack Compose (https://developer.android.com/jetpack/compose/setup#using-the-bom) It would be cool to use it for Showkase as well :)

    opened by oas004 0
  • add support to other build types than only debug and release

    add support to other build types than only debug and release

    Currently only debug and release build types are supported

    imagine having a setup with build types

    
    android {
    
        buildTypes {
    
            alpha {
                initWith(buildTypes.debug)
            }
    
            release {
            }
    
            beta {
                initWith(buildTypes.release)
            }
        }
    
        dependencies {
            alphaImplementation "com.airbnb.android:showkase:1.0.0-beta14"
            kaptAlpha "com.airbnb.android:showkase-processor:1.0.0-beta14"
        }
    }
    
    

    then the dependencies won't be available in alpha builds because the way it is published to maven central

    https://repo1.maven.org/maven2/com/airbnb/android/showkase/1.0.0-beta14/

    compared to e.g. leakcanary

    https://repo1.maven.org/maven2/com/squareup/leakcanary/leakcanary-android/2.9.1/

    the problem lies with the file naming

    where instead of having one artifact, the showkase library has 2: one for debug and one for release, which is not a bad idea if you want automatic switching with just adding implementation instead of having to add debugImplementation and releaseImplementation

    however I'm not aware of a way to have those files added for alpha/beta/release or any other non default build type now


    for this to work we would have to have two different artifacts, e.g. one for

        com.airbnb.android:showkase:1.0.0-beta14 
        com.airbnb.android:showkase-no-op:1.0.0-beta14 // e.g. for release 
    
    opened by kibotu 4
  • ShowkaseModuleCodegen not regenerated nor updated when preview components are removed.

    ShowkaseModuleCodegen not regenerated nor updated when preview components are removed.

    Prerequisite:

    • Add a Foo and a FooPreview composable accordingly, with FooPreview as a preview function for Foo.
    • Build the project

    Scenario:

    • Remove FooPreview function.

    Actual Result:

    • Project will not compile since ShowkaseModuleCodegen is still looking and referencing for FooPreview.

    Expected Result:

    • ShowkaseModuleCodegen should be regenerated or updated since a preview component was removed from the codebase.

    Workaround: Clean / Rebuild the app

    opened by crjacinro 2
  • ShowkaseMetadata class leaking into public API

    ShowkaseMetadata class leaking into public API

    I have a gradle setup where I have several public preview methods (> 20) in a module. This module also has some public API that will be consumed. Since I have renamed all the public preview methods with PreviewXyz, no one is going to be using it because the intended public API starts with EGDSXyz so that should be fine.

    However, there is one issue. For one of those preview methods, a generated class is leaking through. Screen Shot 2022-10-11 at 12 31 34

    Screen Shot 2022-10-11 at 12 40 34 Screen Shot 2022-10-11 at 12 40 44 Screen Shot 2022-10-11 at 12 40 54

    I am able to access ShowkaseMetadata_com_xyz_components_composables_scrim() in the consuming project. I am not sure why this is happening for this preview method only as there are other preview methods that have the exactly same setup and are not leaking through:

    • annotated with a custom warning level annotation
    • annotated with @Preview
    • is a public composable function
    Screen Shot 2022-10-11 at 12 37 37 Screen Shot 2022-10-11 at 12 37 44
    opened by iamutkarshtiwari 0
  • Multi-modules project : how to avoid public visibility for @Preview?

    Multi-modules project : how to avoid public visibility for @Preview?

    The documentation states that

    The root module is the main module of your app that has a dependency on all other modules in the app. This is relevant because we generate the Showkase related classes in the package of the root module and we need to be able to access the UI elements across all the sub modules. This is only possible from the root module as it typically has a dependency on all the sub-modules.

    • Does it mean that we can only have one @ShowkaseRootModule per project?

    • Setting all the composables to render with a public visibility adds unnecessary items to the auto-completion list. Do you have an idea to help mitigate this?

    Many thanks!

    opened by borsini 3
Releases(1.0.0-beta14)
  • 1.0.0-beta14(Aug 22, 2022)

    • Bumped up the version of Compose Compiler to use the 1.2.0 stable release (https://github.com/airbnb/Showkase/pull/249)
    • Fix long provider name codegen making code that does not compile (https://github.com/airbnb/Showkase/pull/241)
    • Fix textoverflow on top appbar for components with long names (#240)
    • Optimize code formatting in ShowkaseTheme.kt (#243)
    • Add close button for search field with animation (#242)

    I am particularly excited about this release because all these improvements were from the community, particularly from Odin, who is the MVP for this release 🥳

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta13(Jun 10, 2022)

    This release contains the following exiting changes 🥳

    • Showkase is based off Compose version 1.1.1 (https://github.com/airbnb/Showkase/pull/229)
    • Showkase's automated screenshot testing infra just got a sample integration with Shot, a popular screenshot testing library. This also means that there's a new artifact available showkase-screenshot-testing-shot. Refer this PR for more details. Extensive documentation around automated screenshot testing with Showkase coming shortly (https://github.com/airbnb/Showkase/pull/232)
    • Fix for app already in inverse mode issue (https://github.com/airbnb/Showkase/pull/223)
    • Fix for function names with illegal characters in codegen (https://github.com/airbnb/Showkase/pull/234)
    • Added more documentation around how Showkase supports the ability to document component styles and enables linking all components styles in a single view in the browser (https://github.com/airbnb/Showkase/pull/227)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta12(Feb 10, 2022)

    • Fix unable to find showkase root issue (https://github.com/airbnb/Showkase/commit/725882854130fcc67a5f935d9adba8c585f6c149)
    • Fix super small previews issue(https://github.com/airbnb/Showkase/commit/e1ae330d4b94232f03fe8211780624648fa83e22)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta11(Feb 4, 2022)

    Fixed bug that was introduced in beta09 release that stopped showing sub module components (https://github.com/airbnb/Showkase/issues/213)

    Also contains other fixes and contributions

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta10(Jan 11, 2022)

  • 1.0.0-beta09(Jan 3, 2022)

    This release adds KSP support to Showkase. Thanks to @elihart for his fantastic contributions 👏

    Based on some early analysis, we think that this will speed up the build times for Showkase by 70%!!! (https://github.com/airbnb/Showkase/pull/194)

    In order to use Showkase with ksp, you will need to make the following changes to your Showkase setup in the build.gradle file

    +   plugins {
    +       "com.google.devtools.ksp" version "$KSP_VERSION"
    +   }
    
    
    -    kapt "com.airbnb.android:showkase-processor:1.0.0-beta09"
    +    ksp "com.airbnb.android:showkase-processor:1.0.0-beta09"
    

    In addition, you need to pass the following flag to the gradle command that you use to build/run - -PuseKsp=true. Here's an example of what this would look like

    ./gradlew sample:clean sample:build -i -PuseKsp=true
    
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta08(Jan 3, 2022)

    This release includes a simple change to add additional information for screenshot testing

    • Pass style information down for implementing screenshot test (#187)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta07(Oct 27, 2021)

    • Added the ability to specify if a Composable is a style of another Composable function. Created two new properties in the @ShowkaseComposable annotation - styleName & defaultStyle. Here's how you'd use it.
    @ShowkaseComposable(group = "Chips", name = "BasicChip", defaultStyle = true)
    @Composable
    fun ChipPreview() {
        Chip(isError = false)
    }
    
    @ShowkaseComposable(group = "Chips", name = "BasicChip", styleName = "Error")
    @Composable
    fun ChipErrorPreview() {
        Chip(isError = true)
    }
    
    • Improvements to screenshot testing code generation. This feature is still work in progress so I've intentionally not added documentation for this just yet but it should be coming soon!
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta06(Oct 14, 2021)

  • 1.0.0-beta05(Oct 5, 2021)

  • 1.0.0-beta04(Sep 9, 2021)

    • Fixed bug where using a custom theme on the activity caused a crash with MDC adapter (#168)
    • Built against Compose 1.0.1 and Kotlin 1.5.21 (#166)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta03(Aug 6, 2021)

    • Make Showkase compatible with not including kapt on compile classpath (#162)
    • Added support for components with vertical scroll (#163)
    • Added functionality to allow skipping Previews (#164)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta02(Aug 4, 2021)

    • Adds support for the first stable Compose release - 1.0.0 🥳
    • Get rid of some unnecessary dependencies
    • Bug fix for Showkase crashing for some usecases where kapt is not included on the compile classpath (https://github.com/airbnb/Showkase/pull/160)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-beta01(Jul 16, 2021)

  • 1.0.0-alpha12(Jun 27, 2021)

  • 1.0.0-alpha11(Jun 6, 2021)

  • 1.0.0-alpha10(Apr 27, 2021)

  • 1.0.0-alpha09(Mar 26, 2021)

    • Based of Compose 1.0.0-beta03
    • Changes the method name to get the ShowkaseBrowser intent from createShowkaseBrowserIntent to getBrowserIntent. It's also not available as a top level function but you need to access it using Showkase.getBrowserIntent. More info here.
    • Addition of a new API that allows you to access all the UI elements that are configured with Showkase. You can access it using Showkase.getMetadata(). Use cases of this might be to create your custom browser, take screenshots of your UI elements(coming soon), etc.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-alpha08(Mar 26, 2021)

  • 1.0.0-alpha07(Mar 7, 2021)

  • 1.0.0-alpha06(Feb 4, 2021)

  • 1.0.0-alpha05(Jan 4, 2021)

  • 1.0.0-alpha04(Dec 21, 2020)

Notes is a simple and private notes app. Organize your thoughts, discoveries, and ideas and simplify planning important moments in your life with your digital notepad.

Notes Example Download Download the latest version of the Android app from this link. Building Using Android Studio Clone the repo, open it in Android

Dmitry Savin 1 Jan 3, 2022
Explode compose elements on click! Just add explodeOnClick() modifier!

compose-explode Explode compose elements on click! Just add explodeOnClick() modifier! Inspired from ExplosionField Getting started Go to library/expl

Nikhil Chaudhari 32 Oct 9, 2022
🎨 Jetpack Compose canvas library that helps you draw paths, images on canvas with color pickers and palettes

?? Jetpack Compose canvas library that helps you draw paths and images on canvas with color pickers and palettes. Sketchbook also provides useful components and functions that can easily interact with canvas.

Stream 339 Nov 24, 2022
FullMangement - an application that helps you manage your tasks effectively. built with the latest tachs like Compose UI, Jetpack libraries, and MVVM design pattern.

Full Management is an application that helps you manage your tasks effectively. built with the latest tachs like Compose UI, Jetpack libraries and MVVM design pattern.

Amr algnyat 4 Nov 1, 2022
A collection of animations, compositions, UIs using Jetpack Compose. You can say Jetpack Compose cookbook or play-ground if you want!

Why Not Compose! A collection of animations, compositions, UIs using Jetpack Compose. You can say Jetpack Compose cookbook or play-ground if you want!

Md. Mahmudul Hasan Shohag 177 Nov 14, 2022
An Android / Wear OS app built with Jetpack Compose to search for definitions of English words

EnglishDictionary A simple Android / Wear OS app built with Jetpack Compose to s

daniil 4 Sep 21, 2022
Carol 12 Sep 25, 2022
Butterfly - Butterfly helps to build adaptive and responsive UIs for Android with Jetpack WindowManager

?? Butterfly helps to build adaptive and responsive UIs for Android with Jetpack

Stream 197 Oct 2, 2022
Jetpack Compose based project, used to stress-testing compose features / integrations and explore non-trivial functionality

Project containing Jetpack Compose samples For pagination & network images it uses CATAAS. Known issues Navigation-Compose Issue with fast tapping on

Denis Rudenko 58 Nov 13, 2022
A simple authentication application using Jetpack compose to illustrate signin and sign up using Mvvm, Kotlin and jetpack compose

Authentication A simple authentication application using Jetpack compose to illustrate signin and sign up using Mvvm, Kotlin and jetpack compose Scree

Felix Kariuki 5 Oct 11, 2022
Compose-navigation - Set of utils to help with integrating Jetpack Compose and Jetpack's Navigation

Jetpack Compose Navigation Set of utils to help with integrating Jetpack Compose

Adam Kobus 5 Apr 5, 2022
Learn Jetpack Compose for Android by Examples. Learn how to use Jetpack Compose for Android App Development. Android’s modern toolkit for building native UI.

Learn Jetpack Compose for Android by Examples. Learn how to use Jetpack Compose for Android App Development. Android’s modern toolkit for building native UI.

MindOrks 378 Nov 21, 2022
Movies App represent a list of movies, list of categories, search about movie and Save movie in Room Database

What is this project? Movies App represent a list of movies, list of categories, search about movie and Save movie in Room Database Main Features Kotl

Ahmed Omara 23 Nov 29, 2022
🚀🏞💪 Collection of Images, Modifiers, utility functions for Jetpack Compose to expand and enrich displaying, manipulating, scaling, resizing, zooming, and getting cropped ImageBitmap based on selection area

Collection of Images, Modifiers, utility functions for Jetpack Compose to expand and enrich displaying, manipulating, scaling, resizing, zooming, and getting cropped ImageBitmap based on selection area, before/after image to with handle to show partial of both images and more is cooking up

Smart Tool Factory 199 Nov 8, 2022
⌨️ A tool that gives you a massive head start when building Compose Desktop apps. It saves you from time-consuming setup and configuration

?? create-compose-app A tool that gives you a massive head start when building Compose based apps. It saves you from time-consuming setup and configur

theapache64 464 Nov 29, 2022
⌨️ A tool that gives you a massive head start when building Compose Desktop apps. It saves you from time-consuming setup and configuration

?? create-compose-app A tool that gives you a massive head start when building Compose based apps. It saves you from time-consuming setup and configur

theapache64 463 Nov 16, 2022
A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

Roman Levinzon 59 Oct 19, 2022
A library that you can use in 4 different types toast written with Jetpack Compose

Composable Sweet Toast A library that you can use in 4 different types(Success, Error, Warning, Info) written with Jetpack Compose. You can use this t

Talha Fakıoğlu 65 Oct 22, 2022
A library that you can use in 4 different types(Success, Error, Warning, Info) written with Jetpack Compose.

Composable Sweet Toast A library that you can use in 4 different types(Success, Error, Warning, Info) written with Jetpack Compose. You can use this t

Talha Fakıoğlu 65 Oct 22, 2022