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

Adding support for Compose assets and reference assets #537

Open
wants to merge 15 commits 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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ _backup
.eslintcache
.next
android-tools
*.iml


# Xcode
Expand Down
31 changes: 22 additions & 9 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ rules_kotlin_extensions = use_extension(
"@rules_kotlin//src/main/starlark/core/repositories:bzlmod_setup.bzl",
"rules_kotlin_extensions",
)

# sha256 values can be found here - https://github.com/JetBrains/kotlin/releases/tag/v1.7.20
rules_kotlin_extensions.kotlinc_version(
sha256 = "9db4b467743c1aea8a21c08e1c286bc2aeb93f14c7ba2037dbd8f48adc357d83",
version = "1.7.22",
sha256 = "5e3c8d0f965410ff12e90d6f8dc5df2fc09fd595a684d514616851ce7e94ae7d",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason in particular we have to downgrade rules versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was mainly due to point 5 in the PR description. This was the highest version I could stay at with the least amount of changes. I will give another combination of dependencies a shot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to upgrade to a few different versions till v1.8.20 but all such changes open a can of worms that require several other dependencies to be updated

version = "1.7.20",
)

use_repo(
rules_kotlin_extensions,
"com_github_jetbrains_kotlin",
Expand Down Expand Up @@ -144,15 +147,25 @@ maven.install(
"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0",

# Android
"androidx.databinding:viewbinding:7.2.2",
"androidx.databinding:viewbinding:8.0.0",
"androidx.annotation:annotation:1.1.0",

# Compose
"androidx.compose.ui:ui:1.2.0",
"androidx.compose.ui:ui-tooling:1.2.0",
"androidx.compose.ui:ui-test-junit4:1.2.0",
"androidx.compose.runtime:runtime:1.2.0",
"androidx.compose.runtime:runtime:1.2.0",
"androidx.compose.compiler:compiler:1.3.2",
"androidx.compose.foundation:foundation:1.2.0",
"androidx.compose.material:material:1.2.0",

# Android Demo
"androidx.navigation:navigation-runtime:2.3.3",
"androidx.navigation:navigation-ui-ktx:2.3.3",
"androidx.navigation:navigation-fragment-ktx:2.3.3",
"androidx.navigation:navigation-runtime:2.3.1",
"androidx.navigation:navigation-ui-ktx:2.3.1",
"androidx.navigation:navigation-fragment-ktx:2.3.1",
"com.afollestad.material-dialogs:core:3.3.0",
"com.google.android.material:material:1.4.0",
"com.google.android.material:material:1.6.1",

# Android Demo Testing
"androidx.test.espresso:espresso-intents:3.3.0",
Expand All @@ -172,8 +185,8 @@ maven.install(
"androidx.constraintlayout:constraintlayout:2.1.4",

# AndroidX Resolutions
"androidx.activity:activity-ktx:1.2.3",
"androidx.fragment:fragment-ktx:1.3.4",
"androidx.activity:activity-ktx:1.3.0",
"androidx.fragment:fragment-ktx:1.4.1",

# Graal
"org.graalvm.js:js:21.2.0",
Expand Down
3 changes: 1 addition & 2 deletions android/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ It's been tested on Andriod Studio Chipmunk(2021.2.1) and Android Studio Giraffe
Assuming you have read the [requirements on the root contributing guide](https://github.com/player-ui/player/blob/main/CONTRIBUTING.md).

1. Once you have Android Studio installed, you will need to go to tools->SDK Manager->SDK Platforms.
1. Make sure you have **only** the following SDK installed: Android API 32.
*If you are using Android Giraffe, you may need to click on show package details and it will be under Android12L. (Android SDK Platrform 32)*
1. Make sure you have **only** the following SDK installed: Android API 33.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is needed but I couldnt get it to build with API 32

2. The next step will be to make sure you have the right (and _only_ the right) SDK Build tools and NDK. Click on the SDK Tools tab and make sure you have only the following clicked:
1. 30.0.3
2. 21.4.7075529
Expand Down
1 change: 1 addition & 0 deletions android/demo/deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ maven_main = [
maven_test = [
"@maven//:androidx_test_espresso_espresso_intents",
"@maven//:androidx_test_ext_junit_ktx",
"@maven//:androidx_compose_ui_ui_test_junit4",
]

main_deps = maven_main + [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.intuit.playerui.android.reference.demo.test.assets.badge

import androidx.compose.ui.test.onNodeWithText
import com.intuit.playerui.android.reference.demo.test.base.ComposeUITest
import com.intuit.playerui.android.reference.demo.test.base.shouldBePlayerState
import com.intuit.playerui.core.player.state.InProgressState
import org.junit.Test

class BadgeUITest : ComposeUITest("badge") {

@Test
fun basic() {
launchMock("badge-all")

androidComposeRule.onNodeWithText("INFO")
.assertExists()
androidComposeRule.onNodeWithText("ERROR")
.assertExists()
currentState.shouldBePlayerState<InProgressState>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.intuit.playerui.android.reference.demo.test.base.AssetUITest
import com.intuit.playerui.android.reference.demo.test.base.ComposeUITest
import com.intuit.playerui.android.reference.demo.test.base.shouldBePlayerState
import com.intuit.playerui.android.reference.demo.test.base.waitForViewInRoot
import com.intuit.playerui.core.player.state.InProgressState
import org.junit.Test

class CollectionUITest : AssetUITest("collection") {
class CollectionUITest : ComposeUITest("collection") {

@Test
fun basic() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ abstract class AssetUITest(val group: String? = null) {
@get:Rule
val rule = activityScenarioRule<MainActivity>()

open fun getActivityRule() = rule

protected lateinit var viewModel: MainViewModel

protected lateinit var playerViewModel: DemoPlayerViewModel
Expand All @@ -33,7 +35,7 @@ abstract class AssetUITest(val group: String? = null) {
@Before
fun before() {
Intents.init()
rule.scenario.onActivity {
getActivityRule().scenario.onActivity {
viewModel = it.viewModel
}
}
Expand All @@ -57,7 +59,7 @@ abstract class AssetUITest(val group: String? = null) {
fun launchMock(mock: Mock<*>) {
viewModel.launch(mock)

rule.scenario.onActivity {
getActivityRule().scenario.onActivity {
playerViewModel = it.currentPlayer?.playerViewModel as? DemoPlayerViewModel
?: throw IllegalStateException("player not found")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.intuit.playerui.android.reference.demo.test.base

import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.intuit.playerui.android.reference.demo.ui.main.MainActivity
import org.junit.Rule

abstract class ComposeUITest(group: String? = null) : AssetUITest(group) {
@get:Rule
val androidComposeRule = createAndroidComposeRule<MainActivity>()

override fun getActivityRule(): ActivityScenarioRule<MainActivity> {
return androidComposeRule.activityRule
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.intuit.playerui.android.reference.demo.test.base.AssetUITest
import com.intuit.playerui.android.reference.demo.test.base.ComposeUITest
import com.intuit.playerui.android.reference.demo.test.base.waitForViewInRoot
import org.junit.Test

class PlayerFragmentScrollingTest : AssetUITest("misc") {
class PlayerFragmentScrollingTest : ComposeUITest("misc") {

@Test
fun shouldScrollToTopOnTransition() {
launchMock("long-multi-view")
waitForViewInRoot(withText("It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way—in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.\n\nThere were a king with a large jaw and a queen with a plain face, on the throne of England; there were a king with a large jaw and a queen with a fair face, on the throne of France. In both countries it was clearer than crystal to the lords of the State preserves of loaves and fishes, that things in general were settled for ever.\n\nIt was the year of Our Lord one thousand seven hundred and seventy-five. Spiritual revelations were conceded to England at that favoured period, as at this. Mrs. Southcott had recently attained her five-and-twentieth blessed birthday, of whom a prophetic private in the Life Guards had heralded the sublime appearance by announcing that arrangements were made for the swallowing up of London and Westminster. Even the Cock-lane ghost had been laid only a round dozen of years, after rapping out its messages, as the spirits of this very year last past (supernaturally deficient in originality) rapped out theirs. Mere messages in the earthly order of events had lately come to the English Crown and People, from a congress of British subjects in America: which, strange to relate, have proved more important to the human race than any communications yet received through any of the chickens of the Cock-lane brood.\n\nFrance, less favoured on the whole as to matters spiritual than her sister of the shield and trident, rolled with exceeding smoothness down hill, making paper money and spending it. Under the guidance of her Christian pastors, she entertained herself, besides, with such humane achievements as sentencing a youth to have his hands cut off, his tongue torn out with pincers, and his body burned alive, because he had not kneeled down in the rain to do honour to a dirty procession of monks which passed within his view, at a distance of some fifty or sixty yards. It is likely enough that, rooted in the woods of France and Norway, there were growing trees, when that sufferer was put to death, already marked by the Woodman, Fate, to come down and be sawn into boards, to make a certain movable framework with a sack and a knife in it, terrible in history. It is likely enough that in the rough outhouses of some tillers of the heavy lands adjacent to Paris, there were sheltered from the weather that very day, rude carts, bespattered with rustic mire, snuffed about by pigs, and roosted in by poultry, which the Farmer, Death, had already set apart to be his tumbrils of the Revolution. But that Woodman and that Farmer, though they work unceasingly, work silently, and no one heard them as they went about with muffled tread: the rather, forasmuch as to entertain any suspicion that they were awake, was to be atheistical and traitorous.\n\nIn England, there was scarcely an amount of order and protection to justify much national boasting. Daring burglaries by armed men, and highway robberies, took place in the capital itself every night; families were publicly cautioned not to go out of town without removing their furniture to upholsterers’ warehouses for security; the highwayman in the dark was a City tradesman in the light, and, being recognised and challenged by his fellow-tradesman whom he stopped in his character of “the Captain,” gallantly shot him through the head and rode away; the mail was waylaid by seven robbers, and the guard shot three dead, and then got shot dead himself by the other four, “in consequence of the failure of his ammunition:” after which the mail was robbed in peace; that magnificent potentate, the Lord Mayor of London, was made to stand and deliver on Turnham Green, by one highwayman, who despoiled the illustrious creature in sight of all his retinue; prisoners in London gaols fought battles with their turnkeys, and the majesty of the law fired blunderbusses in among them, loaded with rounds of shot and ball; thieves snipped off diamond crosses from the necks of noble lords at Court drawing-rooms; musketeers went into St. Giles’s, to search for contraband goods, and the mob fired on the musketeers, and the musketeers fired on the mob, and nobody thought any of these occurrences much out of the common way. In the midst of them, the hangman, ever busy and ever worse than useless, was in constant requisition; now, stringing up long rows of miscellaneous criminals; now, hanging a housebreaker on Saturday who had been taken on Tuesday; now, burning people in the hand at Newgate by the dozen, and now burning pamphlets at the door of Westminster Hall; to-day, taking the life of an atrocious murderer, and to-morrow of a wretched pilferer who had robbed a farmer’s boy of sixpence.\n\nAll these things, and a thousand like them, came to pass in and close upon the dear old year one thousand seven hundred and seventy-five. Environed by them, while the Woodman and the Farmer worked unheeded, those two of the large jaws, and those other two of the plain and the fair faces, trod with stir enough, and carried their divine rights with a high hand. Thus did the year one thousand seven hundred and seventy-five conduct their Greatnesses, and myriads of small creatures—the creatures of this chronicle among the rest—along the roads that lay before them."))
.check(matches(isDisplayed()))
onView(withText("Go to view 2")).perform(scrollTo(), click())
onView(withText("Go to view 2")).check(matches(isDisplayed())).perform(scrollTo(), click())
waitForViewInRoot(withText("Can you see me?"))
.check(matches(isDisplayed()))
}
Expand Down
6 changes: 6 additions & 0 deletions android/demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
xmlns:tools="http://schemas.android.com/tools"
package="com.intuit.playerui.android.reference.demo">

<uses-sdk
Expand Down Expand Up @@ -40,5 +41,10 @@
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<!-- Resolve a conflict issue while building with Bazel. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
</manifest>
29 changes: 29 additions & 0 deletions android/demo/src/main/assets/mocks/badge/badge-all.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"id": "collection-badge",
"type": "collection",
"label": {
"asset": {
"id": "collection-label",
"type": "text",
"value": "Badge variants"
}
},
"values": [
{
"asset": {
"id": "item-1",
"type": "badge",
"label": "INFO",
"status": "info"
}
},
{
"asset": {
"id": "item-2",
"type": "badge",
"label": "ERROR",
"status": "error"
}
}
]
}
10 changes: 10 additions & 0 deletions android/player/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ load("@rules_kotlin//kotlin:android.bzl", "kt_android_library")
load("@rules_kotlin//kotlin:core.bzl", "kt_kotlinc_options")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
load("@rules_player//kotlin:defs.bzl", "kt_jvm_junit5_test", "lint")
load("@rules_kotlin//kotlin:core.bzl", "kt_compiler_plugin")
load("//jvm:defs.bzl", "distribution")
load(":deps.bzl", "main_deps", "main_exports", "main_resources", "test_deps")

Expand Down Expand Up @@ -39,9 +40,18 @@ kt_android_library(
manifest = ":src/main/AndroidManifest.xml",
resource_files = glob(["src/main/res/**"]),
resources = main_resources,
plugins = [":jetpack_compose_compiler_plugin"],
deps = main_deps,
)

kt_compiler_plugin(
name = "jetpack_compose_compiler_plugin",
id = "androidx.compose.compiler",
target_embedded_compiler = True,
visibility = ["//visibility:public"],
deps = ["@maven//:androidx_compose_compiler_compiler"],
)

android_library(
name = "player",
custom_package = "com.intuit.playerui.android",
Expand Down
9 changes: 9 additions & 0 deletions android/player/deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ main_deps = main_exports + [
"//plugins/beacon/jvm:beacon",
"//plugins/pubsub/jvm:pubsub",
"//plugins/coroutines/jvm:coroutines",

# Compose deps
"@maven//:androidx_activity_activity_compose",
"@maven//:androidx_appcompat_appcompat",
"@maven//:androidx_compose_foundation_foundation",
"@maven//:androidx_compose_foundation_foundation_layout",
"@maven//:androidx_compose_runtime_runtime",
"@maven//:androidx_compose_ui_ui",
"@maven//:androidx_compose_ui_ui_tooling",
]

main_resources = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.intuit.playerui.android

import android.content.Context
import android.view.View
import androidx.compose.runtime.ProvidedValue
import com.intuit.hooks.BailResult
import com.intuit.hooks.HookContext
import com.intuit.hooks.SyncBailHook
Expand Down Expand Up @@ -122,8 +123,13 @@ public class AndroidPlayer private constructor(
)
}

public class CompositionLocalProvidedValuesHook : SyncHook<(HookContext, (List<ProvidedValue<*>>) -> Unit) -> Unit>() {
public fun call(hookContext: HookContext, updateProvidedValues: (List<ProvidedValue<*>>) -> Unit) = super.call { f, context -> f(context, updateProvidedValues) }
}

public val context: ContextHook = ContextHook()
public val update: UpdateHook = UpdateHook()
public val compositionLocalProvidedValues: CompositionLocalProvidedValuesHook = CompositionLocalProvidedValuesHook()
internal val recycle: RecycleHook = RecycleHook()
internal val release: ReleaseHook = ReleaseHook()
}
Expand Down Expand Up @@ -206,6 +212,9 @@ public class AndroidPlayer private constructor(
context.overlayStyles(styles)
}

/** List of provided values to pass into the CompositionLocalProvider that wraps all compose assets */
internal var providedValues: MutableList<ProvidedValue<*>> = mutableListOf()

/**
* Cache [AssetContext]-[View] pairs against the [AssetContext.id]. The [AssetContext] is
* cached to enable checking if the [View] has been hydrated with the latest [Asset].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public fun AssetContext.withContext(context: Context): AssetContext = copy(conte
public fun AssetContext.withStyles(@StyleRes vararg styles: Style?): AssetContext = withStyles(styles.filterNotNull())

/** Create a new, styled [AssetContext] */
public fun AssetContext.withStyles(@StyleRes styles: Styles): AssetContext = if (styles.isEmpty()) {
public fun AssetContext.withStyles(@StyleRes styles: Styles?): AssetContext = if (styles.isNullOrEmpty()) {
this
} else {
copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper {
.render()

/** Render a [View] with specific [styles] */
public fun RenderableAsset.render(@StyleRes styles: Styles): View = assetContext
public fun RenderableAsset.render(@StyleRes styles: Styles?): View = assetContext
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated these to avoid have to call multiple render implementations here

Any concerns?

.withContext([email protected]())
.withStyles(styles)
.build()
Expand All @@ -208,7 +208,7 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper {
.render()

/** Render a [View] with specific [styles] */
public fun RenderableAsset.render(@StyleRes styles: Styles, tag: String): View = assetContext
public fun RenderableAsset.render(@StyleRes styles: Styles?, tag: String): View = assetContext
.withContext([email protected]())
.withTag(tag)
.withStyles(styles)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.intuit.playerui.android.compose

import androidx.compose.ui.text.TextStyle
import com.intuit.playerui.android.extensions.Style

interface AssetStyle {
val textStyle: TextStyle?
val xmlStyles: List<Style>?
}
Loading