diff --git a/app/build.gradle b/app/build.gradle index 8ceaaf9a..548580a0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,8 +99,8 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.ui:ui-util:$compose_version" - debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" - debugImplementation "androidx.customview:customview:1.2.0-alpha01" + implementation "androidx.compose.ui:ui-tooling:$compose_version" + debugImplementation "androidx.customview:customview:1.2.0-alpha02" debugImplementation "androidx.customview:customview-poolingcontainer:1.0.0" // Compose - Additions diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt index 937d5ff7..21b010f4 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt @@ -1,11 +1,15 @@ package bruhcollective.itaysonlab.jetispot.ui.dac +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import bruhcollective.itaysonlab.jetispot.proto.ErrorComponent import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.* import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.* import com.google.protobuf.Message @@ -37,11 +41,25 @@ fun DacRender ( is SectionHeaderComponent -> SectionHeaderComponentBinder(item.title) is SectionComponent -> SectionComponentBinder(item) is RecentlyPlayedSectionComponent -> RecentlyPlayedSectionComponentBinder() + // is SnappyGridSectionComponent -> SnappyGridSectionComponentBinder(item) // Other + + is ErrorComponent -> { + Column { + Text(if (item.type == ErrorComponent.ErrorType.UNSUPPORTED) { + "DAC unsupported component" + } else { + "DAC rendering error" + }, Modifier.padding(horizontal = 16.dp)) + Text(item.message ?: "", color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), modifier = Modifier + .padding(top = 4.dp) + .padding(horizontal = 16.dp)) + } + } + else -> { - Text("DAC proto-known, but UI-unknown component: ${item::class.java.simpleName}\n\n${item}") - Spacer(modifier = Modifier.height(8.dp)) + Text("DAC proto-known, but UI-unknown component: ${item::class.java.simpleName}\n\n${item}", modifier = Modifier.padding(16.dp)) } } } \ No newline at end of file diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponentBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponentBinder.kt index 23738a66..fdf79ad2 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponentBinder.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponentBinder.kt @@ -1,5 +1,6 @@ package bruhcollective.itaysonlab.jetispot.ui.dac.components_home +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.History @@ -9,6 +10,7 @@ import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp import bruhcollective.itaysonlab.jetispot.ui.ext.dynamicUnpack import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController import com.spotify.home.dac.component.v1.proto.ToolbarComponent @@ -21,7 +23,7 @@ import com.spotify.home.dac.component.v1.proto.ToolbarItemSettingsComponent fun ToolbarComponentBinder( item: ToolbarComponent ) { - SmallTopAppBar(title = { + TopAppBar(title = { Text(item.dayPartMessage) }, actions = { item.itemsList.forEach { @@ -31,7 +33,7 @@ fun ToolbarComponentBinder( is ToolbarItemSettingsComponent -> ToolbarItem(Icons.Rounded.Settings, protoItem.navigateUri, protoItem.title) } } - }, modifier = Modifier.statusBarsPadding()) + }, modifier = Modifier.statusBarsPadding(), windowInsets = WindowInsets(top = 0.dp)) } @Composable diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/ext/ProtoExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/ext/ProtoExt.kt index 3a054269..3b742924 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/ext/ProtoExt.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/ext/ProtoExt.kt @@ -3,6 +3,7 @@ package bruhcollective.itaysonlab.jetispot.ui.ext import com.google.protobuf.Any import com.google.protobuf.Message -fun Any.dynamicUnpack() = unpack(Class.forName(typeUrl.split("/")[1].let { +@Suppress("UNCHECKED_CAST") +fun Any.dynamicUnpack(): Message = unpack(Class.forName(typeUrl.split("/")[1].let { if (!it.startsWith("com.spotify")) "com.spotify.${it}" else it }) as Class) \ No newline at end of file diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt index 9a5be31b..2dcc1545 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt @@ -61,7 +61,7 @@ fun BaseConfigScreen( } } }, scrollBehavior = scrollBehavior) - }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> LazyColumn( Modifier .fillMaxHeight() diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt index 961e062d..947a8c32 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt @@ -70,7 +70,7 @@ fun StorageScreen( Icon(Icons.Rounded.ArrowBack, null) } }, scrollBehavior = scrollBehavior) - }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> LazyColumn( modifier = Modifier .fillMaxHeight() diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt index f7cfa64a..f526f601 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt @@ -1,5 +1,6 @@ package bruhcollective.itaysonlab.jetispot.ui.screens.dac +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -8,14 +9,12 @@ import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.ViewModel -import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi +import bruhcollective.itaysonlab.jetispot.proto.ErrorComponent import bruhcollective.itaysonlab.jetispot.ui.dac.DacRender import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.FilterComponentBinder import bruhcollective.itaysonlab.jetispot.ui.ext.dynamicUnpack @@ -28,13 +27,16 @@ import com.spotify.dac.api.components.VerticalListComponent import com.spotify.dac.api.v1.proto.DacResponse import com.spotify.home.dac.component.experimental.v1.proto.FilterComponent import com.spotify.home.dac.component.v1.proto.HomePageComponent +import com.spotify.home.dac.component.v1.proto.ToolbarComponent import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject // generally just a HubScreen with simplifed code and DAC arch usage // DAC is something like another ServerSideUI from Spotify -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun DacRendererScreen( title: String, @@ -55,10 +57,9 @@ fun DacRendererScreen( is DacViewModel.State.Loaded -> { Scaffold(topBar = { if (fullscreen) { - TopAppBar(title = {}, colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = Color.Transparent, - scrolledContainerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f) - ), scrollBehavior = scrollBehavior) + (viewModel.state as? DacViewModel.State.Loaded)?.sticky?.let { msg -> + DacRender(msg) + } } else { LargeTopAppBar(title = { Text(title) @@ -68,56 +69,26 @@ fun DacRendererScreen( } }, scrollBehavior = scrollBehavior) } - }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + }, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> LazyColumn( modifier = Modifier .fillMaxHeight() - .let { if (!fullscreen) it.padding(padding) else it } + .padding(padding) ) { - (viewModel.state as? DacViewModel.State.Loaded)?.data?.apply { - val cmBind: (List) -> Unit = { componentsList -> - items(componentsList) { item -> - var exception: Exception? = null - var unpackedItem: Message? = null - - try { - unpackedItem = item.dynamicUnpack() - } catch (e: Exception) { - exception = e - } - - if (unpackedItem == null && exception != null) { - when (exception) { - is ClassNotFoundException -> { - Text("DAC unsupported component", Modifier.padding(horizontal = 16.dp)) - Text(exception.message ?: "", color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), modifier = Modifier.padding(top = 4.dp).padding(horizontal = 16.dp)) - } - else -> { - Text("DAC rendering error: ${exception.message}\n\n${exception.stackTraceToString()}") - } - } - - Spacer(modifier = Modifier.height(8.dp)) - } else if (unpackedItem != null) { - if (unpackedItem is FilterComponent) { - FilterComponentBinder(unpackedItem, viewModel.facet) { nf -> - scope.launch { - viewModel.facet = nf - viewModel.reload(loader) - } - } - } else { - DacRender(unpackedItem) + (viewModel.state as? DacViewModel.State.Loaded)?.apply { + items(data) { item -> + if (item is FilterComponent) { + FilterComponentBinder(item, viewModel.facet) { nf -> + scope.launch { + viewModel.facet = nf + viewModel.reload(loader) } } + } else { + DacRender(item) } } - when (this) { - is VerticalListComponent -> cmBind(this.componentsList) - is HomePageComponent -> cmBind(this.componentsList) - } - item { Spacer(modifier = Modifier.height(8.dp)) } @@ -138,8 +109,7 @@ fun DacRendererScreen( @HiltViewModel class DacViewModel @Inject constructor( - private val spInternalApi: SpInternalApi, - private val spPlayerServiceManager: SpPlayerServiceManager + private val spInternalApi: SpInternalApi ) : ViewModel() { var facet = "default" @@ -148,22 +118,44 @@ class DacViewModel @Inject constructor( suspend fun load(loader: suspend SpInternalApi.(String) -> DacResponse) { _state.value = try { - val unpackedRaw = spInternalApi.loader(facet) - val unpackedMessage = unpackedRaw.component.dynamicUnpack() - State.Loaded(unpackedMessage) + val (sticky, list) = withContext(Dispatchers.Default) { + val messages = parseMessages(when (val protoList = spInternalApi.loader(facet).component.dynamicUnpack()) { + is VerticalListComponent -> protoList.componentsList + is HomePageComponent -> protoList.componentsList + else -> error("Invalid root for DAC renderer! Found: ${protoList.javaClass.simpleName}") + }) + + if (messages.first() is ToolbarComponent) { + messages.first() to messages.drop(1) + } else { + null to messages + } + } + + State.Loaded(sticky, list) } catch (e: Exception) { e.printStackTrace() State.Error(e) } } + private fun parseMessages(list: List): List = list.map { item -> + try { + item.dynamicUnpack() + } catch (e: ClassNotFoundException) { + ErrorComponent.newBuilder().setType(ErrorComponent.ErrorType.UNSUPPORTED).setMessage(e.message).build() + } catch (e: java.lang.Exception) { + ErrorComponent.newBuilder().setType(ErrorComponent.ErrorType.GENERIC_EXCEPTION).setMessage(e.message + "\n\n" + e.stackTraceToString()).build() + } + } + suspend fun reload(loader: suspend SpInternalApi.(String) -> DacResponse) { _state.value = State.Loading load(loader) } sealed class State { - class Loaded(val data: Message) : State() + class Loaded(val sticky: Message?, val data: List) : State() class Error(val error: Exception) : State() object Loading : State() } diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseRadioScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseRadioScreen.kt index f1995d81..ce82e3bf 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseRadioScreen.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseRadioScreen.kt @@ -1,6 +1,7 @@ package bruhcollective.itaysonlab.jetispot.ui.screens.hub import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -10,6 +11,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import bruhcollective.itaysonlab.jetispot.ui.ext.rememberEUCScrollBehavior import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController @@ -28,7 +30,7 @@ fun BrowseRadioScreen() { Icon(Icons.Rounded.ArrowBack, null) } }, colors = TopAppBarDefaults.largeTopAppBarColors(), scrollBehavior = scrollBehavior) - }, modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + }, modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> Box(Modifier.padding(padding)) { HubScreen( needContentPadding = false, diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseScreen.kt index 3d1f1c9c..226fadd0 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseScreen.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/BrowseScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import bruhcollective.itaysonlab.jetispot.ui.ext.rememberEUCScrollBehavior import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController @@ -29,7 +30,7 @@ fun BrowseScreen( Icon(Icons.Rounded.ArrowBack, null) } }, colors = TopAppBarDefaults.largeTopAppBarColors(), scrollBehavior = scrollBehavior) - }, modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + }, modifier = Modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> Box(Modifier.padding(padding)) { HubScreen( needContentPadding = false, diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt index 6e43e371..9d42224b 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt +++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt @@ -1,10 +1,7 @@ package bruhcollective.itaysonlab.jetispot.ui.screens.hub import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons @@ -54,7 +51,7 @@ fun HubScaffold( } }, colors = TopAppBarDefaults.largeTopAppBarColors(), scrollBehavior = scrollBehavior) } else { - SmallTopAppBar(title = { + TopAppBar(title = { Text(appBarTitle, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.alpha(scrollBehavior.state.overlappedFraction)) }, navigationIcon = { IconButton(onClick = { navController.popBackStack() }) { @@ -67,7 +64,7 @@ fun HubScaffold( } }, modifier = Modifier .fillMaxSize() - .nestedScroll(scrollBehavior.nestedScrollConnection)) { padding -> + .nestedScroll(scrollBehavior.nestedScrollConnection), contentWindowInsets = WindowInsets(top = 0.dp)) { padding -> CompositionLocalProvider(LocalHubScreenDelegate provides viewModel) { LazyColumn( modifier = Modifier diff --git a/app/src/main/proto/sp/context_menu.proto b/app/src/main/proto/sp/context_menu.proto new file mode 100644 index 00000000..2da97bd0 --- /dev/null +++ b/app/src/main/proto/sp/context_menu.proto @@ -0,0 +1 @@ +// com.spotify.home.dac.contextMenu.v1.proto \ No newline at end of file diff --git a/app/src/main/proto/sp/home/PromoComponents.proto b/app/src/main/proto/sp/home/PromoComponents.proto new file mode 100644 index 00000000..d75470cd --- /dev/null +++ b/app/src/main/proto/sp/home/PromoComponents.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "sp/DacPlayer.proto"; + +package com.spotify.home.dac.component.v1.proto; + +option java_multiple_files = true; +option java_package = "com.spotify.home.dac.component.v1.proto"; + +message PromoSectionHeadingComponent { + string title = 1; + google.protobuf.Any context_menu = 2; +} + +message AlbumCardPromoComponent { + string title = 1; + string subtitle = 2; + string navigate_uri = 3; + string like_uri = 4; + string image_uri = 5; + string tag = 6; + spotify.dac.player.v1.proto.PlayCommand play_command = 7; +} + +message PromoCardOnlyYouComponent { + string title = 1; + string subtitle = 2; + string navigate_uri = 3; + string background_image_uri = 4; +} + +message PromoCardHomeComponent { + string title = 1; + string subtitle = 2; + string tag = 3; + string navigate_uri = 4; + string background_image_uri = 5; + string logo_image_uri = 6; + string gradient_color = 7; + spotify.dac.player.v1.proto.PlayCommand play_command = 8; + google.protobuf.Any context_menu = 9; + string play_button_color = 10; +} \ No newline at end of file diff --git a/app/src/main/proto/sp_ext/dac_jetispot.proto b/app/src/main/proto/sp_ext/dac_jetispot.proto new file mode 100644 index 00000000..2f31d431 --- /dev/null +++ b/app/src/main/proto/sp_ext/dac_jetispot.proto @@ -0,0 +1,15 @@ +// A custom DAC blocks for Jetispot usage +syntax = "proto3"; + +option java_package = "bruhcollective.itaysonlab.jetispot.proto"; +option java_multiple_files = true; + +message ErrorComponent { + enum ErrorType { + UNSUPPORTED = 0; + GENERIC_EXCEPTION = 1; + } + + ErrorType type = 1; + string message = 2; +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index e2e62d10..dbce53ae 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ buildscript { version_code = 12 version_name = "poc_v12" - compose_version = "1.3.0-beta02" - compose_m3_version = "1.0.0-beta02" + compose_version = "1.3.0-beta03" + compose_m3_version = "1.0.0-beta03" compose_compiler_version = "1.3.1" media2_version = "1.2.1" @@ -17,8 +17,8 @@ buildscript { } plugins { - id "com.android.application" version '7.2.2' apply false - id "com.android.library" version '7.2.2' apply false + id "com.android.application" version '7.3.0' apply false + id "com.android.library" version '7.3.0' apply false id "org.jetbrains.kotlin.android" version "1.7.10" apply false id "com.google.dagger.hilt.android" version "$hilt_version" apply false } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 786a3204..aef83543 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Apr 11 16:27:15 CEST 2022 +#Mon Sep 26 19:18:25 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME